J'écris une instruction d'insertion de traitement par lots et je voudrais utiliser une table temporaire pour suivre les ID insérés au lieu de parcourir les éléments moi-même et d'appeler SCOPE_IDENTITY () pour chaque ligne insérée.
Les données qui doivent être insérées ont des identifiants (temporaires) les reliant à d'autres données qui devraient également être insérées dans une autre table, j'ai donc besoin d'une référence croisée de l'identifiant réel et de l'identifiant temporaire.
Voici un exemple de ce que j'ai jusqu'à présent:
-- The existing table
DECLARE @MyTable TABLE (ID INT IDENTITY(1,1), [Name] NVARCHAR(MAX));
-- My data I want to insert
DECLARE @MyInsertData TABLE (ID INT, [Name] NVARCHAR(MAX));
INSERT INTO @MyInsertData ( ID,Name)
VALUES ( -1 , 'bla'),(-2,'test'),(-3,'last');
DECLARE @MyCrossRef TABLE ([NewId] INT, OldId INT);
INSERT INTO @MyTable ( [Name] )
OUTPUT Inserted.ID, INS.ID INTO @MyCrossRef
SELECT [NAME] FROM @MyInsertData INS
-- Check the result
SELECT * FROM @MyCrossRef
Le problème est que je ne peux pas obtenir la clause OUTPUT INTO pour accepter l'ID, j'ai essayé @MyInsertData.ID
et d'autres astuces joignant la table à elle-même, mais rien ne semble fonctionner.
En fait, vous pouvez réaliser la même chose en changeant votre INSERT
en MERGE
. Bien que l'instruction MERGE
soit en fait une façon assez intéressante de faire des "upserts" dans SQL Server, rien ne vous empêche de l'utiliser uniquement dans le but d'insérer:
-- The existing table
DECLARE @MyTable TABLE (ID INT IDENTITY(1,1), [Name] NVARCHAR(MAX));
-- My data I want to insert
DECLARE @MyInsertData TABLE (ID INT, [Name] NVARCHAR(MAX));
INSERT INTO @MyInsertData ( ID,Name)
VALUES ( -1 , 'bla'),(-2,'test'),(-3,'last');
DECLARE @MyCrossRef TABLE ([NewId] INT, OldId INT);
MERGE INTO @MyTable AS dest
USING @MyInsertData AS ins ON 1=0 -- always false
WHEN NOT MATCHED BY TARGET -- happens for every row, because 1 is never 0
THEN INSERT ([Name])
VALUES (ins.[NAME])
OUTPUT inserted.ID, ins.ID
INTO @MyCrossRef (NewId, OldId);
-- Check the result
SELECT * FROM @MyCrossRef
Une des bonnes choses à propos de MERGE
est qu'il vous permet d'accéder aux colonnes source ainsi qu'aux inserted
et deleted
tables dans la clause OUTPUT
.
Mon code peut contenir des erreurs, car je ne l'ai pas testé. Mon article de blog d'il y a quelques années va dans un peu plus de détails, y compris sur les performances des requêtes.
La clause de sortie ne peut accéder qu'aux données des lignes cibles et des constantes/variables, pas aux données provenant ailleurs dans la source SELECT
, comme si vous fonctionniez dans un déclencheur.
https://docs.Microsoft.com/en-us/sql/t-sql/queries/output-clause-transact-sql indique:
Toute référence aux colonnes de la table en cours de modification doit être qualifiée avec le préfixe INSERTED ou DELETED.
Donc, pour obtenir l'ID d'origine, vous devez l'inclure dans la table de destination afin que la clause de sortie puisse le renvoyer, comme suit:
-- The existing table
DECLARE @MyTable TABLE (ID INT IDENTITY(1,1), [Name] NVARCHAR(MAX), SourceID INT);
-- My data I want to insert
DECLARE @MyInsertData TABLE (ID INT, [Name] NVARCHAR(MAX));
INSERT INTO @MyInsertData ( ID,Name)
VALUES ( -1 , 'bla'),(-2,'test'),(-3,'last');
DECLARE @MyCrossRef TABLE ([NewId] INT, OldId INT);
INSERT INTO @MyTable ( [Name], SourceID )
OUTPUT Inserted.ID, Inserted.SourceID INTO @MyCrossRef
SELECT [NAME], ID FROM @MyInsertData INS
-- Check the result
SELECT * FROM @MyCrossRef
bien que la modification du schéma de l'objet cible puisse ne pas être pratique dans votre situation, cela peut ne pas être applicable.