【问题标题】:Need help with sql query to find things tagged with all specified tags需要有关 sql 查询的帮助以查找带有所有指定标签的内容
【发布时间】:2011-04-22 00:47:12
【问题描述】:

假设我有以下表格:

标签

id:整数
名称:字符串

帖子

id:整数
正文:文本

标记

id:整数
tag_id: 整数
post_id: 整数

我将如何编写一个查询来选择所有标记有以下所有标签(标签表的名称属性)的帖子:“奶酪”、“葡萄酒”、“巴黎”、“法国”、“城市” ", "风景", "艺术"

另见:Need help with sql query to find things with most specified tags(注意:相似,但不能重复!)

【问题讨论】:

  • 我建议你把你的两个(非常相似的)问题结合起来。
  • @adrianbanks 我考虑过,但它们是不同的,我想确保我能从每一种方式中得到一个好的答案,并对最好的回答者给予应有的评价。

标签: sql mysql ruby-on-rails tags tagging


【解决方案1】:

试试这个:

Select * From Posts p
   Where Not Exists
       (Select * From tags t
        Where name in 
           ('Cheese', 'Wine', 'Paris', 
             'Frace', 'City', 'Scenic', 'Art')
           And Not Exists
             (Select * From taggings
              Where tag_id = t.Tag_Id
                And post_Id = p.Post_Id))

解释:要求列出具有每一个一组指定标签的帖子的列表是等效 em> 询问那些在同一指定集中没有 no 标签的帖子,并且 not 与它相关联.即上面的sql。

【讨论】:

  • 您缺少TAGSPOSTS 之间的相关性
  • 可能是错的,但是再看看……最后一行呢?查询说“向我显示没有标签(在输入列表中)不在该帖子的标签表中的帖子”
  • 您需要在 TAGS 查询中引用 TAGGINGS 才能与 POST 相关...
  • 为什么?标签子查询中的 where 条件将其限制为那些不在指定 Post 的 taggings 表中的标签。 Select * From Tags ... 部分所做的只是初始化列表中七个标签的列表。
  • 再次,从语义上考虑“给我看那些帖子,列表中没有标签('Cheese', 'Wine', 'Paris', 'Frace', 'City', ' Scenic', 'Art') 未在该特定 Post 的 Taggings 表中表示”,在第二个子查询中需要建立与 Posts 表的相关性,并且它就在其中。
【解决方案2】:

使用 IN:

SELECT p.*
  FROM POSTS p
 WHERE p.id IN (SELECT tg.post_id
                  FROM TAGGINGS tg
                  JOIN TAGS t ON t.id = tg.tag_id
                 WHERE t.name IN ('Cheese','Wine','Paris','Frace','City','Scenic','Art')
              GROUP BY tg.post_id
                HAVING COUNT(DISTINCT t.name) = 7)

使用连接

SELECT p.*
  FROM POSTS p
  JOIN (SELECT tg.post_id
          FROM TAGGINGS tg
          JOIN TAGS t ON t.id = tg.tag_id
         WHERE t.name IN ('Cheese','Wine','Paris','Frace','City','Scenic','Art')
      GROUP BY tg.post_id
        HAVING COUNT(DISTINCT t.name) = 7) x ON x.post_id = p.id

使用 EXISTS

SELECT p.*
  FROM POSTS p
 WHERE EXISTS (SELECT NULL
                 FROM TAGGINGS tg
                 JOIN TAGS t ON t.id = tg.tag_id
                WHERE t.name IN ('Cheese','Wine','Paris','Frace','City','Scenic','Art')
                  AND tg.post_id = p.id
             GROUP BY tg.post_id
               HAVING COUNT(DISTINCT t.name) = 7)

说明

问题的关键在于COUNT(DISTINCT t.name) 需要匹配标签名称的数量,以确保所有这些标签都与帖子相关。如果没有 DISTINCT,其中一个名称的重复可能会返回 7 的计数,因此您会误报。

性能

大多数人会告诉您 JOIN 是最佳的,但 JOIN 也有在结果集中重复行的风险。 EXISTS 将是我的下一个选择 - 没有重复风险,并且通常执行速度更快,但检查解释计划最终会告诉您根据您的设置和数据什么是最好的。

【讨论】:

  • 这适用于所有 sql 数据库吗?特别是,这在 mysql 和 sqlite3 中都有效吗?
  • 哇,感谢您提供这么多方法……哪一种最健壮和最快?
  • @tybro0103:唯一的争论点是COUNT(DISTINCT ...),因为SQLite 不支持它。 See this link about a workaround
  • 我已经控制了标记系统上的输入,所以应该没有重复...这会改变对 sqlite3 的支持吗?
  • @tybro0103:很好,那么您可以放心地省略 DISTINCT,只使用 HAVING COUNT( t.name) = 7) 而不是我在答案中列出的内容。如果没问题,我会保持原样回答,以便其他人意识到可能出现误报。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-02-22
  • 2017-08-02
  • 1970-01-01
  • 1970-01-01
  • 2011-04-18
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多