我的数据库中有以下三个表:
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,因为它与case3tag5并且tag7case3,因为它与case1tag6并且tag8ON DELETE CASCADE没有必要。我很抱歉,但也许这个措辞让我想到了case_tagvstags因为 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;