【问题标题】:SQL Server: Is it possible to insert into two tables at the same time?SQL Server:是否可以同时插入两个表?
【发布时间】:2010-09-15 13:27:24
【问题描述】:

我的数据库包含三个名为Object_TableData_TableLink_Table 的表。链接表只包含两列,一个对象记录的标识和一个数据记录的标识。

我想从链接到一个给定对象标识的DATA_TABLE 复制数据,并将相应的记录插入到Data_TableLink_Table 以获得不同的给定对象标识。

可以通过选择一个表变量并循环通过为每次迭代执行两次插入来做到这一点。

这是最好的方法吗?

编辑:我想避免循环有两个原因,第一个是我很懒,循环/临时表需要更多的代码,更多的代码意味着更多的地方出错和第二个原因是对性能的担忧。

我可以在一次插入中复制所有数据,但是如何让链接表链接到每条记录都有新 id 的新数据记录?

【问题讨论】:

  • 我没有兴趣尝试使用 ONE 插入来进行操作,但使用 2 插入时效果非常好。你的意思是你想确保两个插入都完成了吗?然后你必须检查这个提交/回滚指令。
  • 我对两次插入很满意,只是需要插入链接表的身份是第一次插入时生成的身份。

标签: sql sql-server insert temp-tables


【解决方案1】:

如果您希望操作或多或少是原子的,我会确保将它们包装在事务中。这样你就可以确定两者都发生了,或者两者都没有按照需要发生。

【讨论】:

  • 如果操作被包装在事务中,则它们是原子的,而不是“或多或少”原子的。除非您指定,否则不一定能保证隔离级别。
【解决方案2】:

Insert 一次只能对一张表进行操作。多个插入必须有多个语句。

我不知道您需要通过表变量进行循环 - 您不能只在一个表中使用批量插入,然后在另一个表中使用批量插入吗?

顺便说一句 - 我猜你的意思是从 Object_Table 复制数据;否则这个问题没有意义。

【讨论】:

    【解决方案3】:

    您仍然需要两个 INSERT 语句,但听起来您想从第一个插入中获取 IDENTITY 并在第二个插入中使用它,在这种情况下,您可能需要查看 OUTPUT 或 @ 987654325@:http://msdn.microsoft.com/en-us/library/ms177564.aspx

    【讨论】:

    • 是否可以在一个 sql 中使用两次“OUTPUT INTO”
    • @V.Wu 我不这么认为,我得设置一个测试才能看到。
    【解决方案4】:

    在一个声明中:没有。

    在一个交易中:是

    BEGIN TRANSACTION
       DECLARE @DataID int;
       INSERT INTO DataTable (Column1 ...) VALUES (....);
       SELECT @DataID = scope_identity();
       INSERT INTO LinkTable VALUES (@ObjectID, @DataID);
    COMMIT
    

    好消息是,上面的代码也保证是原子,并且可以在单个函数调用中使用一个 sql 字符串从客户端应用程序发送到服务器,就好像它是一个陈述。您还可以将触发器应用于一个表以获得单个插入的效果。但是,它最终仍然是两个语句,您可能不想为 每个 插入运行触发器。

    【讨论】:

    • @Joel,好问题。大概有人希望有另一种现实,而你是坏消息的承担者。 ;)
    • 最佳答案!我浪费了很多时间试图弄清楚如何在单个查询中链接插入。谢谢!!
    • 这并不能解决问题。他想插入从 Object_Table 读取的数据。 IE。 insert into ... select ... 声明。上述代码如何读取或循环 Object_Table 数据。然后,您仍然需要使用提问者不想做的表变量。
    • @hofnarwillie 是对的,使用此解决方案您仍然必须显式声明一个循环。
    • 当然可以解决问题。也许我没有为此编写 all 的代码,但是 OP 也没有共享他想要复制的所有列。此答案中演示的功能将允许 OP 执行他的要求...运行查询以创建记录,获取新记录的 ID,并以原子方式将该 ID 用于第二条记录。 OP 已经知道如何进行插入/选择。这是他缺少的部分。
    【解决方案5】:

    听起来 Link 表捕获了 Object 表和 Data 表之间的多对多关系。

    我的建议是使用存储过程来管理事务。当您要插入到 Object 或 Data 表时,请执行插入,获取新的 ID 并将它们插入到 Link 表中。

    这允许您的所有逻辑保持封装在一个易于调用的存储过程中。

    【讨论】:

    • 为什么没有其他人支持你?存储过程是显而易见且最好的方法。将您的答案与 Joel Coehoorn 的答案结合起来,您将获得最佳答案!
    【解决方案6】:

    您可以创建一个视图,选择插入语句所需的列名,添加一个 INSTEAD OF INSERT 触发器,然后插入到该视图中。

    【讨论】:

      【解决方案7】:

      在能够在 Oracle 中执行多表插入之前,您可以使用一种技巧,包括将插入到视图中,该视图上定义了 INSTEAD OF 触发器来执行插入。这可以在 SQL Server 中完成吗?

      【讨论】:

        【解决方案8】:

        以下设置了我的情况,使用表变量。

        DECLARE @Object_Table TABLE
        (
            Id INT NOT NULL PRIMARY KEY
        )
        
        DECLARE @Link_Table TABLE
        (
            ObjectId INT NOT NULL,
            DataId INT NOT NULL
        )
        
        DECLARE @Data_Table TABLE
        (
            Id INT NOT NULL Identity(1,1),
            Data VARCHAR(50) NOT NULL
        )
        
        -- create two objects '1' and '2'
        INSERT INTO @Object_Table (Id) VALUES (1)
        INSERT INTO @Object_Table (Id) VALUES (2)
        
        -- create some data
        INSERT INTO @Data_Table (Data) VALUES ('Data One')
        INSERT INTO @Data_Table (Data) VALUES ('Data Two')
        
        -- link all data to first object
        INSERT INTO @Link_Table (ObjectId, DataId)
        SELECT Objects.Id, Data.Id
        FROM @Object_Table AS Objects, @Data_Table AS Data
        WHERE Objects.Id = 1
        

        感谢另一个answer 将我指向 OUTPUT 子句,我可以展示一个解决方案:

        -- now I want to copy the data from from object 1 to object 2 without looping
        INSERT INTO @Data_Table (Data)
        OUTPUT 2, INSERTED.Id INTO @Link_Table (ObjectId, DataId)
        SELECT Data.Data
        FROM @Data_Table AS Data INNER JOIN @Link_Table AS Link ON Data.Id = Link.DataId
                        INNER JOIN @Object_Table AS Objects ON Link.ObjectId = Objects.Id 
        WHERE Objects.Id = 1
        

        然而事实证明,由于以下错误,它在现实生活中并没有那么简单

        OUTPUT INTO 子句不能开启 任一侧(主键,外来 关键)关系

        我仍然可以OUTPUT INTO 一个临时表,然后正常插入完成。所以我可以避免我的循环,但我不能避免临时表。

        【讨论】:

        • 我不认为这是一个有效的答案,因为它使用的数据库表在高负载数据库系统中增加了头上的重要性。最好的解决方案是“谢尔盖·齐诺维耶夫”给出的解决方案,这是对上面排名靠前的答案的补充。请注意,如果没有“SET XACT_ABORT ON;”子句,排名靠前的答案是不正确的。
        • 变量名超级混乱
        【解决方案9】:

        //如果要插入与第一个表相同的内容

        $qry = "INSERT INTO table (one, two, three) VALUES('$one','$two','$three')";
        
        $result = @mysql_query($qry);
        
        $qry2 = "INSERT INTO table2 (one,two, three) VVALUES('$one','$two','$three')";
        
        $result = @mysql_query($qry2);
        

        //或者如果你想插入表一的某些部分

         $qry = "INSERT INTO table (one, two, three) VALUES('$one','$two','$three')";
        
        
          $result = @mysql_query($qry);
        
         $qry2 = "INSERT INTO table2 (two) VALUES('$two')";
        
         $result = @mysql_query($qry2);
        

        //我知道它看起来太好了,但它确实有效,您可以继续添加查询,只需更改

            "$qry"-number and number in @mysql_query($qry"")
        

        我有 17 个表已经使用过。

        【讨论】:

        • 如果在插入过程中出现问题?您的插入内容将不完整。正确的?如果这样做..你有回滚功能来对待它吗?如果不是......你的数据完整性有问题。
        • -1。这个答案似乎是在 PHP 中使用 MySQL 方法。该问题被标记为sqlsql-server,没有提及MySQL 或PHP。
        【解决方案10】:
        -- ================================================
        -- Template generated from Template Explorer using:
        -- Create Procedure (New Menu).SQL
        --
        -- Use the Specify Values for Template Parameters 
        -- command (Ctrl-Shift-M) to fill in the parameter 
        -- values below.
        --
        -- This block of comments will not be included in
        -- the definition of the procedure.
        -- ================================================
        SET ANSI_NULLS ON
        GO
        SET QUOTED_IDENTIFIER ON
        GO
        
        ALTER PROCEDURE InsetIntoTwoTable
        
        (
        @name nvarchar(50),
        @Email nvarchar(50)
        )
        
        AS
        BEGIN
        
            SET NOCOUNT ON;
        
        
            insert into dbo.info(name) values (@name)
            insert into dbo.login(Email) values (@Email)
        END
        GO
        

        【讨论】:

        • 你能补充一些解释吗?
        【解决方案11】:

        我想强调使用

        SET XACT_ABORT ON;
        

        用于具有多个 sql 语句的 MSSQL 事务。

        见:https://msdn.microsoft.com/en-us/library/ms188792.aspx 他们提供了一个很好的例子。

        因此,最终代码应如下所示:

        SET XACT_ABORT ON;
        
        BEGIN TRANSACTION
           DECLARE @DataID int;
           INSERT INTO DataTable (Column1 ...) VALUES (....);
           SELECT @DataID = scope_identity();
           INSERT INTO LinkTable VALUES (@ObjectID, @DataID);
        COMMIT
        

        【讨论】:

        • 只有你的答案才是最相关和最正确的。批准的答案不是最好的。投票最高的答案有你提到的缺陷。
        猜你喜欢
        • 2010-12-04
        • 2021-08-11
        • 2021-04-06
        • 2010-12-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-11-22
        相关资源
        最近更新 更多