Триггер MySQL Before Delete, чтобы избежать удаления нескольких строк - PullRequest
0 голосов
/ 02 ноября 2018

Я пытаюсь избежать удаления более 1 строки за раз в MySQL с помощью триггера BEFORE DELETE.

Таблица с примерами и триггер приведены ниже.

Таблица испытаний:

DROP TABLE IF EXISTS `test`;
CREATE TABLE `test` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `a` int(11) NOT NULL,
 `b` int(11) NOT NULL,
 PRIMARY KEY (`id`));


INSERT INTO `test` (`id`, `a`, `b`)
VALUES (1, 1, 2);

INSERT INTO `test` (`id`, `a`, `b`)
VALUES (2, 3, 4);

Trigger:

DELIMITER //
DROP TRIGGER IF EXISTS prevent_multiple_deletion;
CREATE TRIGGER prevent_multiple_deletion
BEFORE DELETE ON test
FOR EACH STATEMENT 
BEGIN

  IF(ROW_COUNT()>=2) THEN  
    SIGNAL SQLSTATE '45000' 
    SET MESSAGE_TEXT = 'Cannot delete more than one order per time!';
  END IF;

END //

DELIMITER ;

Это все еще позволяет удалять несколько строк. Даже если я изменю ПЧ на> = 1, операция все равно будет разрешена.

Моя идея - избегать таких операций, как:

DELETE FROM `test` WHERE `id`< 5;

Вы можете мне помочь? Я знаю, что текущая версия MySQL не допускает триггеров FOR EACH STATEMENT.

Спасибо!

1 Ответ

0 голосов
/ 02 ноября 2018

Во-первых, получение некоторых синтаксических ошибок из нашей первоначальной попытки:

  • Вместо FOR EACH STATEMENT должно быть FOR EACH ROW.
  • Поскольку вы уже определили разделитель для //; вам нужно использовать // (вместо ;) в операторе DROP TRIGGER IF EXISTS ...
  • Row_Count() будет иметь значение 0 в Before Delete Trigger, поскольку еще не было обновлено ни одной строки. Таким образом, этот подход не будет работать.

Теперь уловка заключается в том, чтобы использовать Доступные (и постоянные) на уровне сеанса определяемые пользователем переменные . Мы можем определить переменную, скажем @rows_being_deleted, и позже проверить, уже определена она или нет.

For Each Row выполняет одинаковый набор операторов для каждой удаляемой строки . Итак, мы просто проверим, существует ли переменная сеанса или нет. Если это не так, мы можем определить это. Таким образом, в основном, для первой строки (удаляемой) она будет определена, которая будет сохраняться в течение всего сеанса.

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

Обратите внимание , что существует вероятность того, что в одном и том же сеансе могут быть запущены несколько операторов удаления. Поэтому, прежде чем вызвать исключение, нам нужно установить значение @rows_being_deleted обратно в null.

Будет работать следующее:

DELIMITER //
DROP TRIGGER IF EXISTS prevent_multiple_deletion //
CREATE TRIGGER prevent_multiple_deletion
  BEFORE DELETE ON `test`
  FOR EACH ROW  
    BEGIN

       -- check if the variable is already defined or not
       IF( @rows_being_deleted IS NULL ) THEN 
         SET @rows_being_deleted = 1; -- set its value

       ELSE -- it already exists and we are in next "row"

         -- just for testing to check the row count
         -- SET @rows_being_deleted = @rows_being_deleted + 1;

         -- We have to reset it to null, as within same session
         -- another delete statement may be triggered.
            SET @rows_being_deleted = NULL;

         -- throw exception
         SIGNAL SQLSTATE '45000' 
         SET MESSAGE_TEXT = 'Cannot delete more than one order per time!';
       END IF;

  END //

DELIMITER ;

ДБ Fiddle Demo 1 : Попытка удалить больше, чем строку.

DELETE FROM `test` WHERE `id`< 5;

Результат:

Ошибка запроса: ошибка: ER_SIGNAL_EXCEPTION: невозможно удалить более одного заказ за раз!


ДБ Fiddle Demo 2 : Попытка удалить только одну строку

Запрос № 1

DELETE FROM `test` WHERE `id` = 1;

Удаление успешно произошло. Мы можем проверить оставшиеся строки, используя Select.

Запрос № 2

SELECT * FROM `test`;

| id  | a   | b   |
| --- | --- | --- |
| 2   | 3   | 4   |
...