【问题标题】:How to insert master detail records in T-SQL?如何在 T-SQL 中插入主从记录?
【发布时间】:2014-11-07 13:05:51
【问题描述】:

我需要复制一些主从记录,大致如下:

INSERT INTO Order
(
    SupplierId
   ,DateOrdered
)
SELECT 
   SupplierID
  ,DateOrdered
FROM Order
WHERE SupplierId = 10

DECLARE @OrderId int;

Select @OrderId = Scope_Identity;

INSERT INTO OrderItem
(
   Quantity
  ,ProductCode
  ,Price
  ,FkOrderId
)
SELECT 
  Quantity
 ,ProductCode
 ,Price 
 ,FkOrderId
FROM OrderItem 
WHERE FkOrderId = @OrderId

这行不通。原因是供应商 = 10 有多个订单。那么在供应商 = 10 的每个订单中迭代的最佳方法是什么,添加订单,然后在进入下一个订单记录之前添加相关的子订单项供应商 = 10.我想我说的是批处理,可能是游标,但我是 T-SQL / Store Procedures 的新手。

我将不胜感激上述建议。

谢谢。

编辑

我希望通过一些示例数据来澄清更多信息。

Original Order Table

Id       SupplierId         DateOrdered
 1          10                01/01/2000
 2          10                01/01/2000

Original OrderItem Table

Id       Quantity      ProductCode     Price      FkOrderId
 1          20             X1           100           1
 2          10             Y1            50           1
 3          30             X1           100           2
 4          20             Y1            50           2

Final Order Table

Id       SupplierId         DateOrdered
 1          10                01/01/2000
 2          10                01/01/2000
 3          10                01/01/2000    (Clone of 1)
 4          10                01/01/2000    (Clone of 2)

Final OrderItem Table

Id       Quantity      ProductCode     Price      FkOrderId
 1          20             X1           100           1
 2          10             Y1            50           1
 3          30             X1           100           2
 4          20             Y1            50           2
 5          20             X1           100           3 (Clone of 1, linked to clone Order=3)
 6          10             Y1            50           3 (Clone of 2, linked to clone Order=3)
 7          30             X1           100           4 (Clone of 3, linked to clone Order=4)
 8          20             Y1            50           5 (Clone of 4, linked to clone Order=4)

所以我需要一些帮助,代码可以克隆 Order 和 OrderItem 以实现“最终”表记录。

看来我需要这样做:

For each matching record in "Order"

  Clone Order Record
  Clone OrderItem Record where FkOrderId = OldOrderId

Next

【问题讨论】:

  • 如果你提供一些你想要实现的数据的表格会更好,因为我真的不知道你的表格中有哪些列。
  • @SubqueryCrunch,感谢您的评论。请参阅上面的编辑。
  • 看看这个问题,Using merge..output to get mapping between source.id and target.id,以及它的 Linked 部分的例子。
  • OrderItem 更好的表名是 OrderDetail(s) 或 OrderLine(s)。

标签: sql-server sql-server-2008 tsql


【解决方案1】:

这回答了你的问题(也没有光标)

SQL Fiddle

MS SQL Server 2008 架构设置

CREATE TABLE [Order]
(
    Id Int Primary Key Identity,
    SupplierId Int,
    DateOrdered Date
)

SET IDENTITY_INSERT [Order] ON

INSERT INTO [Order] (Id, SupplierId, DateOrdered)
VALUES
    (1, 10, '01/01/2000'),
    (2, 10, '01/01/2000')

SET IDENTITY_INSERT [Order] OFF


CREATE TABLE [OrderItem]
(
    ID INT Primary Key Identity,
    Quantity Int,
    ProductCode CHAR(2),
    Price Int,
    FKOrderId Int
)
SET IDENTITY_INSERT [OrderItem] ON

INSERT INTO [OrderItem] (Id, Quantity, ProductCode, Price, FKOrderId)
VALUES 
    (1, 20, 'X1', 100, 1),
    (2, 10, 'Y1', 50, 1),
    (3, 30, 'X1', 100, 2),
    (4, 20, 'Y1', 50, 2)

SET IDENTITY_INSERT [OrderItem] OFF

查询 1

DECLARE @NewEntries TABLE (ID Int, OldId Int);


MERGE INTO [Order]
USING [Order] AS cf
ON 1 = 0 -- Ensure never match - therefore an Insert
WHEN NOT MATCHED AND cf.SupplierId = 10 THEN
  INSERT(SupplierId, DateOrdered) Values(cf.SupplierId, cf.DateOrdered)
Output inserted.Id, cf.Id INTO 
  @NewEntries(Id, OldId);

INSERT INTO [OrderItem]
(
   Quantity
  ,ProductCode
  ,Price
  ,FkOrderId
)
SELECT 
  Quantity
 ,ProductCode
 ,Price 
 ,NE.ID
FROM [OrderItem] OI
INNER JOIN @NewEntries NE
    ON OI.FKOrderId = NE.OldId ;


SELECT *
FROM [OrderItem];

Results

| ID | QUANTITY | PRODUCTCODE | PRICE | FKORDERID |
|----|----------|-------------|-------|-----------|
|  1 |       20 |          X1 |   100 |         1 |
|  2 |       10 |          Y1 |    50 |         1 |
|  3 |       30 |          X1 |   100 |         2 |
|  4 |       20 |          Y1 |    50 |         2 |
|  5 |       20 |          X1 |   100 |         3 |
|  6 |       10 |          Y1 |    50 |         3 |
|  7 |       30 |          X1 |   100 |         4 |
|  8 |       20 |          Y1 |    50 |         4 |

【讨论】:

  • 太棒了!允许访问源标识符和新标识符。感谢这个创造性的解决方案!
【解决方案2】:

在 Order 表中添加一个名为 OriginalOrderId 的附加列。使其可以为空,FK'd 回到 OrderId,并在其上放置一个索引。使用“INSERT INTO [Order] ... SELECT ... OUTPUT INSERTED.* INTO #ClonedOrders From ...”。在#ClonedOrders.OriginalOrderId 上添加索引。然后您可以执行“插入 OrderItem ... SELECT co.OrderId, ... FROM #ClonedOrders co INNER JOIN OrderItem oi ON oi.OrderId = co.OriginalOrderId”。这将为您提供您正在寻找的功能,以及基于集合的语句的性能优势。它还会为您留下订单原始来源的证据以及可用于区分克隆订单和非克隆订单的字段。

【讨论】:

    【解决方案3】:

    在这种情况下,你必须使用输出子句。让我给你一个示例脚本,它可以帮助你与你的需求联系起来

    Declare @Order AS Table(id  int identity(1,1),SupplierID INT)
    
    DECLARE @outputOrder AS TABLE 
    (Orderid INT)
    
    INSERT INTO @Order (SupplierID)
        Output inserted.id into @outputOrder
        Values (102),(202),(303)    
    
    
    select * from @outputOrder
    

    您的案例的下一步将是使用从 outputorder 表中新生成的 orderid 并连接以从输入表中获取 orderitems

    【讨论】:

    • 谢谢。但是我需要将主记录和详细记录链接在一起。希望我对问题的“编辑”能够澄清我的要求。
    【解决方案4】:

    这将处理您的第一个表。

    PS:在这种状态下提供您的问题,他们将得到更快的回答。

    IF OBJECT_ID('Orders') IS NOT NULL DROP TABLE Orders
    IF OBJECT_ID('OrderItem') IS NOT NULL DROP TABLE OrderItem
    IF OBJECT_ID('tempdb..#FinalOrders') IS NOT NULL DROP TABLE #FinalOrders
    
    
    CREATE TABLE Orders (OrdersID INT, SupplierID INT, DateOrdered DATETIME)
    
    CREATE TABLE OrderItem (OrderItemID INT, Quantity INT, FkOrderId INT)
    
    INSERT INTO Orders VALUES (1,20,'01/01/2000'),(2,20,'01/01/2000')
    
    INSERT INTO OrderItem VALUES
    (1,20,1),
    (2,10,1),
    (3,30,2),
    (4,20,2)
    
    SELECT 
        a.OrderItemID,
        b.SupplierID,
        b.DateOrdered
    INTO #FinalOrders
    FROM OrderItem as a
    INNER JOIN Orders as b
    ON a.FkOrderId = b.OrdersID
    
    SELECT * FROM #FinalOrders
    

    【讨论】:

    • 感谢您的回答。我不认为这很容易。我提供的数据是举例。我将无法明确定义要插入的数据。这就是为什么我需要一个像 Cursor 或 While 块这样的结构来使我能够遍历主记录,克隆它,然后遍历原始主记录的详细记录,并将它们克隆到详细表中,以产生如下结果我显示。所以我的问题更多是关于我是否应该使用游标或什么,它会是什么样子?再次感谢。
    • 我真的尽量避免使用游标和 while 循环,因为它们效率低下。否则我确定可以用他们来完成。它也更具挑战性:D
    【解决方案5】:

    这可以通过光标来实现。但请注意,游标会带来显着的性能缺陷。

    DECLARE @SupplierID AS INT
    DECLARE @OrderId AS INT
    DECLARE @DateOrdered AS DATE
    DECLARE @OrderIdNew AS INT
    
    Declare @Order AS Table(OrderId INT,SupplierID INT,DateOrdered Date)
    
    INSERT INTO @Order
    SELECT 
       ID
      ,SupplierID
      ,DateOrdered
    FROM [Order]
    WHERE SupplierId = 10
    
    DECLARE CUR CURSOR FAST_FORWARD FOR
    SELECT 
       OrderId
      ,SupplierID
      ,DateOrdered
    FROM @Order
    
    OPEN CUR
    FETCH NEXT FROM CUR INTO @OrderId, @SupplierID, @DateOrdered
    WHILE @@FETCH_STATUS = 0
    BEGIN
        INSERT INTO [Order]
        (
        SupplierId
       ,DateOrdered
        )
        VALUES
        (@SupplierID,@DateOrdered)
    
        Select  @OrderIdNew=@@IDENTITY
    
        INSERT INTO [OrderItem]
               ([Quantity]
               ,[ProductCode]
               ,[Price]
               ,[FkOrderId])
         SELECT [Quantity]
               ,[ProductCode]
               ,[Price]
               ,@OrderIdNew
         FROM [OrderItem]
         WHERE [FkOrderId]=@OrderId
        FETCH NEXT FROM CUR INTO @OrderId, @SupplierID, @DateOrdered
    END
    
    CLOSE CUR;
    DEALLOCATE CUR;
    

    【讨论】:

    • 谢谢。光标使用的有用描述。我想知道这是否仍然比通过大约 50-100 毫秒的 C#/LINQ/EF 更快。
    【解决方案6】:

    您可以尝试在 Order 和 OrderItems 之间进行内部联接,其中内部联接的子句是 SupplierId = 10, 或者只是修改你的位置以获得相同的结果。 尝试按照以下方式做一些事情:

    INSERT INTO OrderItem
    (
       Quantity
      ,ProductCode
      ,Price
      ,FkOrderId
    )
    SELECT 
      Quantity
     ,ProductCode
     ,Price 
     ,FkOrderId
    FROM OrderItem 
    where FkOrderId in (Select Id FROM Order WHERE SupplierId = 10)
    

    【讨论】:

      猜你喜欢
      • 2016-05-25
      • 1970-01-01
      • 2010-10-16
      • 1970-01-01
      • 2010-11-05
      • 1970-01-01
      • 2012-10-22
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多