通过要求所有许多满足条件来过滤一对多查询

时间:2009-01-26 22:09:39

标签: sql mysql join one-to-many

想象一下下面的表格:

创建表格框(id int,name text,...);

创建表thinginboxes(id int,box_id int,thing enum('apple,'banana','orange');

表格如下:

Boxes:
id | name
1  | orangesOnly
2  | orangesOnly2
3  | orangesBananas
4  | misc

thingsinboxes:
id | box_id | thing
1  |  1     | orange
2  |  1     | orange
3  |  2     | orange
4  |  3     | orange
5  |  3     | banana
6  |  4     | orange
7  |  4     | apple
8  |  4     | banana

如何选择至少包含一个橙色且不包含任何橙色的框?

假设我有数十万个盒子,可能有一百万个盒子里的东西,这个规模如何?

如果可能的话,我想将这一切保留在SQL中,而不是使用脚本对结果集进行后处理。

我正在使用postgres和mysql,所以子查询可能不好,因为mysql不优化子查询(无论如何都是版本6)。

2 个答案:

答案 0 :(得分:5)

SELECT b.*
FROM boxes b JOIN thingsinboxes t ON (b.id = t.box_id)
GROUP BY b.id
HAVING COUNT(DISTINCT t.thing) = 1 AND SUM(t.thing = 'orange') > 0;

这是另一个不使用GROUP BY的解决方案:

SELECT DISTINCT b.*
FROM boxes b
  JOIN thingsinboxes t1 
    ON (b.id = t1.box_id AND t1.thing = 'orange')
  LEFT OUTER JOIN thingsinboxes t2 
    ON (b.id = t2.box_id AND t2.thing != 'orange')
WHERE t2.box_id IS NULL;

与往常一样,在您对查询的可扩展性或性能做出结论之前,您必须尝试使用​​实际数据集,并衡量效果。

答案 1 :(得分:2)

我认为Bill Karwin的查询很好,但是如果相对较小比例的盒子包含橙子,你应该能够通过使用thing字段上的索引加快速度:

SELECT b.*
FROM boxes b JOIN thingsinboxes t1 ON (b.id = t1.box_id)
WHERE t1.thing = 'orange'
AND NOT EXISTS (
    SELECT 1
    FROM thingsinboxes t2
    WHERE t2.box_id = b.id
    AND t2.thing <> 'orange'
)
GROUP BY t1.box_id

WHERE NOT EXISTS子查询只会在每个橙色的东西上运行一次,所以只要橙子不多,它就不会太贵。