Сбой триггера SQL Server для вставки строк в быстрой последовательности - PullRequest
0 голосов
/ 24 января 2019

Я посмотрел вокруг на SO и нашел много похожих вопросов:

SQL Server Триггер для работы с несколькими вставками строк

Обновление SQL для множественной вставки

Триггер для обработки вставок и обновлений нескольких строк

обновить несколько строк триггером после вставки (сервер sql)

Триггер не работает при вставке нескольких записей

Но у меня все еще есть проблемы с моим триггером для обновления нескольких строк при вставке нескольких строк в таблицу.

Схема кода У меня есть таблица резервирования, которая имеет столбцы ReservationID и TourComponentID соответственно. Когда я вставляю в таблицу резервирований, у меня есть следующий триггер для обновления таблицы TourComponent с помощью ReservationID из строки, только что вставленной в таблицу резервирования, с соответствующим TourComponentID:

CREATE TRIGGER [TR_Reservation_CurrentReservation] ON [Reservation] AFTER INSERT AS
UPDATE tc 
    SET tc.[CurrentReservationId] = I.ReservationID   
    FROM [tour].[TourComponent] tc 
    JOIN INSERTED I on I.TourComponentID = tc.TourComponentID
End

Этот триггер отлично работает при обновлении одного tourComponent для нового резервирования (вставка одной строки в таблицу резервирования). Однако если я попытаюсь обновить несколько компонентов тура (вставив несколько строк в таблицу резервирования, чтобы обновить несколько строк в таблице TourComponent), обновится только первый компонент тура, любые строки.

Другие ответы и исследования показали мне, что

Триггеры НЕ выполняются один раз на строку, а скорее как набор на основе операция выполняется только один раз для всей операции DML. Так что вы нужно рассматривать это как любую другую дату обновления с оператором соединения. Таким образом, я ожидал, что мое соединение с таблицей INSERTED обработало несколько строк, или я неправильно понял это?

Интересно, что если я запишу переменные триггера для TourComponentID, ReservationID и INSERTED rowcount во временную таблицу foo, я увижу, что в мою временную таблицу вставлены две записи, каждая с числом строк равным 1.

Используя sql profiler, чтобы поймать фактический sql, выполненный во время выполнения, и запустив его вручную для базы данных, я получаю две строки, обновляемые по желанию. Только при использовании Entity Framework для обновления базы данных, т.е. при запуске приложения, я обнаружил, что обновлена ​​только одна строка.

Я попытался записать значения в таблицу FOO в триггере

INSERT INTO FOO (TourComponentID, ReservationID, Rowcounts )
    SELECT i.TourComponentID, I.ReservationID, 1  --@ReservationId 
    FROM
    INSERTED I

При этом в журнал заносятся две строки с числом строк, равным 1, и правильными значениями tourcomponentsID и bookingID, но в таблице TourComponent по-прежнему обновляется только одна строка.

Любые предложения с благодарностью.

UPDATE

Идентификаторы компонентов тура передаются в виде строки в сообщении Ajax в действие MVC, где заполняются модели компонентов тура, а затем передаются для обновления по одному в коде

public void UpdateTourComponents(IEnumerable<TourComponent> tourComponents)
{
    foreach (var tourComponent in tourComponents)
    {
        UpdateTourComponent(tourComponent);
    }
}

вот вызов UpdateTourComponent

public int UpdateTourComponent(TourComponent tourComponent)
{
    return TourComponentRepository.Update(tourComponent);
}

и последний звонок для обновления

public virtual int Update(TObject TObject)
{
    Dictionary<string, List<string>> newChildKeys;
    return Update(TObject, null, out newChildKeys);
}

Таким образом, вставки происходят по одному, поэтому мой триггер вызывается один раз для каждого компонента TourComponent. Вот почему при подсчете @@ Rowcount в INSERTED и записи в Foo я получаю значение 1. Когда я запускаю вставки вручную, я получаю правильные ожидаемые результаты, поэтому я согласен с тестами @Slava Murygin, что проблема, вероятно, не связана с Сам триггер. Я подумал, что это может быть проблема со скоростью, если мы запускаем запросы один за другим, поэтому я поместил ожидание в триггер и в код, но это не помогло.

Обновление 2

Я использовал профилировщик SQL для захвата SQL, который запускается, когда работают только первые триггеры вставки.

Интересно, что когда EXACT тот же sql запускается в SQL Management Studio, триггер работает как положено, и оба компонента тура обновляются с идентификатором резервирования.

Стоит также упомянуть, что все ограничения были удалены со всех таблиц.

Любые другие предложения, что может быть причиной этой проблемы?

Ответы [ 2 ]

0 голосов
/ 29 января 2019

Таким образом, основная проблема была в Entity Framework.

this.Property(t => t.CurrentReservationId).HasColumnName("CurrentReservationId");

Это одно свойство для уровня доступа к данным SQL. Это кэшировалось и приводило к тому, что данные, считываемые из базы данных, не были последними текущими, поэтому, если у нас есть вставка в таблице Reservations, вторая вставка будет перезаписана кэшированными значениями, которые в моем случае были NULL.

Изменение строки на это решает проблему и заставляет триггер работать должным образом.

this.Property(t => t.CurrentReservationId).HasColumnName("CurrentReservationId").HasDatabaseGeneratedOption(DatabaseGeneratedOption.Computed);

Подробнее о HasDatabaseGeneratedOption

0 голосов
/ 25 января 2019

У вас другая проблема, чем у этого конкретного триггера. Попробуйте посмотреть имя таблицы, которую вы обновляете, «[tour]. [TourComponent]» или «[dbo]. [TourComponent]». Я попробовал ваш триггер, и он отлично работает:

use TestDB
GO
IF object_id('Reservation') is not null DROP TABLE Reservation;
GO
IF object_id('TourComponent') is not null DROP TABLE TourComponent;
GO
CREATE TABLE Reservation (
    ReservationID INT IDENTITY(1,1),
    TourComponentID INT
);
GO
CREATE TABLE TourComponent (
    CurrentReservationId INT,
    TourComponentID INT
);
GO
CREATE TRIGGER [TR_Reservation_CurrentReservation] ON [Reservation] AFTER INSERT AS
UPDATE tc 
    SET tc.[CurrentReservationId] = I.ReservationID   
    FROM [TourComponent] tc 
    JOIN INSERTED I on I.TourComponentID = tc.TourComponentID
GO
INSERT INTO TourComponent(TourComponentID)
VALUES (1),(2),(3),(4),(5),(6)
GO
INSERT INTO Reservation(TourComponentID)
VALUES (1),(2),(3),(4),(5),(6)
GO
SELECT * FROM Reservation
SELECT * FROM TourComponent
...