TSQL Parent> Enfant> duplication secondaire sans slider

Je crée une procédure stockée SQL 2008 R2 pour dupliquer une ligne et tous ses enfants.

Il s'agit d'une configuration à trois niveaux avec un parent, un enfant et un sous-enfant. Étant donné l'ID du parent dont j'ai besoin pour créer un doublon.

Je l'ai résolu en utilisant un cursor fast_forward .

Je sais que je peux aussi le faire avec une boucle en boucle, mais je ne crois pas que ce soit plus rapide que cette méthode de slider. Quelles sont vos pensées?

Existe-t-il un meilleur moyen d'accomplir cette tâche sans utiliser de sliders?

EDIT: Une autre option que j'ai considéré était la création d'une table temporaire contenant les anciens / nouveaux PKID des loggings TBLACStages.

TBLACStages peut avoir de 1 à 20 lignes correspondantes (et TBLACUpgrade aura probablement 3 lignes par ligne TBLACStages)

 CREATE PROCEDURE [dbo].[spDuplicateACUnit] @pACUnitID bigint = 0 AS BEGIN SET NOCOUNT ON; DECLARE @NewACUnitID bigint = 0 INSERT INTO TBLACUnits ([col1] ,[col2] ,[...] ,[coln]) SELECT [col1] ,[col2] ,[...] ,[coln] FROM TBLACUnits WHERE ACUnitID = @pACUnitID SELECT @NewACUnitID = SCOPE_IDENTITY() DECLARE @ACStageID bigint = 0 DECLARE @NewACStageID bigint = 0 DECLARE @ACUnitCursor CURSOR SET @ACUnitCursor = CURSOR LOCAL FAST_FORWARD FOR SELECT ACStageID FROM TBLACStages WHERE TBLACStages.ACUnitID = @pACUnitID OPEN @ACUnitCursor FETCH NEXT FROM @ACUnitCursor INTO @ACStageID WHILE @@FETCH_STATUS = 0 BEGIN INSERT INTO TBLACStages ([ACUnitID] ,[col1] ,[col2] ,[...] ,[coln]) SELECT @NewACUnitID ,[col1] ,[col2] ,[...] ,[coln] FROM TBLACStages WHERE TBLACStages.ACStageID = @ACStageID SELECT @NewACStageID = SCOPE_IDENTITY() INSERT INTO TBLACUpgrade ([ACStageID] ,[col1] ,[col2] ,[...] ,[coln]) SELECT @NewACStageID ,[col1] ,[col2] ,[...] ,[coln] FROM TBLACUpgrade WHERE TBLACUpgrade.[ACStageID] = @ACStageID FETCH NEXT FROM @ACUnitCursor INTO @ACStageID END CLOSE @ACUnitCursor DEALLOCATE @ACUnitCursor END GO 

Cela devrait vous donner l'idée:

 CREATE TABLE t_parent (id INT NOT NULL PRIMARY KEY IDENTITY, value VARCHAR(100)) CREATE TABLE t_child (id INT NOT NULL PRIMARY KEY IDENTITY, parent INT NOT NULL, value VARCHAR(100)) CREATE TABLE t_grandchild (id INT NOT NULL PRIMARY KEY IDENTITY, child INT NOT NULL, value VARCHAR(100)) INSERT INTO t_parent (value) VALUES ('Parent 1') INSERT INTO t_parent (value) VALUES ('Parent 2') INSERT INTO t_child (parent, value) VALUES (1, 'Child 2') INSERT INTO t_child (parent, value) VALUES (2, 'Child 2') INSERT INTO t_grandchild (child, value) VALUES (1, 'Grandchild 1') INSERT INTO t_grandchild (child, value) VALUES (1, 'Grandchild 2') INSERT INTO t_grandchild (child, value) VALUES (2, 'Grandchild 3') DECLARE @parent TABLE (oid INT, nid INT) DECLARE @child TABLE (oid INT, nid INT) MERGE INTO t_parent USING ( SELECT id, value FROM t_parent ) p ON 1 = 0 WHEN NOT MATCHED THEN INSERT (value) VALUES (value) OUTPUT p.id, INSERTED.id INTO @parent; SELECT * FROM @parent MERGE INTO t_child USING ( SELECT c.id, p.nid, c.value FROM @parent p JOIN t_child c ON c.parent = p.oid ) c ON 1 = 0 WHEN NOT MATCHED THEN INSERT (parent, value) VALUES (nid, value) OUTPUT c.id, INSERTED.id INTO @child; SELECT * FROM @child; INSERT INTO t_grandchild (child, value) SELECT c.nid, gc.value FROM @child c JOIN t_grandchild gc ON gc.child = c.oid SELECT * FROM t_grandchild 

Ok, c'est le MERGE que j'ai trouvé basé sur la solution de Quassnoi. Je devrais travailler de manière appropriée sans le CURSOR

 DECLARE @parent TABLE (oid BIGINT, nid BIGINT) DECLARE @child TABLE (oid BIGINT, nid BIGINT) MERGE INTO TBLACUnits T USING (SELECT [col1], [...], [coln] FROM TBLACUnits WHERE ID = @pID) S ON 1 = 0 WHEN NOT MATCHED THEN INSERT ([ACUnitID] ,[col1] ,[...] ,[coln]) VALUES (S.[ACUnitID] ,S.[col1] ,S.[...] ,S.[coln]]) OUTPUT S.ACUnitID, INSERTED.ACUnitID INTO @parent; MERGE INTO TBLACStages T USING ( SELECT tt.[nid] ,TBLACStages.[col1] ,TBLACStages.[...] ,TBLACStages.[coln] FROM TBLACStages JOIN @parent tt ON tt.oid = TBLACStages.ACUnitID ) S ON 1 = 0 WHEN NOT MATCHED THEN INSERT ([ACUnitID] ,[col1] ,[...] ,[coln]) VALUES ([nid] ,[col1] ,[...] ,[coln]) OUTPUT S.[ACStageID], INSERTED.[ACStageID] INTO @child; INSERT INTO TBLACUpgrade ([ACStageID] ,[col1] ,[...] ,[coln]) SELECT c.[nid] ,TBLACUpgrade.[col1] ,TBLACUpgrade.[...] ,TBLACUpgrade.[coln] FROM @child c JOIN TBLACUpgrade ON TBLACUpgrade.ACStageID = c.oid 

J'ai vu ce post, j'ai presque haleté à la complexité, mais sûr que ça a l'air bien. Lorsque j'ai besoin de cloner ou copyr des tables avec des enfants ou des petits-enfants, j'ajoute simplement une nouvelle colonne à la table appelée PreCloneControl , puis reference ce champ dans la requête enfant de la nouvelle table pour searchr rapidement et facilement les anciennes données parentes. Facile. Mais si vous n'avez pas access à append des colonnes à la table, un hack rapide peut souvent servir. Un exemple est un champ Last Modified User, souvent un nvarchar de 100 caractères ou plus. En règle générale, nous devons mettre à jour ce champ de toute façon, alors déposez votre ancien numéro de contrôle là-bas et vous partez. N'oubliez pas d'effectuer une mise à jour rapide sur le champ Last Modified User lorsque vous avez terminé. Voici un exemple, j'utilise des tables temporaires pour les tests, mais vous devez utiliser des tables réelles.

 Declare @OldControl int = 123456 Declare @TT1 Table ( TT1Control [int] IDENTITY(1,1) NOT NULL, SomeData nvarchar(20) ) insert into @TT1 ( SomeData ) Select SomeDate from LiveTable where LTControl = @OldControl Declare @NewControl int = SCOPE_IDENTITY() Declare @TempTT2 Table ( TT2Control int IDENTITY(1,1) NOT NULL, TT2TT1FKControl int, TT2ChildData nvarchar(20), TT2ModUser nvarchar(100) ) insert into @TempTT2 ( TT2TT1FKControl,TT2ChildData,TT2ModUser ) Select @NewControl, TT2ChildData, Cast(TT2Control as nvarchar(100)) From TT2 where TT2TT1FKControl = @OldControl Select * from @TempTT2 Declare @TT3 Table ( TT3Control int IDENTITY(1,1) NOT NULL, TT3TT2FKControl int, TT3GrandChildData nvarchar(50), TT3OldTT2Control int ) Insert Into @TT3 ( TT3TT2FKControl,TT3GrandChildData,TT3OldTT2Control ) Select t.TT2Control, BookItemItemNumber,TT2.TT2Control From TT2 inner join GrandChildTable on TT2Control = GCTFKControl ,@TempTT2 as t Where TT2TT1FKControl = @OldControl and t.TT2ModUser = Cast(TT2Control as nvarchar(100)) Select * From @TT3 Update @TempTT2 set TT2ModUser = 'UserName' Where TT2TT1FKControl = @NewControl Select * from @TempTT2 

Pour augmenter la vitesse de votre SP, vous pouvez append une autre instruction FOR READ ONLY

Donc, votre SP sera comme ça:

  ... SET @ACUnitCursor = CURSOR LOCAL FAST_FORWARD FOR SELECT ACStageID FROM TBLACStages WHERE TBLACStages.ACUnitID = @pACUnitID FOR READ ONLY -- add this to increase the speed OPEN @ACUnitCursor FETCH NEXT FROM @ACUnitCursor INTO @ACStageID ...