SQL Server - Rückgabewert nach INSERT

317

Ich versuche, den Schlüsselwert nach einer INSERT-Anweisung zurückzubekommen. Beispiel: Ich habe eine Tabelle mit den Attributen Name und ID. id ist ein generierter Wert.

    INSERT INTO table (name) VALUES('bob');

Jetzt möchte ich die ID im selben Schritt zurückbekommen. Wie wird das gemacht?

Wir verwenden Microsoft SQL Server 2008.

melbisch
quelle
Ich fand hier eine nützliche Antwort: [vorbereitete Erklärung mit Anweisung-Rückgabe-generierten Schlüsseln] [1] [1]: stackoverflow.com/questions/4224228/…
Lars Ladegaard
1
Mögliches Duplikat von Bester Weg, um die Identität der eingefügten Zeile zu erhalten?
Vladimir Vagaytsev

Antworten:

476

Keine separate SELECT ...

INSERT INTO table (name)
OUTPUT Inserted.ID
VALUES('bob');

Dies funktioniert auch für Nicht-IDENTITY-Spalten (z. B. GUIDs)

gbn
quelle
29
Könntest du etwas näher darauf eingehen? Wohin geht die Ausgabe in diesem Beispiel? Die Dokumentation zeigt nur Beispiele für Tabellen (mit Ausgabe ... in). Idealerweise möchte ich es einfach in eine Variable übergeben können
JonnyRaa
2
@JonnyLeeds: Sie können es nicht mit einer Variablen machen (außer mit einer Tabellenvariablen). Der Ausgang geht an den Kunden oder einen Tisch
gbn
7
Leider können Sie sich nicht darauf verlassen, da das Hinzufügen eines Auslösers zur Tabelle Ihre Aussagen zerstört! re: blogs.msdn.com/b/sqlprogrammability/archive/2008/07/11/…
hajikelist
1
@hajikelist: Dies ist ein ziemlicher Randfall, SET NCOOUNT ON im Trigger hilft normalerweise. Siehe stackoverflow.com/questions/1483732/set-nocount-on-usage
gbn
5
Verwenden Sie niemals @@ IDENTITY. SCOPE_IDENTITY, ja, aber niemals @@ IDENTITY. Es ist unzuverlässig
gbn
188

Verwenden Sie SCOPE_IDENTITY()diese Option , um den neuen ID-Wert abzurufen

INSERT INTO table (name) VALUES('bob');

SELECT SCOPE_IDENTITY()

http://msdn.microsoft.com/en-us/library/ms190315.aspx

Curt
quelle
7
Annahme idist Identität
Ilia G
12
@ liho1eye - Das OP hat den Namen der Identitätsspalte als bezeichnet id, also ja.
Curt
3
Was ist, wenn auf einem größeren System viele SQL-Dateien gleichzeitig ausgeführt werden? Wird bei jeder Anfrage die zuletzt eingefügte ID zurückgegeben?
Shiv
2
@Shiv "SCOPE_IDENTITY gibt Werte zurück, die nur innerhalb des aktuellen Bereichs eingefügt wurden"
goodies4uall
45
INSERT INTO files (title) VALUES ('whatever'); 
SELECT * FROM files WHERE id = SCOPE_IDENTITY();

Ist die sicherste Wette, da ein bekanntes Problem mit dem OUTPUT-Klauselkonflikt bei Tabellen mit Triggern vorliegt. Dies macht dies ziemlich unzuverlässig, da selbst wenn Ihre Tabelle derzeit keine Auslöser hat - jemand, der später einen hinzufügt, Ihre Anwendung beschädigt. Zeitbombe Verhalten.

Weitere Informationen finden Sie im msdn-Artikel:

http://blogs.msdn.com/b/sqlprogrammability/archive/2008/07/11/update-with-output-clause-triggers-and-sqlmoreresults.aspx

Hajikelist
quelle
Nur wenn Sie SET NOCOUNT ON nicht in Triggern hinzufügen. Siehe auch docs.microsoft.com/en-us/sql/database-engine/configure-windows/…
gbn
Dies ist keine Option für unsere Legacy-Umgebungen @gbn
hajikelist
@hajikelist Wir haben alle ein Vermächtnis, aber das Risiko, dass Auslöser die Ausgabe durcheinander bringen, ist gering. Alles, was nötig ist, ist nicht aktiviert. Wenn jemand einen Trigger hinzufügt, sollte er wissen, wie man ihn codiert (impliziert, dass Sie hauptsächlich die Kontrolle haben), oder Sie müssen Ihre Entwickler schulen. Irgendwann müssen Sie migrieren, wenn diese SQL-Version nicht mehr verfügbar ist unterstützt usw., sodass Trigger keine Ergebnismenge verursachen. Was auch immer, es ist nicht die beste Antwort, denn wenn Sie STATT Trigger haben, funktioniert SCOPE_IDENTITY möglicherweise nicht ( stackoverflow.com/questions/908257/… )
gbn
@gbn - Ich mag es einfach, solche dummen Dinge zu vermeiden. Ich werde nicht allen meinen Entwicklern sagen: "Vergessen Sie nicht, bei jedem Trigger die Anweisung" Nicht meine App brechen "hinzuzufügen." - du kannst es behalten. Das "stattdessen" -Szenario ist weitaus mehr ein Randfall imo.
Hajikelist
Eine sicherere Antwort könnte darin bestehen, dass die Anwendung eine weitere Abfrage ausführt, sobald sie von dieser zurückkehrt. Solange dies im Backend erfolgt, sollte die Leistungseinbußen die Einfachheit der Verwaltung der Entwicklung in Gruppen von Personen wert sein, und sie entspricht eher den Standards als einige verrückte Funktionen mit Randfällen. Ich möchte lieber, dass die Randfälle in meinem Code enthalten sind und sie auf den Plattformen vermeiden. nur meine Meinung nicht ausflippen :)
Dan Chase
33

Entity Framework führt etwas Ähnliches aus wie die Antwort von gbn:

DECLARE @generated_keys table([Id] uniqueidentifier)

INSERT INTO Customers(FirstName)
OUTPUT inserted.CustomerID INTO @generated_keys
VALUES('bob');

SELECT t.[CustomerID]
FROM @generated_keys AS g 
   JOIN dbo.Customers AS t 
   ON g.Id = t.CustomerID
WHERE @@ROWCOUNT > 0

Die Ausgabeergebnisse werden in einer temporären Tabellenvariablen gespeichert und dann wieder für den Client ausgewählt. Muss sich der Gotcha bewusst sein:

Einfügungen können mehr als eine Zeile generieren, sodass die Variable mehr als eine Zeile enthalten kann, sodass Sie mehr als eine Zeile zurückgeben können ID

Ich habe keine Ahnung, warum EF die kurzlebige Tabelle wieder mit der realen Tabelle verbinden würde (unter welchen Umständen würden die beiden nicht übereinstimmen).

Aber genau das macht EF.

Nur SQL Server 2008 oder neuer. Wenn es 2005 ist, haben Sie kein Glück.

Ian Boyd
quelle
2
Der Grund, warum EF dies tut, besteht darin, sicherzustellen, dass auch alle anderen Änderungen am eingefügten CustomerDatensatz "angezeigt" werden können, da möglicherweise andere DB-seitige Logik Auswirkungen darauf hat, z. B. DEFAULTbei einigen Spalten, Triggern in der Tabelle usw. EF aktualisiert die Entität (Objekt), die für die Einfügung verwendet wird, sodass die Clientseite das Kundenobjekt mit der ID und allem anderen erhält, das den aktuellen Status der Zeile darstellt.
Hilarion
Ein weiterer Grund, EF nicht zu verwenden.
cskwg
11

@@IDENTITY Ist eine Systemfunktion, die den zuletzt eingefügten Identitätswert zurückgibt.

AngelaG
quelle
4
Ich muss davon abraten, jemals @@ IDENTITY zu verwenden - es ist nicht genau (zu breit), viel weniger threadsicher - siehe @ Curt's Antwort zu SCOPE_IDENTITY ().
Zanlok
Es ist überhaupt nicht threadsicher.
cskwg
10

Es gibt viele Möglichkeiten, nach dem Einfügen zu beenden

Wenn Sie Daten in eine Tabelle einfügen, können Sie mit der OUTPUT-Klausel eine Kopie der Daten zurückgeben, die in die Tabelle eingefügt wurden. Die OUTPUT-Klausel hat zwei Grundformen: OUTPUT und OUTPUT INTO. Verwenden Sie das OUTPUT-Formular, wenn Sie die Daten an die aufrufende Anwendung zurückgeben möchten. Verwenden Sie das Formular OUTPUT INTO, wenn Sie die Daten an eine Tabelle oder eine Tabellenvariable zurückgeben möchten.

DECLARE @MyTableVar TABLE (id INT,NAME NVARCHAR(50));

INSERT INTO tableName
(
  NAME,....
)OUTPUT INSERTED.id,INSERTED.Name INTO @MyTableVar
VALUES
(
   'test',...
)

IDENT_CURRENT : Gibt die letzte Identität zurück, die für eine bestimmte Tabelle oder Ansicht in einer Sitzung erstellt wurde.

SELECT IDENT_CURRENT('tableName') AS [IDENT_CURRENT]

SCOPE_IDENTITY : Gibt die letzte Identität aus derselben Sitzung und demselben Bereich zurück. Ein Bereich ist eine gespeicherte Prozedur / ein Trigger usw.

SELECT SCOPE_IDENTITY() AS [SCOPE_IDENTITY];  

@@ IDENTITY : Gibt die letzte Identität aus derselben Sitzung zurück.

SELECT @@IDENTITY AS [@@IDENTITY];
Reza Jenabi
quelle
Ich habe OUTPUT INTO ausprobiert, aber es ist sehr langsam.
cskwg
1
@RezaJenabi Jun außer Betrieb gesetzt wird sehr gut gearbeitet, ist besser als in der Tabelle viele id zu finden. Ich habe out putfür bulk insertund eingefügt von select statement.
Vielen
5

Die beste und sicherste Lösung ist die Verwendung SCOPE_IDENTITY().

Sie müssen lediglich nach jeder Einfügung die Bereichsidentität abrufen und in einer Variablen speichern, da Sie zwei Einfügungen im selben Bereich aufrufen können.

ident_currentund @@identityvielleicht funktionieren sie, aber sie sind kein sicherer Bereich. In einer großen Anwendung können Probleme auftreten

  declare @duplicataId int
  select @duplicataId =   (SELECT SCOPE_IDENTITY())

Weitere Details finden Sie hier Microsoft-Dokumente

MNF
quelle
1
Kann vereinfacht diesselect @duplicataId = SCOPE_IDENTITY()
pcnate
1
OUTPUTKlausel ist eine bessere, reinere Lösung :)
Dale K
OUTPUT INTO ist sehr langsam.
cskwg
4

Sie können scope_identity()die ID der Zeile auswählen, die Sie gerade in eine Variable eingefügt haben, und dann einfach die gewünschten Spalten aus dieser Tabelle auswählen, in der die ID = die Identität ist, aus der Sie sie erhalten habenscope_identity()

Hier finden Sie die MSDN-Informationen unter http://msdn.microsoft.com/en-us/library/ms190315.aspx

Purplegoldfish
quelle
1

Es gibt mehrere Möglichkeiten, die zuletzt eingefügte ID nach dem Einfügebefehl abzurufen.

  1. @@IDENTITY : Es wird der letzte Identitätswert zurückgegeben, der in einer Verbindung in der aktuellen Sitzung generiert wurde, unabhängig von der Tabelle und dem Umfang der Anweisung, die den Wert erzeugt hat
  2. SCOPE_IDENTITY(): Gibt den letzten Identitätswert zurück, der von der insert-Anweisung im aktuellen Bereich in der aktuellen Verbindung unabhängig von der Tabelle generiert wurde.
  3. IDENT_CURRENT(‘TABLENAME’): Gibt den zuletzt in der angegebenen Tabelle generierten Identitätswert zurück, unabhängig von einer Verbindung, Sitzung oder einem Bereich. IDENT_CURRENT ist nicht durch Umfang und Sitzung beschränkt. Es ist auf eine bestimmte Tabelle beschränkt.

Jetzt scheint es schwieriger zu sein, zu entscheiden, welches genau zu meiner Anforderung passt.

Ich bevorzuge meistens SCOPE_IDENTITY ().

Wenn Sie in der Anweisung insert die Option SCOPE_IDENTITY () zusammen mit TableName verwenden, erhalten Sie das genaue Ergebnis gemäß Ihren Erwartungen.

Quelle: CodoBee

Ishrar
quelle
0

So verwende ich OUTPUT INSERTED, wenn ich in eine Tabelle einfüge, die ID als Identitätsspalte in SQL Server verwendet:

'myConn is the ADO connection, RS a recordset and ID an integer
Set RS=myConn.Execute("INSERT INTO M2_VOTELIST(PRODUCER_ID,TITLE,TIMEU) OUTPUT INSERTED.ID VALUES ('Gator','Test',GETDATE())")
ID=RS(0)
Richard Zembron
quelle
0

Sie können eine select-Anweisung an Ihre insert-Anweisung anhängen. Integer myInt = In Tabelle1 (FName) Werte einfügen ('Fred'); Wählen Sie Scope_Identity (); Dies gibt einen Wert der Identität zurück, wenn der Skalierer ausgeführt wird.

Robert Quinn
quelle
-4

Nach dem Einfügen in eine Tabelle mit einer Identitätsspalte können Sie auf @@ IDENTITY verweisen, um den Wert abzurufen: http://msdn.microsoft.com/en-us/library/aa933167%28v=sql.80%29.aspx

FanOfTamago
quelle
24
Verwenden Sie niemals @@ IDENTITY: Es ist nicht bereichssicher: Auslöser usw. wirken sich darauf aus.
Gbn
-4

* Die Reihenfolge der Parameter in der Verbindungszeichenfolge ist manchmal wichtig. * Die Position des Provider-Parameters kann den Recordset-Cursor nach dem Hinzufügen einer Zeile beschädigen. Wir haben dieses Verhalten beim SQLOLEDB-Anbieter gesehen.

Nachdem eine Zeile hinzugefügt wurde, sind die Zeilenfelder nicht verfügbar, es sei denn, der Anbieter wird als erster Parameter in der Verbindungszeichenfolge angegeben. Wenn sich der Anbieter an einer beliebigen Stelle in der Verbindungszeichenfolge befindet, außer als erster Parameter, sind die neu eingefügten Zeilenfelder nicht verfügbar. Als wir den Provider auf den ersten Parameter verschoben haben, erschienen die Zeilenfelder auf magische Weise.

David Guidos
quelle
1
Können Sie uns sagen, wie dieser Kommentar auf die gestellte Frage antwortet / relevant ist? Ich glaube nicht, dass es Kappen verdient. Wenn Ihre Antwort als hilfreich erachtet wird, stimmen die Benutzer ab.
n__o
Viele Benutzer sind wahrscheinlich auf diese Seite gekommen, weil sie keine gültigen Felder hatten, um die gerade hinzugefügte Zeile zu identifizieren. Dieses Verhalten, das wir festgestellt haben (dass das einfache Ändern der Reihenfolge der Parameter in der Verbindungszeichenfolge den sofortigen Zugriff auf die neu hinzugefügte Zeile ermöglicht), ist so bizarr, dass ich dachte, es hätte es verdient, in Großbuchstaben erwähnt zu werden, zumal es sehr wahrscheinlich den Grund dafür behebt, dass die Leute das Neue wollen Zeilen-ID und andere Felder dieser Zeile. Wenn Sie einfach den Anbieter als ersten Parameter angeben, verschwindet das Problem.
David Guidos
Sie müssen Ihre Antwort bearbeiten und verbessern. Es ist derzeit laut und kommt nicht als anständige Antwort oder gar als Versuch
James
Was genau meinst du mit "laut"? Sie müssen Ihre Beschwerde erklären. Es ist ungefähr so ​​einfach wie es nur sein kann. Wenn Sie die Reihenfolge der Parameter in Ihrer Verbindungszeichenfolge ändern, kann dies Auswirkungen darauf haben, ob Zeilendaten nach dem Einfügen verfügbar sind.
David Guidos