Являются ли триггеры базы данных безопасными для ограничений целостности таблицы?
Я предложил использовать триггер, чтобы проверить ограничение целостности кросс-таблицы, отвечая на этот вопрос. В комментариях было высказано мнение, что это может вызвать проблемы:
Триггеры для проверки перекрестных строк редко работают в большинстве баз данных... потому что они не могут читать незафиксированные строки из других транзакций.
Я не нашел ни одного источника, поддерживающего заявку. Официально документация ничего не говорит. Другие проблемы, которые я нашел, рассматриваются здесь на SO - это в основном критикует потенциальную скрытую сложность, поскольку триггеры не видны с первого взгляда. Ставка даже на самый высокий рейтинг позволяет использовать их для проблем с целостностью.
Итак, мой вопрос: Являются ли триггеры базы данных безопасными для ограничений целостности кросс-таблицы? В частности, будет ли решение ниже работать?
Подвести исходный вопрос. У нас есть таблицы
- Player - PlayerID, PlayerName
- Ставка - BetID, BetName
- play_in - BetID, PlayerID
Ограничение на то, чтобы комбинация BetName и PlayerID sholud была уникальной. Определение предлагаемого триггера:
CREATE TRIGGER check_bet_name BEFORE INSERT ON plays_in
FOR EACH ROW BEGIN
DECLARE bet_exists INT DEFAULT 0;
DECLARE msg VARCHAR(255);
SELECT 1 INTO bet_exists
FROM Bet AS b1
WHERE b1.BetID = NEW.BetID
AND EXISTS (SELECT *
FROM plays_in AS p JOIN Bet AS b2 USING (BetID)
WHERE p.PlayerID = NEW.PlayerID AND b2.BetName = b1.BetName
)
LIMIT 1;
IF bet_exists THEN
SET msg = "Bet name already exists...";
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = msg;
END IF;
END//
Ответы
Ответ 1
Ответ триггеры не безопасны.
Оказывается, что триггер действительно не видит неоправданных изменений, сделанных в других транзакциях, и проходит без ошибок. Это можно продемонстрировать следующим образом:
Сделка 1:
START TRANSACTION;
INSERT INTO plays_in (BetID, PlayerID) VALUES (1,1); -- query A
Сделка 2:
START TRANSACTION;
INSERT INTO plays_in (BetID, PlayerID) VALUES (1,2); -- query B; in conflict with A, but passses
Обе транзакции:
COMMIT;
Теперь plays_in
будет содержать обе вставленные записи, хотя если бы A и B выполнялись в одной транзакции, триггер выдавал бы ошибку.
Все исходные примеры могут быть получены здесь
Ответ 2
Вероятно, это зависит от того, какая база данных и насколько хорошо вы пишете логику.
Ранние версии infomodeler/visiomodeler поддерживали некоторые довольно тайные и сложные формы ссылочной целостности и предоставляли код для их реализации в нескольких базах данных. Ранние версии сервера Sybase/sql не поддерживали декларативную ссылочную целостность, поэтому вся логика была реализована в триггерах - успешно.
Я бы не ошибся, если бы одна реализация контр-примера была авторитетной.