在多对多关系中进行复杂搜索的最快SQL表达式?

时间:2011-04-21 14:59:00

标签: sql performance many-to-many

在product_tag表中,列为

  

id,product_id,tag_id

如果我想搜索tag1或tag2或tag3的产品,直接方式是:

SELECT DISTINCT productId FROM product_tags WHERE tagId IN (2,4);

如果我想搜索tag1 AND tag2 AND tag3的产品,直接方式是:

SELECT productId FROM product_tag WHERE tag_id IN (tag1, tag2, tag3) GROUP BY productId HAVING COUNT(*) = 3

但问题是我是否想要搜索具有复杂标签关系的产品,例如:

  

产品(tag1 OR tag2 OR tag3)   AND(标签4或标签5或标签6)AND(标签   7 OR tag8 OR tag9)

具有最佳性能的SQL表达式是什么? (最好是优雅的)。

编辑:
最重要的性能提升是添加索引,如评论中推荐的Remus。

5 个答案:

答案 0 :(得分:1)

您实际上无法使用基于集合的语言(如SQL)直接执行此操作。

除非您没有(productId,tagId)的副本,否则您的简单“AND”版本也无效。

对于复杂的关系,有必要将查询分成几个子查询。首先打破所有“AND”条款:

WHERE tag_id IN (tag1, tag2, tag3)
WHERE tag_id IN (tag4, tag5, tag6)
WHERE tag_id IN (tag7, tag8, tag9)

然后对查询结果进行交叉。

如果这些子查询中的任何一个不是简单的OR列表,而是在更复杂的逻辑结构中包含AND,则需要进一步递归地细分这些子查询。

换句话说,您可以沿“AND”子句递归地分解逻辑树,然后在每个树级别执行查询结果的INTERSECT。

这样做可能比生成一个可以一次返回结果的巨大SQL快得多 - 因为每个简单的OR列表都可以利用你在tag_id上的索引。

答案 1 :(得分:1)

联盟所有3组。他们是3选,但他们真的很简单。

答案 2 :(得分:0)

我注意到Select values that meet different conditions on different rows?

怎么样

SELECT DISTINCT t1.productId FROM product_tags t1
JOIN product_tags t2 ON t1.productId=t2.productId AND t2.tagId IN (tag4,tag5,tag6)
JOIN product_tags t3 ON t1.productId=t3.productId AND t3.tagId IN (tag7, tag8, tag9)
AND t1.tagId IN (tag1,tag2,tag3)

如果以某种方式删除DISTINCT会更好。

答案 3 :(得分:0)

性能不会那么好但你可以做一个嵌套查询

SELECT 
ProductID FROM
Products 
WHERE tag_id IN (tag1, tag2, tag3)
AND ProductID IN (
SELECT 
ProductID FROM
Products 
WHERE tag_id IN (tag4, tag5, tag6)
)
AND ProductID IN (
SELECT 
ProductID FROM
Products 
WHERE tag_id IN (tag7, tag8, tag9)
)

答案 4 :(得分:0)

预先知道标签的数量吗?如果它不会随着时间的推移而增长,我会将tag_id更改为bitset。

WITH T AS 
 (SELECT product_id, bit_or((1<<tag_id)::bigint) tagset 
  FROM product_tag GROUP BY product_id) 
SELECT product_id 
WHERE (tagset & 7)>0 AND (tagset & 56)>0 AND (tagset & 448)>0;

我在这里使用过Postgres,其中&amp;被称为按位AND; bit_or是一个聚合函数(假设在product_tag表中没有允许重复,SUM在这里也可以正常工作)。掩码中的幻数只是两个幂的bit_ors。双冒号是Postgres演员。这里的一切都应该在其他地方略有不同的名称下提供。但PG也有不确定大小的位串,并且可以为大量标签实现与位串相同的逻辑。

顺便说一下,匹配所有标签的情况只是(tagset & mask)=mask

这实际上是为什么你的指数运作如此之快;它们可能被合并到这种类型的测试中。