2

我的数据库中有以下三个表:

cases
  id   INT
  description   TEXT

tags
  id   INT
  name   TEXT

case_tag
  id   INT
  case_id   INT
  tag_id    INT

一个单独的案例可以有任意数量的标签,一个标签可以属于任意数量的案例。

如果一个案例被删除,我想删除tags表中链接到它的任何标签,如果(且仅当)它们没有链接到任何其他案例。我将如何实现这一目标?

4

3 回答 3

2

假设您使用的是 InnoDB,请在case和之间创建一个引用操作 ON DELETE CASCADE case_tag。然后在您删除了所需的 之后case,相应的case_tag行将被自动删除,剩下的就是简单地:

DELETE FROM tag WHERE tag_id NOT IN (SELECT tag_id FROM case_tag);

[SQL 小提琴]

如果需要,您甚至可以从触发器中执行此操作,但此方法更适合某种后台“清理”过程,因为它会清除所有多余的标签,而不仅仅是与最后一个相关的标签 deleted case

另一种方法是预先选择与要删除的案例相关的标签,然后尝试仅删除它们


附带建议:

  • TEXT(BLOB 的一种形式)对于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 的副本。
  • 命名表格时使用单数(或至少不要混合复数和单数)。
  • 在 PK 字段名称中使用前缀。这有点争议,但根据我的经验,增加了 ER 模型、SQL 和DAL的可读性。
于 2013-05-28T22:38:12.897 回答
1

对于此示例,假设您要删除 id 为 127 的案例。

此查询应为您提供所有带有附加到案例 127 的标签的案例

查询 #1

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 即可

查询 #2

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;

警告 #1

如果 @sharedtags_casecount > 0,您可以运行 DELETE 查询。它不应该删除任何内容。请备份数据并在登台服务器上进行测试。

注意事项 #2

请确保case_tag有一个复合索引case_idtag_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;

试试看 !!!

更新 2013-05-29 11:30 EDT

我查看了@Branko和提供的SQL Fiddle的答案。答案与问题不符。为什么?在 SQL Fiddle 中,case1 和 case2 共享三个标签 (1,2,4)。你在问题中说

如果一个案例被删除,我想删除标签表中链接到它的任何标签,如果(且仅当)它们没有链接到任何其他案例。

由于case1case2共享标签,case1不应该被允许被删除。机械地执行删除,ON DELETE CASCADE但无法调整以遵守其他人共享的标签的规定。我的解决方案坚持它。

这是我的 SQL Fiddle,它演示了以下内容:

  • 将@caseid_to_delete 设置为 1 禁止删除case1,因为它与case3
  • 将 @caseid_to_delete 设置为 2 是可以的,因为没有其他人拥有tag5并且tag7
  • 将@caseid_to_delete 设置为 3 禁止删除case3,因为它与case1
  • 将 @caseid_to_delete 设置为 4 是可以的,因为没有其他人拥有tag6并且tag8
  • 外键是可选的。ON DELETE CASCADE没有必要。

我很抱歉,但也许这个措辞让我想到了case_tagvstags因为 ON DELETE CASCADE 是case_tag.

更新 2013-05-29 11:36 EDT

我撤回了我的论点,因为问题是从标签中删除而不是 case_tag。+1 布兰科的回答。

于 2013-05-28T17:02:33.530 回答
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;

SQL 小提琴

于 2013-05-28T17:35:26.893 回答