【问题标题】:Adding one to many records in stored procedure在存储过程中添加一对多记录
【发布时间】:2018-11-11 06:31:08
【问题描述】:

我需要了解 SQL 的人的帮助,因为这超出了我的 SQL 技能范围。那是我的桌子:

CREATE TABLE [dbo].[Orders]
(
    id              INT IDENTITY(1,1) NOT NULL PRIMARY KEY,
    customerName    VARCHAR(30) NOT NULL,
    customerAddress VARCHAR(30) NOT NULL,
    submitDate      DATE NOT NULL,
    realizationDate DATE,
    deliveryTypeId  INT NOT NULL

    FOREIGN KEY(deliveryTypeId) REFERENCES deliveryTypes(id)
);

CREATE TABLE [dbo].[OrderItems]
(
    id              INT IDENTITY(1,1) NOT NULL PRIMARY KEY,
    orderId         INT NOT NULL,
    productId       INT NOT NULL,
    productQuantity INT,

    FOREIGN KEY(orderId) REFERENCES Orders(id), 
    FOREIGN KEY(productId) REFERENCES Products(id)
);

我需要创建一个存储过程,将一对多相关记录插入到数据库中的两个表中(事实上,这确实是多对多绑定的一部分,但我只需要添加到这两个表中)。在我的应用程序(.NET)中,我需要传递一条记录“Orders”和一条或多条记录“OrderItems”。

我的问题是:

  • 如果我插入“订单”,我将不知道在OrderItems -> orderId 中设置什么值。我找到了SCOPE_IDENTITY 函数,但我真的不知道如何在我的情况下使用它。
  • 如果我不知道表中有多少条OrderItems 记录,如何插入?我只知道可以有一个或多个。
  • 我不知道如何调用从应用程序级别接收许多OrderItems的过程以及我应该在存储过程中声明哪些参数

我非常感谢所有帮助,因为我面临这个问题几个小时,我真的不知道该怎么做。

编辑:这是我到目前为止提出的代码:

CREATE PROCEDURE spAddOrder
    (@customerName    VARCHAR(30),
     @customerAddress VARCHAR(30),
     @submitDate      DATE,
     @deliveryTypeId  INT
    -- I don't know how to pass many OrderItems rows as argument
)
AS
BEGIN
    INSERT INTO Orders(customerName, customerAddress, submitDate, deliveryTypeId)
    VALUES (@customerName, @customerAddress, @submitDate, @deliveryTypeId)

    SELECT SCOPE_IDENTITY() AS orderId

    -- I don't know how to do it for unknown number of rows
    INSERT INTO OrderItems(orderId, productId, productQuantity)
    VALUES (@orderId, @productId, @productQuantity)
END;
GO

【问题讨论】:

  • 我建议您也可以在dba.stackexchange.com 中提问
  • @BarrJ 谢谢你的建议,我会做的
  • 如果你能在minimal reproducible example 上分享你目前的进展,那就太棒了。
  • @mjwills 我刚刚编辑了我的问题,感谢您在下面的回答。

标签: c# sql stored-procedures one-to-many sql-server-2017


【解决方案1】:

虽然这个问题可能被认为过于宽泛,因此偏离主题,但它写得很好,描述了许多没有经验的开发人员面临的问题 - 所以我认为它值得回答。
所以让我们把它分解成它的成分:

  1. 问:如何在插入后取回identity 列的值?
    答: SQL Server 提供了几种方法来做最简单的可能是scope_identity,但最好的方法是使用output 子句。

  2. 问:如何将多行发送到存储过程?
    答:使用table valued parameter

现在让我们看看我们将如何做到这一点。

我们需要做的第一件事是创建一个用户定义的表类型,用于表值参数 - 所以:

CREATE TYPE [dbo].[Udt_OrderItems] AS TABLE
(
    productId       int NOT NULL,
    productQuantity int,
);
GO

然后,我们可以使用该类型作为表值参数创建存储过程。 我们还将订单详细信息作为标量参数发送到存储过程:

CREATE PROCEDURE stp_InsertOrderWithItems
(
    @customerName    varchar(30),
    @customerAddress varchar(30),
    @submitDate      date,
    @realizationDate date,
    @deliveryTypeId  int,
    @orderItems dbo.Udt_OrderItems readonly -- Table valued parameters must be readonly
)
AS

    DECLARE @Ids AS TABLE (orderId int NOT NULL) -- for the output clause

    INSERT INTO dbo.Orders (customerName, customerAddress, submitDate, realizationDate, deliveryTypeId)
    OUTPUT inserted.Id INTO @Ids
    VALUES(@customerName, @customerAddress, @submitDate, @realizationDate, @deliveryTypeId)

    INSERT INTO dbo.OrderItems (orderId, productId, productQuantity)
    SELECT orderId, productId, productQuantity
    FROM @orderItems
    CROSS JOIN @Ids -- We only have one value in @Ids so cross join is safe
GO

至于 c# 部分,这取决于您如何连接到数据库 - 我将展示一个基本 ADO.Net 版本的示例:

using(var con = new SqlConnection(connectionString))
{
    using(var cmd = new SqlCommand(con, "stp_InsertOrderWithItems"))
    {
        var dt = new DataTable()
        dt.Columns.Add("productId", typeof(int));
        dt.Columns.Add("productQuantity", typeof(int));

        // populate data table here

        cmd.Parameters.Add("@customerName", SqlDbType.VarChar, 30).Value = customerName;
        // all the other scalar parameters here...
        cmd.Parameters.Add(@orderItems, SqlDbType.Structured).Value = dt;

        con.Open();
        cmd.ExecuteNonQuery();
    }
}

【讨论】:

  • 这是一个非常有用的解释,但是你能告诉我 orderIdint 的目的是什么吗? (存储过程,12行)
  • 这是一个错字 - 应该是 orderId int(这是 @Ids 表上的一个不可为空的 int 列。答案已修复。
猜你喜欢
  • 2019-08-10
  • 1970-01-01
  • 2015-05-25
  • 1970-01-01
  • 2016-07-28
  • 1970-01-01
  • 2019-02-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多