Der beste Weg, um die letzte Identität in eine Tabelle einzufügen

35

Welche Option ist die beste, um den Identitätswert zu erhalten, den ich gerade über eine Einfügung generiert habe? Welche Auswirkungen haben diese Aussagen auf die Leistung?

  1. SCOPE_IDENTITY()
  2. Aggregatfunktion MAX()
  3. SELECT TOP 1IdentityColumn FROM TableNameORDER BY IdentityColumn DESC
AA.SC
quelle
1
Verwenden Sie postgreSQL und Sie haben es aus dem Regal postgresql.org/docs/9.1/static/sql-insert.html
Jewgenij Afanassjew
Linkes Feld Option - Wenn Sie eine Guid-Spalte in der Tabelle haben und eine neue Guid generieren und diese während des Einfügens in die neue Spalte einfügen können, können Sie die Zeile mit dieser Guid auswählen, um die generierte int-Identität abzurufen.
Niico

Antworten:

56

VerwendenSCOPE_IDENTITY() Sie diese Option, wenn Sie eine einzelne Zeile einfügen und die generierte ID abrufen möchten.

CREATE TABLE #a(identity_column INT IDENTITY(1,1), x CHAR(1));

INSERT #a(x) VALUES('a');

SELECT SCOPE_IDENTITY();

Ergebnis:

----
1

Verwenden Sie die OUTPUTKlausel , wenn Sie mehrere Zeilen und Bedarf abzurufen , die einfügen Satz von IDs , die erzeugt wurden.

INSERT #a(x) 
  OUTPUT inserted.identity_column 
  VALUES('b'),('c');

Ergebnis:

----
2
3

und warum ist das am besten schneller möglich?

Abgesehen von der Leistung sind dies die einzigen, die in der Standardisolationsstufe und / oder bei mehreren Benutzern garantiert korrekt sind. Selbst wenn Sie den Korrektheitsaspekt ignorieren, SCOPE_IDENTITY()speichert SQL Server den eingefügten Wert im Arbeitsspeicher. Dies ist natürlich schneller, als Ihre eigene isolierte Abfrage für die Tabelle oder für Systemtabellen auszuführen.

Das Ignorieren des Korrektheitsaspekts bedeutet, dem Postboten mitzuteilen, dass er gute Arbeit bei der Zustellung der heutigen Post geleistet hat. Er beendete seine Route 10 Minuten schneller als seine durchschnittliche Zeit.

Sie nicht eine der folgenden Bedingungen verwendet werden :

  • @@IDENTITY - Da dies nicht in allen Szenarien verwendet werden kann, z. B. wenn eine Tabelle mit einer Identitätsspalte einen Trigger hat, der auch in eine andere Tabelle mit einer eigenen Identitätsspalte eingefügt wird, erhalten Sie den falschen Wert zurück.
  • IDENT_CURRENT()- Ich gehe hier ausführlich darauf ein , und die Kommentare sind ebenfalls hilfreich, aber im Grunde genommen werden Sie bei gleichzeitiger Verwendung häufig die falsche Antwort erhalten.
  • MAX()oder TOP 1- Sie müssten die beiden Anweisungen mit serialisierbarer Isolation schützen, um sicherzustellen, dass MAX()sie nicht von jemand anderem stammen. Dies ist viel teurer als nur die Verwendung SCOPE_IDENTITY().

Diese Funktionen schlagen auch immer dann fehl, wenn Sie zwei oder mehr Zeilen einfügen und alle generierten Identitätswerte benötigen - Ihre einzige Option ist die OUTPUTKlausel.

Aaron Bertrand
quelle
Eine weitere Frage wurde in meinem Gedächtnis ausgelöst. Wann immer wir brauchen, um die letzte Identität in einer bestimmten Tabelle entweder von einer Sitzung oder von einem Benutzer zu generieren, ist der einzig richtige und beste Weg MAX () dieser Spalte?
AA.SC
Wie wäre es, wenn ich mehrere Zeilen in eine Tabelle einfügen würde? SCOPE_IDENTITY () gibt immer die zuletzt generierte Identität zurück. Was ist, wenn die Spalte der Primärschlüssel, aber nicht die Identitätsspalte ist?
AA.SC
@ AA.SC ja, es wird der letzte zurückgegeben. Wenn es sich nicht um eine Identitätsspalte handelt, funktioniert keine dieser Funktionen. Woher kommt in diesem Fall der PK-Wert?
Aaron Bertrand
Ich habe dies für eine Spalte in unserer Anwendung gesehen, in der die Spalte vom Typ INT ist und Entwickler MAX (Spaltenname) +1 verwenden, wenn sie einen neuen Datensatz einfügen müssen
AA.SC
Dann wissen sie bereits, welchen Wert sie gerade eingefügt haben. SQL Server hat keine Möglichkeit, Ihnen dies mitzuteilen (Sie können sich nicht darauf verlassen, MAX nachträglich erneut abzurufen, es sei denn, Sie haben Ihre gesamte Transaktion vollständig isoliert, was sich nicht positiv auf die Leistung oder die Parallelität auswirkt).
Aaron Bertrand
7

Abgesehen von der Leistung haben sie alle unterschiedliche Bedeutungen.

SCOPE_IDENTITY()Gibt den letzten Identitätswert an, der direkt im aktuellen Bereich in eine Tabelle eingefügt wurde (Bereich = Batch, gespeicherte Prozedur usw., jedoch nicht beispielsweise in einem Trigger, der vom aktuellen Bereich ausgelöst wurde).

IDENT_CURRENT()gibt Ihnen den letzten Identitätswert , der von einem Benutzer aus einem beliebigen Bereich in eine bestimmte Tabelle eingefügt wurde .

@@IDENTITYGibt den letzten Identitätswert an, der von der letzten INSERT-Anweisung für die aktuelle Verbindung generiert wurde, unabhängig von Tabelle oder Bereich. (Randnotiz: Access verwendet diese Funktion und hat daher einige Probleme mit Triggern, die Werte in Tabellen mit Identitätsspalten einfügen.)

Die Verwendung von MAX()oder TOP 1kann zu völlig falschen Ergebnissen führen, wenn die Tabelle einen negativen Identitätsschritt aufweist oder Zeilen mit SET IDENTITY_INSERTim Spiel eingefügt wurden . Hier ist ein Skript, das all dies demonstriert:

CREATE TABLE ReverseIdent (
    id int IDENTITY(9000,-1) NOT NULL PRIMARY KEY CLUSTERED,
    data char(4)
)

INSERT INTO ReverseIdent (data)
VALUES ('a'), ('b'), ('c')

SELECT * FROM ReverseIdent

SELECT IDENT_CURRENT('ReverseIdent') --8998
SELECT MAX(id) FROM ReverseIdent --9000

SET IDENTITY_INSERT ReverseIdent ON

INSERT INTO ReverseIdent (id, data)
VALUES (9005, 'd')

SET IDENTITY_INSERT ReverseIdent OFF

SELECT IDENT_CURRENT('ReverseIdent') --8998
SELECT MAX(id) FROM ReverseIdent --9005

Zusammenfassung: Halten Sie sich an SCOPE_IDENTITY(), IDENT_CURRENT()oder @@IDENTITYund vergewissern Sie sich, dass Sie diejenige verwenden, die das zurückgibt, was Sie tatsächlich benötigen.

db2
quelle
1
Warum ermutigen Sie zur Verwendung von IDENT_CURRENT()und @@IDENTITYwenn Ihr eigenes Skript zeigt, dass es falsche Ergebnisse ausgibt?
Aaron Bertrand
1
@ AaronBertrand Ich bin nicht sicher, ob ich folge. Der letzte generierte Identitätswert war 8998 (beachten Sie, dass der Schritt -1 ist), und das ist, was IDENT_CURRENT()zurückgibt. MAX () gibt nie den richtigen Wert über die erste Zeile hinaus zurück, da id rückwärts zählt und mit IDENTITY_INSERTon 9005 kein generierter Identitätswert ist und daher nicht von reflektiert wird IDENT_CURRENT(). Aber es kann "falsche" Ergebnisse zurückgeben, wenn Sie wirklich hinter dem her sind, was SCOPE_IDENTITY()zurückkommt. Wählen Sie das richtige Werkzeug für den Job.
db2
Das OP scheint nach dem eingegebenen Identitätswert zu sein - in diesem Fall ist 8998 falsch. Die Randfälle, die Sie erwähnen (rückwärts inkrementieren und IDENTITY_INSERT ein), sprechen meiner Meinung nach sogar noch gegen die Verwendung von IDENT_CURRENT, und @@ IDENTITY sollte aufgrund der Gefahr von Triggern (jetzt oder später hinzugefügt) nie wirklich verwendet werden. Ich kämpfe immer noch darum zu verstehen, warum IDENT_CURRENT derjenige ist, den das OP verwenden möchte (insbesondere unter Parallelität), oder warum @@ IDENTITY jemals von jemandem verwendet wird, wenn viel zuverlässigere Methoden existieren.
Aaron Bertrand
@AaronBertrand Aus der Frage geht nicht zu 100% hervor, dass das gewünschte Ergebnis die letzte Einfügung aus dem aktuellen Bereich ist (Option 1 unterscheidet sich in dieser Hinsicht von 2 und 3). Daher hielt ich es für eine gute Idee, beide zu beschreiben und zu beschreiben, wie sie lauten abweichen. Ich stimme jedoch zu, dass dies @@IDENTITYfast nie der ideale Weg ist, um generierte Identitätswerte zu erhalten. Der wichtigste Punkt ist, dass MAX()oder TOP 1wie eine weniger zuverlässige Version von IDENT_CURRENT(), die eine perfekte Funktion ist, wenn Sie verstehen, was es tut. Könnte für Wartungsarbeiten oder ähnliches nützlich sein.
db2