Как обеспечить соблюдение ссылочной целостности при однократном наследовании?
Я читал некоторые из ответов Билла Карвина о одиночном наследовании таблицы и думаю, что этот подход будет хорошо для настройки, которую я рассматриваю:
Playlist
--------
id AUTO_INCREMENT
title
TeamPlaylist
------------
id REFERENCES Playlist.id
teamId REFERENCES Team.id
UserPlaylist
------------
id REFERENCES Playlist.id
userId REFERENCES User.id
PlaylistVideo
-------------
id
playlistId REFERENCES Playlist.id
videoId REFERENCES Video.id
Все параметры CASCADE
установлены на DELETE
, которые будут корректно работать, когда удаляется Playlist
, однако, что происходит, если удаляется User
или Team
?
т. Если a User
удаляется, строки в UserPlaylist
будут удалены, но ссылки в строках в Playlist
и PlaylistVideo
останутся. Я думал об обеспечении этого как TRIGGER AFTER DELETE
, но не существует способа узнать, вызвал ли запрос удаления, потому что был удален Playlist
или удалено User
.
Каков наилучший способ обеспечения целостности в этой ситуации?
Изменить (предоставлено ERD)
![enter image description here]()
Ответы
Ответ 1
На мой взгляд, проблема в том, что ваши таблицы User
и Team
- это те, которые должны иметь таблицу супертипа (например, Party
), а не таблицы списка воспроизведения.
Как вы указали, выполнение "наследования таблиц" в плейлистах происходит со штрафами при попытке выяснить, что удалить. Все эти проблемы уходят, когда вы перемещаете наследование до уровня пользователя/команды.
Вы можете увидеть этот ответ для более подробной информации о супертипировании/подтипинге.
Извините, что не поставлял код, так как я не знаю синтаксиса MySQL наизусть.
Основная концепция заключается в том, что таблица супертипов позволяет реализовать тип полиморфизма базы данных. Когда таблица, с которой вы работаете, должна ссылаться на какую-либо группу подтипов, вы просто делаете FK вместо этого супертипом, и это автоматически дает вам желаемое "только одно из них за раз" деловое ограничение, Тип супер имеет отношение "один к нулю" или "один к одному" с каждой из таблиц подтипа, и каждая таблица подтипов использует то же значение в PK, что и PK из таблицы супертипа.
В вашей базе данных, имея только одну таблицу Playlist
с FK до Party (PartyID)
, вы легко выполнили свое бизнес-правило на уровне базы данных без триггеров.
Ответ 2
Что вы можете сделать, это реализовать триггеры в таблицах Users
и Team
, которые выполняются, когда строки удаляются из:
Таблица пользователей:
DELIMITER $$
CREATE TRIGGER user_playlist_delete
BEFORE DELETE ON User FOR EACH ROW
BEGIN
DELETE a FROM Playlist a
INNER JOIN UserPlaylist b ON a.id = b.id AND b.userId = OLD.id;
END$$
DELIMITER ;
Таблица команд:
DELIMITER $$
CREATE TRIGGER team_playlist_delete
BEFORE DELETE ON Team FOR EACH ROW
BEGIN
DELETE a FROM Playlist a
INNER JOIN TeamPlaylist b ON a.id = b.id AND b.teamId = OLD.id;
END$$
DELIMITER ;
Что эти триггеры будут делать, каждый раз, когда запись удаляется из одной из этих таблиц, операция DELETE
будет автоматически выполняться в таблице Playlists
с помощью id
, которая должна быть удалена (через внутреннее соединение).
Я тестировал это, и он отлично работает.
Ответ 3
OK Я вижу, что вы хотите здесь... то, что вы хотите сделать, это запустить запрос типа
DELETE FROM playlist
WHERE id
NOT IN (
SELECT id
FROM UserPlayList
UNION
SELECT id
FROM TeamPlayList
)
после того, как строка удалена из пользователей или команд
Ответ 4
Ответ Zane Bien довольно очевиден и превосходный. Но у меня есть идея сделать это без использования триггера, потому что триггер имеет много проблем.
Используете ли вы язык программирования? Если да, то
Используйте один transaction
и создайте свою базу данных auto commit false
напишите запрос удаления для ссылочных строк в списке воспроизведения и в списке воспроизведения. Вручную вы должны сначала написать этот запрос, используя этот идентификатор ссылки (с условием) и запустите его.
Теперь подготовьте другой запрос для своей основной задачи, то есть удалите пользователя, а строки в UserPlaylist будут удалены автоматически (из-за опции CASCADE DELETE
). Теперь запустите свой второй запрос и commit
.
Наконец, сделайте свою транзакцию auto commit true
.
Он работает успешно, надеюсь, что это будет полезно.