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

Posted

技术标签:

【中文标题】通过要求所有多个满足条件来过滤一对多查询【英文标题】:Filter a one-to-many query by requiring all of many meet criteria 【发布时间】:2009-01-26 22:09:39 【问题描述】:

想象以下表格:

创建表格框(id int,名称文本,...);

create table thingsinboxes(id int, box_id int, thing enum('apple,'banana','orange');

表格看起来像:

盒子: 编号 |姓名 1 |只有橘子 2 |只有橘子2 3 |橙子香蕉 4 |杂项 收件箱: 编号 | box_id |事物 1 | 1 |橙 2 | 1 |橙 3 | 2 |橙 4 | 3 |橙 5 | 3 |香蕉 6 | 4 |橙 7 | 4 |苹果 8 | 4 |香蕉

如何选择至少包含一个橙色且没有非橙色的框?

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

如果可能,我希望将这一切都保存在 SQL 中,而不是使用脚本对结果集进行后处理。

我同时使用 postgres 和 mysql,因此子查询可能很糟糕,因为 mysql 不优化子查询(无论如何都是 6 之前的版本)。

【问题讨论】:

我不确定你到底想要完成什么,但你的表结构很奇怪! 是什么让你觉得 MySQL 没有优化子查询? 表格结构对我来说看起来很正常。 【参考方案1】:
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;

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

【讨论】:

因为 HAVING 在其他所有内容之后运行,所以此查询构建了一个巨大的临时表,然后在其上运行过滤器。在上面的可扩展性场景中,这是一种极其昂贵的方法。肯定有更有效的方法吗? Sam:我怀疑查询优化器会构造一个大的临时表——因为它知道它需要 GROUP BY b.id,它可以一次生成一行 b.id 顺序,并且跟踪具有相同 b.id 的最后一行中不同事物的数量和橙子的数量。 @Sam:您说您不想使用子查询,从而限制了解决方案。 第二个查询的性能比第一个好得多(按数量级),至少在我的情况下,可能是因为表的大小。当搜索是正则表达式而不是 = 时,它也相当快。它可能相当于一个子查询,但 mysql 并没有扼杀它。【参考方案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 子查询对于每个橙色事物只会运行一次,因此在没有太多橙色的情况下它不会太贵。

【讨论】:

这个对我来说很合适。

以上是关于通过要求所有多个满足条件来过滤一对多查询的主要内容,如果未能解决你的问题,请参考以下文章

SQL Join 一对多从满足条件的相同键值组中提取值

查询集

通过在嵌套的对象数组中查找多个条件来过滤数组

和/或具有满足多个条件的单因素水平的条件过滤

C# WPF SQL 多条件查询

SQL语句where多条件查询怎么写