2016-11-16 2 views
0

Ich habe ein ziemlich komplexes Objekt, das verschachtelte Listen von Elementen enthält, jedes von ihnen mit einer eigenen Tabelle.Get SCOPE_IDENTITY Mitte der Transaktion

Wenn ich dieses Objekt erstelle, möchte ich es aus Leistungsgründen zusammen mit seinen Kindern und allen ihren Nachkommen in einer einzigen Transaktion einfügen.

meine Tabellen:

Parent 
|Id| Has a list of child 

Child 
|Id|ParentId| Has a list of Grandchild 

Grandchild 
|Id|ChildId| 

Hier ist, was meine Transaktion aussehen würde:

INSERT INTO Parent(mycolumns) VALUES (mydata);SELECT SCOPE_IDENTITY() into @ParentId; 

--insert the first child and his grandchilds 
INSERT INTO Child(mycolumns, parentid) VALUES (mydata, @ParentId);SELECT SCOPE_IDENTITY() into @ChildId; 
INSERT into Grandchild(mycolumns, childid) VALUES (mydata, @ChildId); 
INSERT into Grandchild(mycolumns, childid) VALUES (mydata, @ChildId); 
... loop through all grandchilds with this childid 

--insert the second child and his grandchilds 
INSERT INTO Child(mycolumns, parentid) VALUES (mydata, @ParentId);SELECT SCOPE_IDENTITY() into @ChildId; 
INSERT into Grandchild(mycolumns, childid) VALUES (mydata, @ChildId); 
INSERT into Grandchild(mycolumns, childid) VALUES (mydata, @ChildId); 
... loop through all grandchild with this childid again... 

Die Art, wie ich dies durch die Speicherung aller meiner Abfragen in eine ‚Operation‘ Objekt ist zu tun, und dann Durchlaufen sie in einer Transaktion.

using (SqlConnection connection = new SqlConnection(this.ConnectionString)) 
     { 
      connection.Open(); 
      using (SqlTransaction transaction = connection.BeginTransaction()) 
      { 
       foreach (var operation in operations) 
       { 
        using (SqlCommand command = new SqlCommand(operation.SqlCommand, connection, transaction)) 
        { 
         if (operation.Parameters != null) 
         { 
          foreach (var param in operation.Parameters) 
          { 
           command.Parameters.AddWithValue(param.Name, param.Value); 
          } 
         } 
         command.ExecuteNonQuery(); 
        } 
       } 
       transaction.Commit(); 
      } 
     } 

mein Problem ist, ich kann nicht scheinen, einen Weg zu finden SELECT SCOPE_IDENTITY zu speichern() in eine Variable (so etwas wie folgt aus: "SELECT SCOPE_IDENTITY() in @ChildId;") in einem späteren Befehl zu verwenden (aber in der gleichen Transaktion).

+0

Ein kurzer Scan..Modify 'command.ExecuteNonQuery();' deklarieren Sie dann und verwenden Sie 'insertId = (int) command.ExecuteScalar();', um die neue ID zu erhalten, die Sie als 'Parameter' übergeben müssen für die nächste Operation .. – Searching

+0

Eine ganz andere Möglichkeit ist es, alle Zeilen für eine Generation in einem einzigen 'INSERT' mit einem [' OUTPUT' einzufügen (https://msdn.microsoft.com/en-us/library/ ms177564.aspx) -Klausel zum Abrufen der Identitätsspaltenwerte für neu eingefügte Zeilen (und eine weitere eindeutige Spalte) in eine temporäre Tabelle. ('OUTPUT' kann mit' INSERT', 'UPDATE',' DELETE' und 'MERGE' verwendet werden und bietet Zugriff auf _before_ und _after_ Werte im Falle von 'UPDATE'.) Wiederholen Sie dies für jede Generation, indem Sie die neuen Zeilen verbinden mit den Vorfahren auf dem einzigartigen Wert. Staple alles in einen Befehl und lass die Datenbank den Rest erledigen. – HABO

+0

Ich endete damit, was @Searching sagte. Ich habe eine andere Methode erstellt, die die fehlenden Werte für parentid/childid in meinen Abfragen nach dem Ausführen von command.ExecuteScalar() ersetzt hat. Vielen Dank! – brem

Antwort

0

Anstelle der SCOPE_IDENTITY-Funktion können Sie immer die OUTPUT-Klausel verwenden. Dies ist ein viel robusterer und flexiblerer Ansatz.

declare @id TABLE(id int not null); 
INSERT INTO Child(mycolumns, parentid) 
OUTPUT INSERTED.childid INTO @ID(id) 
VALUES (mydata, @ParentId); 

Zusätzlicher Vorteil ist, dass Sie mehrere IDs in einer Tabellenvariablen speichern können. Zum Beispiel könnten Sie die ParentID speichern neben den ChildID:

declare @id TABLE(ParentId int not null, ChildID int not null); 
INSERT INTO Child(mycolumns, parentid) 
OUTPUT INSERTED.parentid, INSERTED.childid INTO @ID(ParentID, ChildID) 
VALUES (mydata, @ParentId); 
0

Zunächst einmal gibt es keine Notwendigkeit für drei Tabellen, wenn nur eine Tabelle tun.

id parentid 
1 null 
2 1 
3 2 

In Ihrem aktuellen scenerio, wenn es Einsatz Masse sein kann (mehr als ein Einsatz), dann sollten ja Sie OUTPUT-Klausel verwenden.

Ihr vollständiger Code ist nicht sichtbar, obwohl ich denke, dass Sie den Ausgabeparameter zurückgeben müssen, um ihn erneut zu übergeben.