Laquelle est la meilleure option pour obtenir la valeur d'identité que je viens de générer via un insert? Quel est l'impact de ces déclarations en termes de performances?
SCOPE_IDENTITY()
MAX()
TOP 1
IdentityColumn FROM TableName ORDER BY IdentityColumn DESC
Utilisez SCOPE_IDENTITY()
si vous insérez une seule ligne et souhaitez récupérer l'ID qui a été généré.
CREATE TABLE #a(identity_column INT IDENTITY(1,1), x CHAR(1));
INSERT #a(x) VALUES('a');
SELECT SCOPE_IDENTITY();
Résultat:
----
1
Utilisez la clause OUTPUT
si vous insérez plusieurs lignes et devez récupérer le set des ID qui ont été générés.
INSERT #a(x)
OUTPUT inserted.identity_column
VALUES('b'),('c');
Résultat:
----
2
3
et pourquoi c'est la meilleure option plus rapide?
Mis à part les performances, ce sont les seuls garantis pour être corrects au niveau d'isolement par défaut et/ou avec plusieurs utilisateurs. Même si vous ignorez l'aspect correct, SQL Server conserve la valeur insérée dans SCOPE_IDENTITY()
en mémoire, donc naturellement ce sera plus rapide que d'aller et d'exécuter votre propre requête isolée sur la table ou sur les tables système.
Ignorer l'aspect correct, c'est comme dire au facteur qu'il a fait du bon travail en livrant le courrier d'aujourd'hui - il a terminé son itinéraire 10 minutes plus vite que son temps moyen, le problème est qu'aucun du courrier n'a été livré à la bonne maison.
N'utilisez aucun des éléments suivants:
@@IDENTITY
- car cela ne peut pas être utilisé dans tous les scénarios, par exemple lorsqu'une table avec une colonne d'identité a un déclencheur qui s'insère également dans une autre table avec sa propre colonne d'identité - vous obtiendrez la mauvaise valeur.IDENT_CURRENT()
- J'entre dans les détails à ce sujet ici , et les commentaires sont également utiles à la lecture, mais essentiellement, sous concurrence, vous obtiendrez souvent la mauvaise réponse.MAX()
ou TOP 1
- vous devez protéger les deux instructions avec une isolation sérialisable afin de vous assurer que la MAX()
que vous obtenez n'appartient pas à quelqu'un d'autre. C'est beaucoup plus cher que d'utiliser simplement SCOPE_IDENTITY()
.Ces fonctions échouent également chaque fois que vous insérez deux lignes ou plus et nécessitent toutes les valeurs d'identité générées - votre seule option est la clause OUTPUT
.
Mis à part la performance, ils ont tous des significations assez différentes.
SCOPE_IDENTITY()
vous donnera la dernière valeur d'identité insérée dans la table anydirectement dans la portée actuelle (scope = batch, procédure stockée, etc. mais pas dans , disons, un déclencheur déclenché par la portée actuelle).
IDENT_CURRENT()
vous donnera la dernière valeur d'identité insérée dans une table spécifique de any scope, par any user.
@@IDENTITY
Vous donne la dernière valeur d'identité générée par la dernière instruction INSERT pour la connexion actuelle, quelle que soit la table ou la portée. (Note latérale: Access utilise cette fonction, et a donc quelques problèmes avec les déclencheurs qui insèrent des valeurs dans des tables avec des colonnes d'identité.)
L'utilisation de MAX()
ou TOP 1
Peut vous donner des résultats totalement erronés si la table a une étape d'identité négative, ou si des lignes ont été insérées avec SET IDENTITY_INSERT
En jeu. Voici un script illustrant tout cela:
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
Résumé: restez avec SCOPE_IDENTITY()
, IDENT_CURRENT()
ou @@IDENTITY
, Et assurez-vous que vous utilisez celui qui renvoie ce dont vous avez réellement besoin.