13

我读过一些关于single table inheritanceBill Karwin's答案,并认为这种做法将有利于我正在考虑设置:如何在单表继承上实施参照完整性?

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是但是,如果删除UserTeam,会发生什么情况?

即。如果删除UserUserPlaylist中的行将被删除,但PlaylistPlaylistVideo中的引用行将保留。我考虑将此强制执行为TRIGGER AFTER DELETE,但无法知道删除请求是否因为Playlist已删除或User已被删除而出现。

在这种情况下强制执行完整性的最佳方式是什么?

编辑(提供ERD)

enter image description here

+1

我不明白UserPlaylist如何成为播放列表的继承。它不应该是一个关系表吗? – Sebas 2012-07-18 01:46:38

+0

我不明白你的问题。 UserPlaylist与播放列表相关,只是该ID来自Playlist.id。以下是有关单表继承的更多问题 - http://stackoverflow.com/a/3383320/47278 – 2012-07-18 03:05:39

+0

您不希望用户被删除以取出播放列表和播放列表视频行的所有原因是因为他们也可以参考由其他用户播放列表或团队播放列表记录。 – WebChemist 2012-07-18 04:57:35

回答

3

在我看来,问题是,你的UserTeam表是应该有一个超表(如Party),而不是播放表的人。

正如您已经指出的那样,在播放列表上执行“表格继承”时,在尝试确定要删除的内容时会带来处罚。将继承移至用户/团队级别时,所有这些问题都会消失。您可以看到this answer for more detail about supertyping/subtyping

我很抱歉不提供代码,因为我不太了解MySQL语法。

基本概念是超类型表允许您实现数据库类型的多态性。当你正在使用的表格需要链接到任意一个的一组子类型时,您只需将FK指向超类型,而这会自动获得所需的“一次只有其中一个”业务约束。超类型与每个子类型表具有“一对一或一”关系,并且每个子类型表在其PK中使用与超类型表中的PK相同的值。

在您的数据库中,通过只有一个Playlist表的FK为Party (PartyID),您可以轻松地在数据库级别实施您的业务规则,而无需触发器。

+0

这是一个很好的观点和一个好主意。然而,在这个系统中,一个团队与一个用户有很大的不同。例如。用户可以登录,团队不能。这是生产中的现有系统(而播放列表是新的)。如果我是从头开始构建它,这可能是要走的路。但是我需要考虑一下,如果改变这个设置是值得的(即团队和用户目前有重叠的ID)。但谢谢你的建议。 – 2012-07-25 01:19:52

+0

这就是子类型表的用途:它们的不同之处。更改中的真正问题是更新其中一个组的ID,并删除和重新创建约束。但我认为这是值得的。 :) – ErikE 2012-07-25 05:28:23

+0

感谢您选择我的答案!是什么让你决定这是要走的路? – ErikE 2012-07-31 00:41:38

4

确定我看到你想要的这里......你想要做什么之后是行是运行像

DELETE FROM playlist 
WHERE  id 
NOT IN  (
    SELECT id 
    FROM UserPlayList 
    UNION 
    SELECT id 
    FROM TeamPlayList 
) 

查询从用户或团队中删除

11

你可以做的是在你的UsersTeam表上执行触发器exec UTE每当行也会从删除或者:

用户表:

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操作将使用即将被删除的idPlaylists表上自动执行(通过内部连接)。

我测试过这个,它工作的很好。

+0

辉煌的解决方案。没有测试过myslef,但看起来很稳固。+1 – techtheatre 2012-07-19 01:54:18

+0

好主意赞恩。出于某种原因,我没有考虑将触发器放在User或Team表上 - 只有UserPlaylist或TeamPlaylist表。 – 2012-07-19 22:44:16

+0

我不认为这是解决问题的最佳方法... – ErikE 2012-07-24 23:50:02

1

Zane Bien的回答很明显&非常棒。但是我有一个不使用触发器的想法,因为触发器有很多问题。

您是否在使用任何编程语言?如果是的话,

使用单一transaction,使您的数据库auto commit false

写在播放列表和PlaylistVideo引用的行删除查询。手动您必须首先使用该引用id(with where条件)编写该查询并运行它。

现在为您的主要任务准备另一个查询,即删除用户,并且UserPlaylist中的行将自动删除(由于CASCADE DELETE选项)。现在运行你的第二个查询和commit

最后让您的交易auto commit true

它工作成功,希望它会有所帮助。