SQL Server - Renvoyer une valeur après INSERT

318

J'essaie de récupérer la valeur-clé après une instruction INSERT. Exemple: j'ai une table avec le nom et l'identifiant des attributs. id est une valeur générée.

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

Maintenant, je veux récupérer l'identifiant dans la même étape. Comment cela se fait-il?

Nous utilisons Microsoft SQL Server 2008.

melbic
la source
J'ai trouvé une réponse utile ici: [préparé-déclaration-avec-déclaration-retour-généré-clés] [1] [1]: stackoverflow.com/questions/4224228/…
Lars Ladegaard
1
Copie possible de la meilleure façon d'obtenir l'identité de la ligne insérée?
Vladimir Vagaytsev

Réponses:

477

Pas besoin d'un SELECT séparé ...

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

Cela fonctionne également pour les colonnes non IDENTITY (telles que les GUID)

gbn
la source
29
pourriez-vous élaborer un peu? Où va la sortie dans cet exemple? La documentation ne montre que des exemples de tableaux (en utilisant la sortie ... dans). Idéalement, j'aimerais juste pouvoir le passer dans une variable
JonnyRaa
2
@JonnyLeeds: vous ne pouvez pas le faire sur une variable (sauf une variable de table). La SORTIE va au client ou à une table
gbn
7
Malheureusement, vous ne pouvez pas vous y fier car l'ajout d'un déclencheur à la table cassera vos déclarations! re: blogs.msdn.com/b/sqlprogrammability/archive/2008/07/11/…
hajikelist
1
@hajikelist: il s'agit d'un cas assez complexe, SET NCOOUNT ON dans le déclencheur aide généralement. Voir stackoverflow.com/questions/1483732/set-nocount-on-usage
gbn
5
N'utilisez jamais @@ IDENTITY. SCOPE_IDENTITY, oui, mais jamais @@ IDENTITY. Ce n'est pas fiable
gbn
188

Utilisez SCOPE_IDENTITY()pour obtenir la nouvelle valeur d'ID

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

SELECT SCOPE_IDENTITY()

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

Sec
la source
7
en supposant que idc'est l'identité
Ilia G
12
@ liho1eye - L'OP faisait référence au nom de la colonne d'identité id, alors oui.
Curt
3
Sur un système plus grand, que se passe-t-il si de nombreux sql s'exécutent en même temps? Renverra-t-il le dernier identifiant inséré à chaque demande?
Shiv
2
@Shiv "SCOPE_IDENTITY renvoie les valeurs insérées uniquement dans la portée actuelle"
goodies4uall
45
INSERT INTO files (title) VALUES ('whatever'); 
SELECT * FROM files WHERE id = SCOPE_IDENTITY();

Est le pari le plus sûr car il existe un problème connu avec le conflit de clause OUTPUT sur les tables avec déclencheurs. Rend cela assez peu fiable car même si votre table n'a actuellement aucun déclencheur - quelqu'un qui en ajoute un en bas de la ligne cassera votre application. Comportement de type bombe à retardement.

Voir l'article msdn pour une explication plus approfondie:

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

hajikelist
la source
Seulement si vous n'ajoutez pas SET NOCOUNT ON dans les déclencheurs. Voir aussi docs.microsoft.com/en-us/sql/database-engine/configure-windows/...
GBN
ce n'est pas une option pour nos environnements hérités @gbn
hajikelist
@hajikelist Nous avons tous un héritage, mais le risque que des déclencheurs gâchent OUTPUT est faible, tout ce qu'il faut est réglé sur pas de compte. Si quelqu'un ajoute un déclencheur, il doit savoir comment le coder (implique que vous avez principalement le contrôle), ou vous devez former vos développeurs. À un moment donné, vous serez obligé de migrer lorsque cette version de SQL ne sera plus supporté etc donc les déclencheurs ne provoqueront pas un jeu de résultats. Quoi qu'il en soit, ce n'est pas la meilleure réponse car si vous avez des déclencheurs INSTEAD OF, SCOPE_IDENTITY pourrait ne pas fonctionner ( stackoverflow.com/questions/908257/… )
gbn
@gbn - J'aime juste éviter des choses idiotes comme ça. Je ne vais pas dire à tous mes développeurs: "N'oubliez pas d'ajouter la déclaration" ne pas casser mon application "dans chaque déclencheur." - Tu peux le garder. Le scénario «à la place» est bien plus une affaire de bord imo.
hajikelist
Une réponse plus sûre peut être simplement de demander à l'application d'exécuter une autre requête une fois qu'elle revient de celle-ci. Tant que cela se fait sur le back-end, la pénalité de performance devrait valoir la simplicité de gérer le développement en groupes de personnes, et c'est plus proche conformément aux normes que certaines fonctionnalités folles avec des cas de bord. Je préfère que les cas de bord soient dans mon code et les éviter sur les plates-formes. juste mon opinion ne panique pas :)
Dan Chase
33

Entity Framework effectue quelque chose de similaire à la réponse de 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

Les résultats de sortie sont stockés dans une variable de table temporaire, puis sélectionnés à nouveau sur le client. Faut être conscient du gotcha:

les insertions peuvent générer plusieurs lignes, de sorte que la variable peut contenir plusieurs lignes, vous pouvez donc en renvoyer plusieurs ID

Je n'ai aucune idée pourquoi EF rejoindrait intérieurement la table éphémère à la vraie table (dans quelles circonstances les deux ne correspondraient-elles pas).

Mais c'est ce que fait EF.

SQL Server 2008 ou plus récent uniquement. Si c'est 2005, alors vous n'avez pas de chance.

Ian Boyd
la source
2
La raison pour laquelle EF le fait est de s'assurer qu'il peut également "voir" toutes les autres modifications de l' Customerenregistrement inséré , car il peut y avoir d'autres logiques côté DB qui l'affectent, par exemple DEFAULTsur certaines colonnes, déclencheurs sur la table, etc. EF met à jour le l'entité (objet) qu'il a utilisée pour l'insertion, de sorte que le côté client obtient l'objet client avec l'ID et tout le reste représentant l'état actuel de la ligne.
Hilarion
Une autre raison de ne pas utiliser EF.
cskwg
11

@@IDENTITY Est une fonction système qui renvoie la dernière valeur d'identité insérée.

AngelaG
la source
4
Je dois déconseiller d'utiliser @@ IDENTITY - ce n'est pas précis (trop large) et encore moins thread-safe - veuillez voir la réponse de @ Curt à propos de SCOPE_IDENTITY ().
zanlok
10

Il existe de nombreuses façons de quitter après l'insertion

Lorsque vous insérez des données dans une table, vous pouvez utiliser la clause OUTPUT pour renvoyer une copie des données qui ont été insérées dans la table. La clause OUTPUT prend deux formes de base: OUTPUT et OUTPUT INTO. Utilisez le formulaire OUTPUT si vous souhaitez renvoyer les données à l'application appelante. Utilisez le formulaire OUTPUT INTO si vous souhaitez renvoyer les données vers une table ou une variable de table.

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

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

IDENT_CURRENT : il renvoie la dernière identité créée pour une table ou une vue particulière dans une session.

SELECT IDENT_CURRENT('tableName') AS [IDENT_CURRENT]

SCOPE_IDENTITY : il renvoie la dernière identité d'une même session et la même étendue. Une étendue est une procédure stockée / déclencheur, etc.

SELECT SCOPE_IDENTITY() AS [SCOPE_IDENTITY];  

@@ IDENTITY : Il renvoie la dernière identité de la même session.

SELECT @@IDENTITY AS [@@IDENTITY];
Reza Jenabi
la source
1
@RezaJenabi Jun, le résultat est très bien travaillé, vaut mieux que de trouver de nombreux identifiants dans le tableau. Je out putpour bulk insertet par insertion select statement. merci votre suggestion
Amirhossein
5

La solution la meilleure et la plus sûre utilise SCOPE_IDENTITY().

Il vous suffit d'obtenir l'identité de la portée après chaque insertion et de l'enregistrer dans une variable car vous pouvez appeler deux insertions dans la même portée.

ident_currentet @@identitypeut-être qu'ils fonctionnent, mais ils ne sont pas une portée sûre. Vous pouvez avoir des problèmes dans une grande application

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

Plus de détails sont ici Microsoft docs

MNF
la source
1
Peut simplifier cela pourselect @duplicataId = SCOPE_IDENTITY()
pcnate
1
OUTPUTclause est une solution meilleure et plus pure :)
Dale K
OUTPUT INTO est très lent.
cskwg
4

Vous pouvez utiliser scope_identity()pour sélectionner l'ID de la ligne que vous venez d'insérer dans une variable, puis sélectionnez simplement les colonnes que vous souhaitez dans cette table où l'id = l'identité que vous avez obtenuescope_identity()

Voir ici pour les informations MSDN http://msdn.microsoft.com/en-us/library/ms190315.aspx

Purplegoldfish
la source
1

Il existe plusieurs façons d'obtenir le dernier ID inséré après la commande d'insertion.

  1. @@IDENTITY : Il renvoie la dernière valeur d'identité générée sur une connexion dans la session en cours, indépendamment de la table et de la portée de l'instruction qui a produit la valeur
  2. SCOPE_IDENTITY(): Il renvoie la dernière valeur d'identité générée par l'instruction insert dans la portée actuelle de la connexion actuelle, quelle que soit la table.
  3. IDENT_CURRENT(‘TABLENAME’): Il renvoie la dernière valeur d'identité générée sur la table spécifiée, indépendamment de toute connexion, session ou étendue. IDENT_CURRENT n'est pas limité par la portée et la session; il est limité à une table spécifiée.

Maintenant, il semble plus difficile de décider lequel correspondra exactement à mes besoins.

Je préfère principalement SCOPE_IDENTITY ().

Si vous utilisez select SCOPE_IDENTITY () avec TableName dans l'instruction d'insertion, vous obtiendrez le résultat exact selon vos attentes.

Source: CodoBee

Ishrar
la source
0

Voici comment j'utilise OUTPUT INSERTED, lors de l'insertion dans une table qui utilise l'ID comme colonne d'identité dans SQL Server:

'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
la source
0

Vous pouvez ajouter une instruction select à votre instruction insert. Integer myInt = Insérer dans la table1 (FName) les valeurs ('Fred'); Sélectionnez Scope_Identity (); Cela renverra une valeur de l'identité lors de l'exécution du scaler.

Robert Quinn
la source
-4

Après avoir effectué une insertion dans une table avec une colonne d'identité, vous pouvez référencer @@ IDENTITY pour obtenir la valeur: http://msdn.microsoft.com/en-us/library/aa933167%28v=sql.80%29.aspx

FanOfTamago
la source
24
N'utilisez jamais @@ IDENTITY: il n'est pas sûr pour la portée: les déclencheurs, etc. et l'affectent.
gbn
-4

* L'ordre des paramètres dans la chaîne de connexion est parfois important. * L'emplacement du paramètre Provider peut casser le curseur du jeu d'enregistrements après l'ajout d'une ligne. Nous avons vu ce comportement avec le fournisseur SQLOLEDB.

Après l'ajout d'une ligne, les champs de ligne ne sont pas disponibles, À MOINS QUE le fournisseur ne soit spécifié comme premier paramètre dans la chaîne de connexion. Lorsque le fournisseur se trouve n'importe où dans la chaîne de connexion, sauf en tant que premier paramètre, les nouveaux champs de ligne insérés ne sont pas disponibles. Lorsque nous avons déplacé le fournisseur vers le premier paramètre, les champs de ligne sont apparus comme par magie.

David Guidos
la source
1
Pourriez-vous nous dire comment ce commentaire répond / est pertinent par rapport à la question qui a été posée? Je ne pense pas que cela mérite des majuscules / gras. Si votre réponse est jugée utile, les utilisateurs la voteront.
n__o
Beaucoup d'utilisateurs sont probablement venus sur cette page car ils n'avaient pas de champs valides pour identifier la ligne que vous venez d'ajouter. Ce comportement que nous avons constaté (que le simple fait de changer l'ordre des paramètres dans la chaîne de connexion permet d'accéder immédiatement à la ligne nouvellement ajoutée) est si bizarre que je pensais qu'il méritait d'être mentionné en majuscules, d'autant plus qu'il résoudra très probablement la raison pour laquelle les gens veulent le nouveau ID de ligne et autres champs de cette ligne. En plaçant simplement le fournisseur comme premier paramètre, le problème disparaît.
David Guidos
Vous devez modifier et améliorer votre réponse. C'est actuellement bruyant et ne semble pas être une réponse décente ou même une tentative
James
Qu'entendez-vous exactement par «bruyant»? Vous devez expliquer votre plainte. C'est à peu près aussi simple que possible. Si vous modifiez l'ordre des paramètres dans votre chaîne de connexion, cela peut affecter la disponibilité des données de ligne après une insertion.
David Guidos