2014-12-05 90 views
1

,我有以下数据:简化和/或优化与INTERSECT SQL查询或HAVING

tag_id | taggable_id 
--------+------------- 
     1 |   1 
     2 |   1 
     3 |   1 
     4 |   1 
     1 |   2 
     2 |   2 
     5 |   2 
     6 |   2 
     7 |   3 
     8 |   3 
     9 |   3 
    10 |   3 

而且我希望把所有这一切都是在一组tag_ids的,而在另一个组和另一个taggable_ids .. (最多4组)。
例如:

  • 如果我搜索所有与TAG_ID 1或7 AND TAG_ID的taggable_ids 4应该返回仅有1
  • 如果我搜索所有与TAG_ID 1 taggable_ids AND TAG_ID 6和TAG_ID 2或8应返回仅有2
  • 如果我搜索所有TAG_ID 8 taggable_ids和TAG_ID 5不应该返回IDS

在一个总的方式(对第二个例子),查询如下:

SELECT taggable_id FROM taggings WHERE tag_id in (1) 
INTERSECT 
SELECT taggable_id FROM taggings WHERE tag_id in (6) 
INTERSECT 
SELECT taggable_id FROM taggings WHERE tag_id in (2,8) 

我想简化它,它看起来像:

SELECT taggable_id FROM taggings WHERE tag_id in (1,2,6,8) 
GROUP BY taggable_id HAVING COUNT(*)=3 

,但我不知道是否能以更简单的方式来完成。有什么想法吗?

+0

你的第二个查询看起来很简单。 – 2014-12-05 19:17:56

+0

'(tag_id,taggable_id)'被定义为唯一的,对吧? – 2014-12-05 21:49:47

+0

您的查询与您的描述不符。而你的第二个是*不等于第一个。提出一个一致的例子将是谨慎的。 – 2014-12-05 22:23:21

回答

2

这可以铸成的relational division的情况下。我们已经按照此相关的问题组装的查询技术的阿森纳:

根据数据分布等因素的影响,这可能是最快的:

SELECT DISTINCT taggable_id 
FROM taggings t1 
JOIN taggings t2 USING (taggable_id) 
JOIN taggings t3 USING (taggable_id) 
WHERE t1.tag_id = 1 
AND t2.tag_id = 6 
AND t3.tag_id IN (2, 8); 

假设独特(tag_id, taggable_id),示例中实际不需要DISTINCT。但是对于其他(列表)谓词可能是必需的。

SQL Fiddle(建立在@ Clodoaldo的,谢谢)。

1

您的第二个查询不等同于第一个查询。在having条款改为:

HAVING SUM(CASE WHEN tag_id = 1 THEN 1 ELSE 0 END) > 0 AND 
     SUM(CASE WHEN tag_id = 6 THEN 1 ELSE 0 END) > 0 AND 
     SUM(CASE WHEN tag_id IN (2, 8) THEN 1 ELSE 0 END) > 0; 

至于性能,测试了查询,看看哪个效果更好。 having方法的优点是您可以添加越来越复杂的条件,而不会对性能产生重大影响。如果元组(8, 2)插入

2

SQL Fiddle

你的第二个查询失败。这里是一个解决方案,虽然我不知道,如果简单那么intersect一个:

select taggable_id 
from taggings 
where tag_id in (1,2,6,8) 
group by taggable_id 
having 
    array_agg(tag_id) @> array[1,2] 
    and 
    array_agg(tag_id) && array[6,8]