我如何在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
您可以编写一个存储过程来遍历您提出的事务。 迭代器将是包含源数据的表的游标。