4

假设我有这张桌子:

item_id tag_id
------- ------
1       1
1       2
2       2
2       3

正如您可能想象的那样,这是一张表,其中我引用了一些属于它们的项目和标签。一个项目可以有多个标签,并且可以为多个项目选择一个标签。

假设我还有一个特定的标签集合(例如 tag_id = 50、73 和 119)和一个带有 id 的“项目”表(由 引用item_id)。

是否有一个 有效的查询给我:

  1. 带有这些标签的项目数
  2. 物品本身?

我试过的

SELECT COUNT(*) FROM
(
SELECT COUNT(*) AS c FROM items_tags it JOIN items i ON i.id = it.item_id
WHERE (tag_id=7 OR tag_id=95 OR tag_id=150) AND `status`='active'
GROUP BY item_id
) t1 WHERE c=3 <-- c= number of tags

我可以同时获得这两个结果,但查询效率非常低(似乎)。经过 EXPLAIN 检查后,我想摆脱 OR 给出的“范围”。

细化我的问题:问题是我得到了一个写得很糟糕的 PHP 框架,它通过各种标签 ID 迭代了 900 多次。假设您有一个或多个固定 ID(选定的标签),它遍历所有 900 多个标签,以查找具有给定标签的共同项目的出现次数加上迭代的一个(这是一个细化搜索,仅显示具有所有给定标签加一的元素)。

给定的代码是这样工作的:我选择一个或多个标签,它们的 ID 进入查询字符串。假设我选择了标签 54 和 77。代码必须找到同时具有标签 54 和 77 的项目的每个项目 ID 并一一列出:我们获得“带有选定标签的项目”列表。

然后,它提供了优化搜索的选项,奇怪的部分出现了:PHP 代码循环遍历所有 900 多个标签,并且对于每次迭代,它需要一个标签,并计算有多少项目具有所有标签 54、77和迭代中的那个。如果计数 > 0,它会显示带有计数编号的标签名称,过滤掉其项目与所选标签没有任何链接的每个标签。

以不那么“密集”的方式实现相同的结果会很好。

4

1 回答 1

3

要获取与所有标签匹配的项目 ID 列表,您可以使用以下查询:

SELECT items.id
FROM items
JOIN items_tags ON items.id = items_tags.item_id
WHERE (items_tags.tag_id IN (7,95,150))
  AND (items.status = 'active')
GROUP BY items.id
HAVING COUNT(DISTINCT items_tags.tag_id) = 3

请注意,如果您确定同一项目永远不会有重复的标签,则可以替换COUNT(DISTINCT items_tags.tag_id)COUNT(*)效率。

要获取这些项目的计数,请将其包装在 COUNT 查询中:

SELECT COUNT(*)
FROM (
  SELECT items.id
  ...
) t

要获取项目列表,请将其包装在此 SELECT 查询中:

SELECT *
FROM items
WHERE id IN (
  SELECT items.id ...
)

更新

要在与原始列表结合时获取每个剩余标签的项目计数,您可以执行以下操作:

SELECT tag_id, COUNT(DISTINCT item_id)
FROM items_tags
WHERE item_id IN (
  SELECT items.id
  ...
)
  AND tag_id NOT IN (7,95,150)
GROUP BY tag_id
于 2013-03-18T18:42:10.533 回答