Nano Hash - криптовалюты, майнинг, программирование

Добавление одной ко многим записям в хранимой процедуре

Мне нужна помощь того, кто знает 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) мне нужно передать одну запись «Заказы» и одну или несколько записей «Заказы».

Мои проблемы:

  • Если я вставлю «Заказ», я не буду знать, какое значение установить в 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. 11.11.2018
  • @BarrJ Спасибо за предложение, я сделаю это 11.11.2018
  • Было бы здорово, если бы вы могли поделиться минимально воспроизводимым примером своего прогресса. 11.11.2018
  • @mjwills Я только что отредактировал свой вопрос, спасибо за ваш ответ ниже. 11.11.2018

Ответы:


1

Хотя этот вопрос может показаться слишком широким и, следовательно, не по теме, он хорошо написан и описывает проблему, с которой сталкиваются многие неопытные разработчики, поэтому я думаю, что он заслуживает ответа.
Итак, давайте разберем его на составляющие:

  1. Вопрос. Как вернуть значение столбца identity после вставки?
    О: SQL Server предоставляет несколько способов сделать это, самым простым из которых, вероятно, будет < a href="https://docs.microsoft.com/en-us/sql/t-sql/functions/scope-identity-transact-sql?view=sql-server-2017" rel="nofollow noreferrer">scope_identity , но лучше всего использовать output.

  2. Вопрос. Как отправить несколько строк в хранимую процедуру?
    Ответ. Используйте табличный параметр.

Теперь давайте посмотрим, как именно мы собираемся это сделать.

Первое, что нам нужно сделать, это создать определяемый пользователем тип таблицы, который будет использоваться для параметра с табличным значением, поэтому:

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();
    }
}
11.11.2018
  • Это отличное и полезное объяснение, но не могли бы вы сказать мне, какова цель orderIdint? (хранимая процедура, 12 строк) 12.11.2018
  • Это опечатка - должно быть orderId int (это необнуляемый столбец int в таблице @Ids. Ответ исправлен. 12.11.2018
  • Новые материалы

    Кластеризация: более глубокий взгляд
    Кластеризация — это метод обучения без учителя, в котором мы пытаемся найти группы в наборе данных на основе некоторых известных или неизвестных свойств, которые могут существовать. Независимо от..

    Как написать эффективное резюме
    Предложения по дизайну и макету, чтобы представить себя профессионально Вам не позвонили на собеседование после того, как вы несколько раз подали заявку на работу своей мечты? У вас может..

    Частный метод Python: улучшение инкапсуляции и безопасности
    Введение Python — универсальный и мощный язык программирования, известный своей простотой и удобством использования. Одной из ключевых особенностей, отличающих Python от других языков, является..

    Как я автоматизирую тестирование с помощью Jest
    Шутка для победы, когда дело касается автоматизации тестирования Одной очень важной частью разработки программного обеспечения является автоматизация тестирования, поскольку она создает..

    Работа с векторными символическими архитектурами, часть 4 (искусственный интеллект)
    Hyperseed: неконтролируемое обучение с векторными символическими архитектурами (arXiv) Автор: Евгений Осипов , Сачин Кахавала , Диланта Хапутантри , Тимал Кемпития , Дасвин Де Сильва ,..

    Понимание расстояния Вассерштейна: мощная метрика в машинном обучении
    В обширной области машинного обучения часто возникает необходимость сравнивать и измерять различия между распределениями вероятностей. Традиционные метрики расстояния, такие как евклидово..

    Обеспечение масштабируемости LLM: облачный анализ с помощью AWS Fargate и Copilot
    В динамичной области искусственного интеллекта все большее распространение получают модели больших языков (LLM). Они жизненно важны для различных приложений, таких как интеллектуальные..