web-dev-qa-db-fra.com

Comment puis-je INSÉRER des données dans deux tables simultanément dans SQL Server?

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?

50
soapergem

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]
;
30
Denis Valeev

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
4
IFink

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
1
Brian
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;
0
Bill

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
0
cjk
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'
0
Shekhar Kumar