我的数据库中有以下三个表:
cases
id INT
description TEXT
tags
id INT
name TEXT
case_tag
id INT
case_id INT
tag_id INT
个案可以包含任意数量的标签,标签可以属于任意数量的案例。
如果案例被删除,我想删除tags
表中链接到它的任何标签,如果(并且仅当)它们没有链接到任何其他情况。我怎么做到这一点?
答案 0 :(得分:2)
假设您正在使用InnoDB,请在case
和case_tag
之间创建对DELETE CASCADE的引用操作。在您删除了所需的case
后,相应的case_tag
行将自动删除,剩下的只是:
DELETE FROM tag WHERE tag_id NOT IN (SELECT tag_id FROM case_tag);
如果需要,您甚至可以从触发器执行此操作,但此方法更适合某种背景“清理”过程,因为它将清除所有多余的标记,而不仅仅是与最后一个相关的标记。已删除case
。
另一种方法是预先选择与要删除的案例相关联的标签,然后尝试仅删除 。
附带建议:
tag.name
的过度杀手 - 而是使用VARCHAR。case_tag.id
。case_tag {tag_id, case_id}
上添加索引(即在我的SQL小提琴中与PK相反)。让tag_id
位于索引的前沿对于在tag_id
上强制执行FK,选择给定标记和上述DELETE的情况非常重要。我们包括case_id
以使索引更有可能cover查询,并且case_id
仍将包含在索引中,因为clustered表中的二级索引包含副本PK。答案 1 :(得分:1)
对于此示例,假设您要删除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,则可以删除案例ID和相应的标签
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&gt;您可以运行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;
经过测试和验证后,解决方案就是连续执行的两个查询
case1
我查看了来自the answer的@Branko和SQL Fiddle提供的内容。答案与问题不符。为什么?在SQL Fiddle中,case1和case2共享三个标记(1,2,4)。你在问题中说过
如果案例被删除,我想删除标签表中链接到它的任何标签,如果(并且仅当)它们没有链接到任何其他案例。
由于case2
和case1
共享标记,因此不应允许删除ON DELETE CASCADE
。 case1
机械地执行删除,但无法调整以遵守其他人共享的标记规定。我的解决方案坚持它。
以下是my SQL Fiddle,演示了以下内容:
case3
,因为它与tag5
共享标签tag7
和case3
case1
,因为它与tag6
共享标签tag8
和ON DELETE CASCADE
我很抱歉,但由于case_tag
需要ON DELETE CASCADE,因此我可能会考虑tags
vs case_tag
。
我撤回了我的论点,因为问题是从标签中删除而不是case_tag。 Branko回答+1。
答案 2 :(得分:0)
试试这个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;