有条件地删除MySQL数据库中的标签

时间:2013-05-28 15:31:41

标签: mysql database-design

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

cases
  id   INT
  description   TEXT

tags
  id   INT
  name   TEXT

case_tag
  id   INT
  case_id   INT
  tag_id    INT

个案可以包含任意数量的标签,标签可以属于任意数量的案例。

如果案例被删除,我想删除tags表中链接到它的任何标签,如果(并且仅当)它们没有链接到任何其他情况。我怎么做到这一点?

3 个答案:

答案 0 :(得分:2)

假设您正在使用InnoDB,请在casecase_tag之间创建对DELETE CASCADE的引用操作。在您删除了所需的case后,相应的case_tag行将自动删除,剩下的只是:

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

[SQL Fiddle]

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

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


附带建议:

  • TEXT(BLOB的一种形式)可能是tag.name的过度杀手 - 而是使用VARCHAR。
  • 您可能不需要surrogate key case_tag.id
  • 考虑在case_tag {tag_id, case_id}上添加索引(即在我的SQL小提琴中与PK相反)。让tag_id位于索引的前沿对于在tag_id上强制执行FK,选择给定标记和上述DELETE的情况非常重要。我们包括case_id以使索引更有可能cover查询,并且case_id仍将包含在索引中,因为clustered表中的二级索引包含副本PK。
  • 在命名表时使用单数(或至少不要混合复数和单数)。
  • 在PK字段名称中使用前缀。这有点争议,但根据我的经验,增加了ER模型,SQL和DAL的可读性。

答案 1 :(得分:1)

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

此查询应该为您提供所有包含附加到案例127

的标记的案例

QUERY#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,则可以删除案例ID和相应的标签

QUERY#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;

CAVEAT#1

如果@sharedtags_casecount&gt;您可以运行DELETE查询。它不应该删除任何东西。请备份数据并在登台服务器上进行测试。

CAVEAT#2

请确保case_tag case_id上有tag_idALTER 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;

EPILOGUE

经过测试和验证后,解决方案就是连续执行的两个查询

case1

试一试!!!

更新2013-05-29 11:30 EDT

我查看了来自the answer@BrankoSQL Fiddle提供的内容。答案与问题不符。为什么?在SQL Fiddle中,case1和case2共享三个标记(1,2,4)。你在问题中说过

  

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

由于case2case1共享标记,因此不应允许删除ON DELETE CASCADEcase1机械地执行删除,但无法调整以遵守其他人共享的标记规定。我的解决方案坚持它。

以下是my SQL Fiddle,演示了以下内容:

  • 将@caseid_to_delete设置为1禁止删除case3,因为它与tag5共享标签
  • 将@caseid_to_delete设置为2即可,因为其他人没有tag7case3
  • 将@caseid_to_delete设置为3禁止删除case1,因为它与tag6共享标签
  • 将@caseid_to_delete设置为4即可,因为其他人没有tag8ON DELETE CASCADE
  • 外键是可选的。

我很抱歉,但由于case_tag需要ON DELETE CASCADE,因此我可能会考虑tags vs case_tag

更新2013-05-29 11:36 EDT

我撤回了我的论点,因为问题是从标签中删除而不是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;

SQL FIDDLE