使用INSERT_IDENTITY插入select到多个相关表中


10

好,设置场景。我有三个表,( Table1Table2DataTable),我想插入Table1Table2使用DataTable作为源。所以对于每一行DataTable我都想要在Table1和中一行Table2,并且Table2需要idTable1...中插入(PK)。

如果我要这样做...

INSERT INTO Table1 SELECT A, B, C FROM MyTable
INSERT INTO Table2 SELECT IDENTITY_INSERT(), D, E, F FROM MyTable

我将获得ID最后插入的记录的Table1

CURSORor WHILE循环的唯一方法吗?

Answers:


10

一个可能适合您的解决方案是使用OUTPUT子句,该子句会吐出所有插入的行,因此您可以将它们重新插入到另一个表中。但是,如果有内存,这会限制Table2上的外键约束。

无论如何,解决方案看起来像这样:

MERGE INTO Table1 AS t1
USING MyTable ON 1=0 -- always generates "not matched by target"

WHEN NOT MATCHED BY TARGET THEN
    -- INSERT into Table1:
    INSERT (A, B, C) VALUES (t1.A, t1.B, t1.C)

--- .. and INSERT into Table2:
OUTPUT inserted.ID, MyTable.D, MyTable.E, MyTable.F
INTO Table2 (ID, D, E, F);

与其他DML语句相反,MERGE可以引用Just inserted和以外的其他表deleted,这在这里对您很有用。

更多:http//sqlsunday.com/2013/08/04/cool-merge-features/


4

如果您打算定期执行此操作(即它是应用程序逻辑的一部分,而不是一次性数据转换工作),则可以使用Table1和Table2上的视图以及INSTEAD OF INSERT触发器来管理数据拆分(和整理)键/关系)-那么您只需执行以下操作:

INSERT newView SELECT NEWID(), A, B, C, D, E, F FROM MyTable

触发器可以很简单:

CREATE trg_newview_insert TRIGGER newView INSTEAD OF UPDATE AS 
    INSERT table1 SELECT ID, A, B, C FROM inserted
    INSERT table2 SELECT ID, D, E, F FROM inserted
GO

假设视图是这样的:

CREATE VIEW newView AS 
SELECT table1.ID, A, B, C, D, E, F 
FROM table1 
    JOIN table2 ON table1.ID = table2.ID;

或者每个表中可能有行而另一个表中没有匹配的行:

CREATE VIEW newView AS 
SELECT ISNULL(table1.ID, table2.ID), A, B, C, D, E, F 
FROM table1 
    FULL OUTER JOIN table2 ON table1.ID = table2.ID;

(当然,SELECT如果您不打算从视图中输出,则从视图中输出的行并不重要SELECT,并且仅存在于提供模板以INSERT供触发器执行其操作的时候)

这是假设您打算在这种情况下将UUID类型用作主键-如果在table1上使用自动递增的整数键,则需要做更多的工作。类似以下内容可能会起作用:

CREATE trg_newview_insert TRIGGER newView INSTEAD OF UPDATE AS 
    INSERT table1 (A, B, C) 
    SELECT A, B, C 
    FROM inserted;
    INSERT table2 (ID, D, E, F) 
    SELECT ID, D, E, F 
    FROM table1 AS t 
        JOIN inserted AS i ON t.A = i.A AND t.B = i.B AND t.C = i.C;
GO

实际上,一对INSERT语句可能像这样一次性使用(不管您使用的是键INT IDENTITY还是UNIQUEIDENTIFIER DEFAULT NEWID()键的类型):

INSERT table1 (A, B, C) 
SELECT A, B, C 
FROM MyTable;
INSERT table2 (ID, D, E, F) 
SELECT ID, D, E, F 
FROM table1 AS t 
    JOIN MyTable AS i ON t.A = i.A AND t.B = i.B AND t.C = i.C;

完全不需要视图并触发,尽管如果您经常在代码中执行此操作,则仍然有必要考虑使用view + trigger来每次都抽象出对多个语句的需求。

CAVEAT:以上所有SQL都是经过仔细考虑而输入的,未经测试,因此需要任何工作才能保证可以根据需要进行工作。


3

好像您想要的:

INSERT dbo.Table1(A,B,C) SELECT A,B,C 
  FROM dbo.DataTable WHERE <identify one row>;

INSERT dbo.Table2(ID,D,E,F) SELECT SCOPE_IDENTITY(),D,E,F
  FROM dbo.DataTable WHERE <identify that same row>;

或者,也许只使用一个表,如果您总是在每个表中都有一行...您是否有充分的理由将这些表拆分成多个表?


1
系统在我从事项目之前就已经到位,负责SE的工作人员想尝试表继承,如果您使用Entity Framework并从代码中进行操作,这很好,因为它可以隐藏所有内容,但在您需要切换时对于ADO而言,由于性能不佳,这是一场噩梦!
m4rc 2014年

1

通过阅读您的问题以及对其他答案的评论,您似乎正在尝试DataTable通过将问题拆分为两个新表来解决问题。

我假设DataTable还没有单个唯一字段,例如IDENTITY(1,1)??如果没有,也许您应该添加一个可用于将数据插入到Table1和中的代码Table2

举个例子:我创建了一个示例架构,将测试数据插入DataTable,修改DataTable为具有一IDENTITY(1,1)列,然后使用该数据将数据插入Table1和中Table2

USE tempdb;
GO

CREATE TABLE dbo.DataTable
(
    A INT
    , B INT
    , C INT
    , D INT
    , E INT
    , F INT
);

INSERT INTO dbo.DataTable (A, B, C, D, E, F)
VALUES (1, 2, 3, 11, 12, 13)
    , (4, 5, 6, 14, 15, 16)
    , (7, 8, 9, 17, 18, 19);

CREATE TABLE dbo.Table1
(
    Table1PK INT NOT NULL CONSTRAINT PK_Table1 PRIMARY KEY CLUSTERED IDENTITY(1,1)
    , A INT
    , B INT
    , C INT
);

CREATE TABLE dbo.Table2
(
    Table2PK INT NOT NULL CONSTRAINT PK_Table2 PRIMARY KEY CLUSTERED IDENTITY(1,1)
    , Table1PK INT NOT NULL CONSTRAINT FK_Table2_Table1_PK FOREIGN KEY REFERENCES dbo.Table1(Table1PK)
    , D INT
    , E INT
    , F INT
);

ALTER TABLE dbo.DataTable ADD TempCol INT NOT NULL IDENTITY(1,1);

SET IDENTITY_INSERT dbo.Table1 ON;

INSERT INTO Table1 (Table1PK, A, B, C)
SELECT TempCol, A, B, C 
FROM DataTable;

SET IDENTITY_INSERT dbo.Table1 OFF;

INSERT INTO Table2 
SELECT Table1PK, D, E, F 
FROM dbo.DataTable DT
    INNER JOIN dbo.Table1 T ON DT.TempCol = T.Table1PK;

SELECT *
FROM dbo.Table1;

SELECT *
FROM dbo.Table2;

-1
INSERT INTO VouchersOtherDetail (
                                 [VouNo],
                                 [Location],
                                 [VouType],
                                 [VouDate],
                                 [InputDate],
                                 [CrossRefGoodsVouNo],
                                 [Reversed],
                                 [ReversalReference],
                                 [UserID]
                                 ) 
SELECT   
                                [VouNo],
                                [Location],
                                [VouType],
                                [VouDate],
                                [InputDate],
                                [CrossRefGoodsVouNo],
                                [Reversed],
                                [ReversalReference],
                                [UserID]
FROM @InsertTableForVoucherDetail           

INSERT INTO VouchersDrCrDetail (
                                [VouID],
                                [AccountCode],
                                [CrossReferAccountCode],
                                [Description],
                                [VouDrAmount],
                                [VouCrAmount],
                                [RunningBalance]
                               )
SELECT  -- IDENT_CURRENT to get the identity of row from previous insert
                                 IDENT_CURRENT('VouchersOtherDetail'), 
                                 [AccountCode],
                                 [CrossReferAccountCode],
                                 [Description],
                                 [VouDrAmount],
                                 [VouCrAmount],
                                 [RunningBalance]
FROM @InsertTableForDrAndCR

这件事对我有用,我知道它的回复很晚,但可能会帮助别人。我IDENT_CURRENT以前从前一次插入中获取行的标识,但对我而言,它始终是一行。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.