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

时间:2010-09-14 20:34:06

标签: sql sql-server tsql insert foreign-keys

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

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],因为我正在做这件事?这甚至可能吗?

7 个答案:

答案 0 :(得分:29)

试试这个:

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

更新:重新:

  

Denis - 这似乎与我想做的非常接近,但也许你可以为我修复以下SQL语句?基本上[table1]中的[data]和[table2]中的[data]表示来自[external_table]的两个不同/不同的列。您在上面发布的声明仅在您希望[data]列相同时才有效。

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

答案 1 :(得分:4)

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

我已尝试使用OUTPUT进行Denis解决方案,但正如他所提到的,在insert语句中输出外部列是不可能的,并且当通过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

答案 2 :(得分:1)

请留意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

答案 3 :(得分:0)

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;

答案 4 :(得分:0)

另一种选择是单独运行两个插入,将FK列保留为null,然后运行更新以正确填充它。

如果两个表中没有任何内容存储,从一个记录到另一个记录(可能),则创建一个临时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

答案 5 :(得分:0)

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

-----主要查询------

insert into #temp1(name,profession)

output inserted.name,inserted.profession into #temp2

select 'Shekhar','IT'

答案 6 :(得分:-1)

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