我如何在SQL Server中同时将数据插入到两个表中?

假设我的表结构如下所示:

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) ) 

第一个表的[id]字段对应于第二个表的[table1_id]字段。 我想要做的是在一个事务中将数据插入到两个表中。 现在我已经知道如何通过执行INSERT-SELECT-INSERT来做到这一点,如下所示:

 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; 

对于那些只插入一些行的小案例来说,这一切都很好。 但是我需要做的是一次插入几十万行甚至一百万行。 数据来自另一个表,所以如果我只是将它插入到一个表中,这将是容易的,我只需要这样做:

 INSERT INTO [table] ([data]) SELECT [data] FROM [external_table]; 

但我怎么做到这一点,并将数据拆分到[table1][table2] ,并仍然更新[table2]与适当的[table1_id]因为我这样做呢? 这甚至可能吗?

尝试这个:

 insert into [table] ([data]) output inserted.id, inserted.data into table2 select [data] from [external_table] 

更新: Re:

Denis – 这看起来和我想要做的非常接近,但也许你可以为我解决下面的SQL语句? 基本上[table1]中的[data]和[table2]中的[data]代表[external_table]中的两个不同/不同的列。 您在上面发表的声明只有当您希望[数据]列是相同的。

 INSERT INTO [table1] ([data]) OUTPUT [inserted].[id], [external_table].[col2] INTO [table2] SELECT [col1] FROM [external_table] 

insert语句中输出外部列是不可能的,所以我认为你可以这样做

 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] ; 

我也在努力解决这个问题,并发现最好的方法是使用CURSOR

我曾尝试使用OUTPUT进行Denis解决scheme,但是正如他所说的那样,在插入语句中不可能输出外部列,并且在通过select插入多行时,MERGE无法工作。

所以,我使用了一个CURSOR,对于外表中的每一行,我已经完成了INSERT,然后使用@@ IDENTITY作为另一个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 

留意SQL Server以支持“INSERT ALL”语句。 Oracle已经有了,看起来像这样( 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; 

另一个select是分别运行两个插入,将FK列留空,然后运行更新以正确地填充它。

如果没有任何内容存储在从一个logging到另一个logging匹配的两个表中(可能),则创build一个临时GUID列,并将其填充到数据中并插入到两个字段中。 然后,您可以使用正确的FK进行更新,并删除GUID。

例如:

 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 

您可以编写一个存储过程来遍历您提出的事务。 迭代器将是包含源数据的表的游标。