170

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

我想从DATA_TABLE链接到一个给定对象标识的位置复制数据,并将相应的记录插入到Data_Table不同Link_Table的给定对象标识中。

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

这是最好的方法吗?

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

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

4

11 回答 11

258

在一个声明中:没有。

在一笔交易中:是

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

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

于 2008-10-06T16:49:35.730 回答
39

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

于 2008-10-06T16:49:21.860 回答
20

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

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

感谢另一个将我指向 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使用临时表,然后以正常插入完成。所以我可以避免我的循环,但我不能避免临时表。

于 2008-10-06T19:16:38.383 回答
10

我想强调使用

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
于 2016-11-01T01:43:16.920 回答
6

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

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

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

于 2008-10-06T16:50:33.013 回答
4

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

于 2008-10-06T16:42:25.137 回答
4

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

于 2008-10-06T16:53:26.237 回答
3

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

于 2008-10-06T17:25:59.493 回答
2

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

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

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

于 2008-10-06T16:42:51.813 回答
-3

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

$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 张桌子。

于 2011-03-31T23:10:30.923 回答
-3
-- ================================================
-- 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
于 2015-05-07T13:30:06.240 回答