0

How can I clone a table with guid's as well as another table that references those guid's as foreign keys.

The two tables have a one to many relationship. I would need to do this in an automated way.

My problem is being able to reference the relationship between the two tables when newly created guid's are created during the clone.

EDIT: I need to clone the entries to the tables themselves, not new tables.

Kritner
  • 13,557
  • 10
  • 46
  • 72
useruseruser
  • 59
  • 1
  • 8
  • what exactly are you having problems with? Not sure your RDBMs but in SQL server you can simply script out your objects with relationships, do a bulk copy, data import wizard, script out objects and data, etc – Kritner Oct 19 '15 at 19:11
  • @Kritner I'm not trying to copy the tables per se but rather duplicate the entries in the table for test data. – useruseruser Oct 19 '15 at 19:14
  • What RDBMs is this? you can try using the `inserted` table that gets inserted into if you're using SQL server – Kritner Oct 19 '15 at 19:19
  • @Kritner using SQL Server. I am not sure what you mean. If I were to copy the entirety of Table 1, each entry would have to have new guids, not a problem. But how do i then establish the relationships to copy all the entries from Table 2 which uses Table 1 as a foreign key. – useruseruser Oct 19 '15 at 19:23
  • select @var = Scope_Identity() <--- reset var after each the input of each record from the parent table and use var in creating your child records. – Rob S. Oct 19 '15 at 20:00
  • how do you use tsql to iterate one by one for the entirety of the table? via a loop, some type of counter @RobS. – useruseruser Oct 19 '15 at 20:06
  • you can use a cursor w/ while loop to iterate through the results of a select statement... i wouldn't do this for a routine task, but just for creating a copy every now and then it's fine. – Rob S. Oct 19 '15 at 20:16
  • http://stackoverflow.com/questions/20662356/sql-server-loop-how-do-i-loop-through-a-set-of-records – Rob S. Oct 19 '15 at 20:17
  • A `MERGE` statement could be useful here: http://stackoverflow.com/a/31868546/4116017 – Vladimir Baranov Oct 19 '15 at 22:11
  • Just a quick one: do you want new tables with the same data as the originals, or do you want to add new records to the existing tables looking up the related children to also add automatically? – Steve Padmore Oct 20 '15 at 21:28

2 Answers2

1

Basically, we want to duplicate the Tables with new Ids but keep the relationship the same as the originals. If required, you could keep the same Ids, but in practice this shouldn't be a requirement; for testing, the Ids should not matter - only the relationship.

I'll demonstrate this with two Tables:

The first is AnimalType with Id (uniqueidentifier - RowGuid, Primary Key) and AnimalType (nvarchar) Columns

The second is Animal with AnimalName (nvarchar) and AnimalType (uniqueidentifier, Foreign Key) Columns

For the parent/lookup Table:

Create a new Table (newTable) to populate with the data of the existing Table (oldTable).

Create newTable with its own Primary Key Id column (ROWGUID, IDENTITY etc.) with its Default

Create an extra Column in newTable to hold a copy of oldTable's Id Column values

The Id Column in newTable will generate unique Ids on creation of records

The second (child) Table:

Create a new Table (newChildTable) to populate with the data of the existing Table (oldChildTable).

Create newChildTable with its own Foreign Key Column to point to newTable's Primary Key Column

Create an extra Column in newChildTable to hold a copy of oldChildTable's Foreign Key Column values

Once created, we populate the new parent/lookup Table with the data from the original Table, placing the Id values in the extra Column added for this data. The Table's own Ids will generate uniquely as usual.

Next, we populate the child Table with the data from its original Table, placing the original Foreign Key Column values into the added Column for this data.

Next, we join the two new tables on the Columns that hold the original Id values and update the Foreign Key Column values to the new Ids in the parent/lookup Table.

Finally, we can remove the Columns holding the original Id values, and we are left with two Tables linked to the same data but by the new Ids we generated when created when the records were copied.

You will not have any reference to the original Ids - just in case of selecting the wrong Table at any time in your testing (although this should be done in a different server...). If you needed the original Ids too, you can perform the above, not move the Ids around, rename the Columns etc. - as you wish really.

/*
Create copy of parent/lookup Table with its own Id column
Add a column to hold the original Ids
*/
CREATE TABLE [dbo].[AnimalTypeBak](
    [Id] [uniqueidentifier] ROWGUIDCOL  NOT NULL CONSTRAINT [DF_AnimalTypeBak_Id]  DEFAULT (newid()),
    [OriginalId] [uniqueidentifier] NOT NULL,
    [AnimalType] [nvarchar](32) NOT NULL,
 CONSTRAINT [PK_AnimalTypeBak] PRIMARY KEY CLUSTERED 
(
    [Id] ASC
)
) ON [PRIMARY]
GO

/*
Create copy of child Table
Add a column to hold the original Foreign Key values
*/
CREATE TABLE [dbo].[AnimalBak](
    [AnimalName] [nvarchar](20) NOT NULL,
    [OriginalAnimalType] [uniqueidentifier] NOT NULL,
    [AnimalType] [uniqueidentifier] NOT NULL
) ON [PRIMARY]

GO

/*
Import data from the parent/lookup Table placing the origional Ids into the added Column
*/
INSERT INTO [dbo].[AnimalTypeBak]
           ([OriginalId]
           ,[AnimalType])
           SELECT [Id], [AnimalType]
           FROM [dbo].[AnimalType]
GO

/*
Import data from the child Table placing the origional Foreign Key values into the added Column
*/
INSERT INTO [dbo].[AnimalBak]
           ([OriginalAnimalType]
           ,[AnimalName])
           SELECT [AnimalType], [AnimalName]
           FROM [dbo].[Animal]
GO

/*
Update the child Table placing the new parent/lookup Ids into the Foreign Key Column
*/
UPDATE [dbo].[AnimalBak]
  SET [dbo].[AnimalBak].[AnimalType] = [dbo].[AnimalTypeBak].[Id]
  FROM [dbo].[AnimalBak]
  INNER JOIN [dbo].[AnimalTypeBak]
  ON [dbo].[AnimalBak].[OriginalAnimalType] = [dbo].[AnimalTypeBak].[OriginalId]
GO

/*
Drop the redundant Columns
*/
ALTER TABLE [dbo].[AnimalBak]
    DROP COLUMN [OriginalAnimalType]
GO

ALTER TABLE [dbo].[AnimalTypeBak]
    DROP COLUMN [OriginalId]

/*
Add the Foreign Key Contraint between the two Tables
*/
ALTER TABLE [dbo].[AnimalBak]  WITH CHECK ADD  CONSTRAINT [FK_AnimalBak_AnimalTypeBak] FOREIGN KEY([AnimalType])
REFERENCES [dbo].[AnimalTypeBak] ([Id])
GO

/*
And select the data to ensure the data is related as it was in the original Tables
*/
SELECT a.AnimalName, a.AnimalType, b.AnimalType FROM [dbo].[AnimalBak] as a INNER JOIN [dbo].[AnimalTypeBak] as b ON b.Id = a.AnimalType
Steve Padmore
  • 1,710
  • 1
  • 12
  • 18
1
declare @Parents table (Id uniqueidentifier, Name varchar(50));
declare @Children table (Id uniqueidentifier, ParentId uniqueidentifier, Name varchar(50));

declare @NewId uniqueidentifier = newid();

insert into @Parents values (@NewId, 'Original parent');
insert into @Children values (newid(), @NewId, 'First original child');
insert into @Children values (newid(), @NewId, 'Second original child');


declare @Ids table (CloneId uniqueidentifier, OriginalId uniqueidentifier);


merge @Parents as target
    using (
        select 
            CloneId = newid(),
            OriginalId = Id,
            Name = Name + ' (Cloned)'
        from
            @Parents
        where   
            Id = @NewId
    ) 
    as source on source.CloneId = target.Id

when not matched by target then
    insert (Id, Name)   
    values (source.CloneId, source.Name)

output 
    source.CloneId, source.OriginalId
    into @Ids (CloneId, OriginalId);



merge @Children as target
    using (
        select 
            Id = newid(),
            ParentId = ids.CloneId,
            Name = Name + ' (Cloned)'
        from
            @Children c
            inner join @Ids ids on ids.OriginalId = c.ParentId
    ) 
    as source on source.Id = target.Id

when not matched by target then
    insert (Id, ParentId, Name) 
    values (source.Id, source.ParentId, source.Name);



select * from @Parents

select * from @Children
Vadim Loboda
  • 2,431
  • 27
  • 44