Триггер для вставки нескольких строк на основе отношения «многие ко многим»

#sql #sql-server #tsql #triggers

Вопрос:

У меня есть три стола Reservation , Reservation_Passenger и Ticket .

В каждом бронировании может быть несколько пассажиров. Мне нужно создать триггер для вставки билета (в зависимости от количества пассажиров) каждый раз, когда статус бронирования обновляется до «Забронировано». Как я могу этого достичь?

  • Бронирование (идентификатор бронирования, статус)
  • Reservation_Passenger (идентификатор бронирования, идентификатор пассажира)
  • Билет (идентификатор билета, идентификатор пассажира, дата выдачи)

Что я пробовал:

 CREATE 
    TRIGGER Generate_Ticket 
    ON Reservation
    AFTER UPDATE
AS
    DECLARE @reservationStatus varchar(15)

    SELECT @reservationStatus =  INSERTED.Status from INSERTED

    IF @reservationStatus = 'Booked'
        BEGIN
        --stuck here

        END
GO
 

Комментарии:

1. Вы должны использовать команду СОЗДАТЬ ТРИГГЕР. Что вы пробовали до сих пор и где вас заблокировали ?.

2. Пожалуйста, опубликуйте вашу существующую попытку и объясните, что не работает или где вы застряли. Учебник по триггерам

3. @MarcGuillot Я обновил свой пост

4. @Stu Я обновил свой пост

5. Пожалуйста, прочтите эту статью

Ответ №1:

Таким же образом, как вы сохраняете статус в переменной, вы также можете получить идентификатор резервирования

 DECLARE @reservationStatus varchar(15)
DECLARE @reservationId int

SELECT @reservationId = INSERTED.reservationId, 
       @reservationStatus = INSERTED.Status 
FROM INSERTED
 

Теперь в той части, где вы застряли, чтобы создать билет для каждого пассажира при бронировании, вы можете вставить ВСТАВКУ с ВЫБОРОМ соответствующих пассажиров.

 INSERT INTO Ticket (passengerId, issuedDate)
       SELECT passengerId, getdate()
       FROM Reservation_Passenger
       WHERE reservationId = @reservationId
 

PS Вам нужно будет быть осторожным, чтобы ваш код не изменил более одного бронирования на забронированное по одной и той же команде ОБНОВЛЕНИЯ. Потому что в этом случае триггер срабатывает только один раз, а все обновленные резервирования хранятся во ВСТАВЛЕННОМ наборе данных. Вам нужно будет использовать курсор для перебора всех этих бронирований, чтобы применить свою логику, или переключиться на этот более простой триггер, который создает билеты для всех пассажиров всех забронированных бронирований за один шаг:

 CREATE TRIGGER Generate_Ticket ON Reservation AFTER UPDATE
AS

INSERT INTO Ticket (passengerId, issuedDate)
       SELECT P.passengerId, getdate()
       FROM INSERTED as R
            INNER JOIN Reservation_Passenger as P on P.reservationId = R.reservationID
       WHERE R.Status = 'Booked'
 

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

Я рекомендую вам проверить не только то, что ВСТАВЛЕНО.Статус = «Забронировано», но также УДАЛЕНО.Статус < > «Забронировано», поэтому вы создаете билеты только тогда, когда поле статуса изменилось на Забронировано с чего-то другого.

Это было бы :

 CREATE TRIGGER Generate_Ticket ON Reservation AFTER UPDATE
AS

INSERT INTO Ticket (passengerId, issuedDate)
       SELECT P.passengerId, getdate()
       FROM INSERTED as I
            INNER JOIN DELETED as D on D.reservationId = I.reservationID
            INNER JOIN Reservation_Passenger as P on P.reservationId = I.reservationID
       WHERE I.Status = 'Booked' and coalesce(D.Status, '') <> 'Booked'
 

Комментарии:

1. В этом есть та же ошибка, на которую я ссылался в приведенных выше комментариях, и предполагается, что только 1 строка когда-либо будет обновлена.

2. @Stu Я добавлял дополнение с некоторыми проблемами его логики и способами их решения.