Disons que ma structure de table ressemble à ceci:
CREATE TABLE [dbo].[table1] (
[id] [int] IDENTITY(1,1) NOT NULL,
[data] [varchar](255) NOT NULL,
CONSTRAINT [PK_table1] PRIMARY KEY CLUSTERED ([id] ASC)
)
CREATE TABLE [dbo].[table2] (
[id] [int] IDENTITY(1,1) NOT NULL,
[table1_id] [int] NOT NULL,
[data] [varchar](255) NOT NULL,
CONSTRAINT [PK_table2] PRIMARY KEY CLUSTERED ([id] ASC)
)
Le champ [id]
De la première table correspond au champ [table1_id]
De la seconde. Ce que je voudrais faire, c'est insérer des données dans les deux tables en une seule transaction. Maintenant, je sais déjà comment faire cela en faisant INSERT-SELECT-INSERT, comme ceci:
BEGIN TRANSACTION;
DECLARE @id [int];
INSERT INTO [table1] ([data]) VALUES ('row 1');
SELECT @id = SCOPE_IDENTITY();
INSERT INTO [table2] ([table1_id], [data]) VALUES (@id, 'more of row 1');
COMMIT TRANSACTION;
Tout cela est bien pour les petits cas comme celui-ci où vous n'insérez peut-être qu'une poignée de lignes. Mais ce que je dois faire, c’est insérer quelques centaines de milliers de lignes, voire un million de lignes en même temps. Les données proviennent d'une autre table, donc si je les insérais dans une seule table, ce serait facile, je devrais simplement faire ceci:
INSERT INTO [table] ([data])
SELECT [data] FROM [external_table];
Mais comment pourrais-je faire cela et scinder les données en [table1]
Et [table2]
, Tout en mettant à jour [table2]
Avec le [table1_id]
Approprié comme je le fais? Est-ce que c'est possible?
Essaye ça:
insert into [table] ([data])
output inserted.id, inserted.data into table2
select [data] from [external_table]
PDATE: Re:
Denis - cela semble très proche de ce que je veux faire, mais vous pourriez peut-être corriger l'instruction SQL suivante pour moi? Fondamentalement, les [données] dans [table1] et les [données] dans [table2] représentent deux colonnes différentes/distinctes de [external_table]. L’énoncé que vous avez posté ci-dessus ne fonctionne que si vous souhaitez que les colonnes [données] soient identiques.
INSERT INTO [table1] ([data])
OUTPUT [inserted].[id], [external_table].[col2]
INTO [table2] SELECT [col1]
FROM [external_table]
Il est impossible de générer des colonnes externes dans une instruction insert
. Je pense donc que vous pourriez faire quelque chose comme ceci.
merge into [table1] as t
using [external_table] as s
on 1=0 --modify this predicate as necessary
when not matched then insert (data)
values (s.[col1])
output inserted.id, s.[col2] into [table2]
;
Je me débattais également avec ce problème et découvrais que la meilleure façon de procéder consistait à utiliser un curseur .
J'ai essayé la solution Denis avec OUTPUT, mais comme il l'a mentionné, il est impossible de générer des colonnes externes dans une instruction insert, et MERGE ne peut pas fonctionner lors de l'insertion de plusieurs lignes par select.
Donc, j'ai utilisé un CURSEUR, pour chaque ligne de la table externe, j'ai fait un INSERT, puis utilisez le @@ IDENTITY pour un autre INSERT.
DECLARE @OuterID int
DECLARE MY_CURSOR CURSOR
LOCAL STATIC READ_ONLY FORWARD_ONLY
FOR
SELECT ID FROM [external_Table]
OPEN MY_CURSOR
FETCH NEXT FROM MY_CURSOR INTO @OuterID
WHILE @@FETCH_STATUS = 0
BEGIN
INSERT INTO [Table] (data)
SELECT data
FROM [external_Table] where ID = @OuterID
INSERT INTO [second_table] (FK,OuterID)
VALUES(@OuterID,@@identity)
FETCH NEXT FROM MY_CURSOR INTO @OuterID
END
CLOSE MY_CURSOR
DEALLOCATE MY_CURSOR
Gardez un œil sur SQL Server pour qu'il prenne en charge l'instruction 'INSERT ALL'. Oracle l'a déjà, cela ressemble à ceci ( SQL Cookbook ):
insert all
when loc in ('NEW YORK', 'BOSTON') THEN
into dept_east(deptno, dname, loc) values(deptno, dname, loc)
when loc in ('CHICAGO') THEN
into dept_mid(deptno, dname, loc) values(deptno, dname, loc)
else
into dept_west(deptno, dname, loc) values(deptno, dname, loc)
select deptno, dname, loc
from dept
BEGIN TRANSACTION;
DECLARE @tblMapping table(sourceid int, destid int)
INSERT INTO [table1] ([data])
OUTPUT source.id, new.id
Select [data] from [external_table] source;
INSERT INTO [table2] ([table1_id], [data])
Select map.destid, source.[more data]
from [external_table] source
inner join @tblMapping map on source.id=map.sourceid;
COMMIT TRANSACTION;
Une autre option consiste à exécuter les deux insertions séparément, en laissant la colonne FK NULL, puis en exécutant une mise à jour pour la générer correctement.
S'il n'y a rien de naturel stocké dans les deux tables qui correspond d'un enregistrement à un autre (probablement), créez une colonne temporaire GUID) et remplissez-la dans vos données, puis insérez-la dans les deux champs. mettre à jour avec le FK approprié et annuler les GUID.
Par exemple.:
CREATE TABLE [dbo].[table1] (
[id] [int] IDENTITY(1,1) NOT NULL,
[data] [varchar](255) NOT NULL,
CONSTRAINT [PK_table1] PRIMARY KEY CLUSTERED ([id] ASC),
JoinGuid UniqueIdentifier NULL
)
CREATE TABLE [dbo].[table2] (
[id] [int] IDENTITY(1,1) NOT NULL,
[table1_id] [int] NULL,
[data] [varchar](255) NOT NULL,
CONSTRAINT [PK_table2] PRIMARY KEY CLUSTERED ([id] ASC),
JoinGuid UniqueIdentifier NULL
)
INSERT INTO Table1....
INSERT INTO Table2....
UPDATE b
SET table1_id = a.id
FROM Table1 a
JOIN Table2 b on a.JoinGuid = b.JoinGuid
WHERE b.table1_id IS NULL
UPDATE Table1 SET JoinGuid = NULL
UPDATE Table2 SET JoinGuid = NULL
Create table #temp1
(
id int identity(1,1),
name varchar(50),
profession varchar(50)
)
Create table #temp2
(
id int identity(1,1),
name varchar(50),
profession varchar(50)
)
----- requête principale ------
insert into #temp1(name,profession)
output inserted.name,inserted.profession into #temp2
select 'Shekhar','IT'