我的数据库中有以下三个表:
cases
id INT
description TEXT
tags
id INT
name TEXT
case_tag
id INT
case_id INT
tag_id INT
一个单独的案例可以有任意数量的标签,一个标签可以属于任意数量的案例。
如果一个案例被删除,我想删除tags
表中链接到它的任何标签,如果(且仅当)它们没有链接到任何其他案例。我将如何实现这一目标?
我的数据库中有以下三个表:
cases
id INT
description TEXT
tags
id INT
name TEXT
case_tag
id INT
case_id INT
tag_id INT
一个单独的案例可以有任意数量的标签,一个标签可以属于任意数量的案例。
如果一个案例被删除,我想删除tags
表中链接到它的任何标签,如果(且仅当)它们没有链接到任何其他案例。我将如何实现这一目标?
假设您使用的是 InnoDB,请在case
和之间创建一个引用操作 ON DELETE CASCADE case_tag
。然后在您删除了所需的 之后case
,相应的case_tag
行将被自动删除,剩下的就是简单地:
DELETE FROM tag WHERE tag_id NOT IN (SELECT tag_id FROM case_tag);
如果需要,您甚至可以从触发器中执行此操作,但此方法更适合某种后台“清理”过程,因为它会清除所有多余的标签,而不仅仅是与最后一个相关的标签 deleted case
。
另一种方法是预先选择与要删除的案例相关的标签,然后尝试仅删除它们。
附带建议:
tag.name
使用 VARCHAR 可能是一种过度杀伤力。case_tag.id
。case_tag {tag_id, case_id}
(即与我的 SQL Fiddle 中的 PK 相反)。位于索引tag_id
的前沿对于强制执行 FK on tag_id
、选择给定标签的案例和上面的 DELETE 很重要。我们包括case_id
使索引更有可能覆盖查询,并且case_id
无论如何都会包含在索引中,因为聚集表中的二级索引包含 PK 的副本。对于此示例,假设您要删除 id 为 127 的案例。
此查询应为您提供所有带有附加到案例 127 的标签的案例
SET @caseid_to_delete = 127;
SELECT COUNT(1) INTO @sharedtags_casecount
FROM (SELECT BB.case_id,B.tag_id
FROM case_tag AA INNER JOIN case_tag BB
ON AA.case_id=@caseid_to_delete
AND AA.tag_id=BB.tag_id
AND AA.case_id<>BB.case_id) A;
如果@sharedtags_casecount 为0,则删除case id 和对应的tag 即可
DELETE C.*,T.* FROM cases C,case_tag CT,tags T
WHERE C.id=@caseid_to_delete
AND CT.case_id=@caseid_to_delete
AND CT.tag_id=T.id
AND @sharedtags_casecount=0;
如果 @sharedtags_casecount > 0,您可以运行 DELETE 查询。它不应该删除任何内容。请备份数据并在登台服务器上进行测试。
请确保case_tag
有一个复合索引case_id
,tag_id
ALTER TABLE case_tag ADD INDEX case_tag_ndx (case_id,tag_id);
测试和验证后,解决方案就是连续执行两个查询
SET @caseid_to_delete = 127;
SELECT COUNT(1) INTO @sharedtags_casecount
FROM (SELECT BB.case_id,BB.tag_id
FROM case_tag AA INNER JOIN case_tag BB
ON AA.case_id=@caseid_to_delete
AND AA.tag_id=BB.tag_id
AND AA.case_id<>BB.case_id) A;
DELETE C.*,CT.* FROM cases C,case_tag CT,tags T
WHERE C.id=@caseid_to_delete
AND CT.case_id=@caseid_to_delete
AND CT.tag_id=T.id
AND @sharedtags_casecount=0;
我查看了@Branko和提供的SQL Fiddle的答案。答案与问题不符。为什么?在 SQL Fiddle 中,case1 和 case2 共享三个标签 (1,2,4)。你在问题中说
如果一个案例被删除,我想删除标签表中链接到它的任何标签,如果(且仅当)它们没有链接到任何其他案例。
由于case1
和case2
共享标签,case1
不应该被允许被删除。机械地执行删除,ON DELETE CASCADE
但无法调整以遵守其他人共享的标签的规定。我的解决方案坚持它。
这是我的 SQL Fiddle,它演示了以下内容:
case1
,因为它与case3
tag5
并且tag7
case3
,因为它与case1
tag6
并且tag8
ON DELETE CASCADE
没有必要。我很抱歉,但也许这个措辞让我想到了case_tag
vstags
因为 ON DELETE CASCADE 是case_tag
.
我撤回了我的论点,因为问题是从标签中删除而不是 case_tag。+1 布兰科的回答。
试试这个 sql
delete t.*
from tags t
inner join case_tag ct
on t.id=ct.tag_id
inner join cases c
on ct.case_id=c.id
and t.id not in(select distinct tag_id from case_tag where case_id<>1)
and ct.case_id=1;