【问题标题】:How can I INSERT data into two tables simultaneously in SQL Server?如何在 SQL Server 中同时将数据插入到两个表中?
【发布时间】:2010-09-14 20:34:06
【问题描述】:

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

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],并且仍然使用适当的[table1_id] 更新[table2]?这可能吗?

【问题讨论】:

  • 很多SQL初学者经常问的好问题。

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


【解决方案1】:

试试这个:

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

【讨论】:

  • 丹尼斯,我只使用 OUTPUT 写入表变量。你能用它直接插入一个活表吗?
  • 顺便说一句,没有必要在begin tran... commit tran 语句中包含它,因为它显然会在单个事务中运行。
  • 丹尼斯 - 这似乎非常接近我想要做的,但也许你可以为我修复以下 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]
  • 我最初对此感到很兴奋,但后来遇到了这个错误:“OUTPUT INTO 子句的目标表不能有任何启用的检查约束”——不可能发生这种情况!哦,好吧,回到使用暂存临时表或表变量来存储输出。无论如何很高兴知道:)
  • @onedaywhen 啊哈哈哈! :))) 是的,有很多东西不需要在那个输出表中(fk、约束、触发器),所以除了临时表之外它基本上没用。
【解决方案2】:

我也在为这个问题苦苦挣扎,发现最好的方法是使用 CURSOR

我尝试过使用 OUTPUT 的 Denis 解决方案,但正如他所说,在插入语句中无法输出外部列,并且通过 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

【讨论】:

    【解决方案3】:

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

    【讨论】:

      【解决方案4】:
      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;
      

      【讨论】:

      • 鉴于丹尼斯对我的评论的回应,他的解决方案比我的要干净得多。
      • 您不能在output 子句中使用source.id, new.id。您只能在其中使用inserted.* 来表示insert。对于deleteupdatemerge,可以包含指定表中的列。
      【解决方案5】:
      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】:

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

      【讨论】:

      • 当有基于集合的解决方案时,永远不要迭代。
      • @HLGEM - 鉴于我的错误,您会推荐哪种解决方案?
      • 绝对是临时表的输出子句。
      【解决方案7】:

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

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

      【讨论】:

      • 如何为两个表设置相同的JoinGuid?如果您在Table1 中有一条记录,在Table2 中有一条记录,这很简单,但我无法想象在多行的情况下如何实现。
      • 在您的代码中的某个位置,您应该能够关联数据,在该位置添加 guid。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-12-29
      • 2012-02-22
      • 2016-08-15
      • 1970-01-01
      相关资源
      最近更新 更多