如何编写查询选择合理的权衡?

Posted

技术标签:

【中文标题】如何编写查询选择合理的权衡?【英文标题】:How to write a query selecting reasonable trade-offs? 【发布时间】:2011-04-15 21:30:30 【问题描述】:

在一个表中,我有两列 obs 和 abd。我有兴趣找到 obs 和 abd 的值都较低,但 abd 的值较低 比 obs 的低值重要。在现实世界中,我有一个 难以定义的低 obs 和低 abd 之间的权衡 数学上很难解释,但重点是 我想从查询中看到一些数据 合理的取舍。我想知道一个内的几个数据对 obs 值的范围。例如:

mysql> select obs, abd from flow where obs < 2000 order by abd,obs limit 10;

    +------+--------------+
    | obs  | abd          |
    +------+--------------+
    | 1372 | 0.0000004744 |
    | 1734 | 0.0000017704 |
    | 1010 | 0.0000017716 |
    | 1999 | 0.0000017716 |
    | 1637 | 0.0000036486 |
    |  383 | 0.0000066084 |
    |  745 | 0.0000066084 |
    | 1107 | 0.0000066084 |
    | 1469 | 0.0000066084 |
    | 1831 | 0.0000066084 |
    +------+--------------+

从上面的结果可以看出有几个值 具有相同 abd 值的 obs。我只对一个感兴趣 每个 abd 值的 obs 值最低。所有其他的 应丢弃重复的 abd 值。这很容易通过 使用 group by 子句:

mysql> select obs, abd from flow where obs < 2000 group by abd order by abd,obs limit 10;

    +------+--------------+
    | obs  | abd          |
    +------+--------------+
    | 1372 | 0.0000004744 |
    | 1734 | 0.0000017704 |
    | 1010 | 0.0000017716 |
    | 1637 | 0.0000036486 |
    |  383 | 0.0000066084 |
    |  648 | 0.0000066096 |
    | 1540 | 0.0000097586 |
    | 1928 | 0.0000109544 |
    | 1566 | 0.0000119724 |
    |  913 | 0.0000119736 |
    +------+--------------+

到目前为止,一切都很好。现在的问题是,在看到第一个 obs 为 1372 且 abd 为 0.0000004744 我不感兴趣的条目 看到 obs 和 abd 都较高的第二个条目。我是 有兴趣查看 obs 较低但 abd 为的第三个条目 更高是因为 obs 和 abd 之间存在权衡。再说一次,我是 对查看第四个条目不感兴趣,因为它的值为 obs 和 abd 都高于第三个中已经显示的值 入口。第五条是我特别感兴趣的,因为 虽然 abd 值稍高,但 obs 值要低得多。 至于其余的条目,我不想看到它们,因为 它们的 obs 和 abd 都比已经看到的要高。

总之,我想要一个能显示给我的查询:

+------+--------------+
| obs  | abd          |
+------+--------------+
| 1372 | 0.0000004744 |
| 1010 | 0.0000017716 |
|  383 | 0.0000066084 |
+------+--------------+

加上另外七个条目,其中 obs 继续减少和 abd 继续增加。有没有办法获取数据对集 一个查询而不诉诸程序?

【问题讨论】:

【参考方案1】:

要通过abd 获得最低的obs,您可以这样做:

select min(obs), abd
from flow
where obs < 2000 
group by abd 
order by abd

但在我看来,权衡规则是一种临时性的。您应该尝试找出您能想到的最佳规则并将它们放在桌面上,以便我们能够实现您想要做的事情。

【讨论】:

【参考方案2】:

你可以说你想要一个Pareto front

它不会很快,但试试这个:

SELECT a.obs
     , a.abd
FROM flow a
  LEFT JOIN flow b
    ON   ( b.obs <= a.obs AND b.abd <  a.abd )
      OR ( b.obs <  a.obs AND b.abd <= a.abd )
WHERE b.obs IS NULL
ORDER BY a.abd

还有:

SELECT a.obs
     , a.abd
FROM flow a
WHERE NOT EXISTS
  ( SELECT 1
    FROM flow b
    WHERE ( b.obs <= a.obs AND b.abd <  a.abd )
      OR ( b.obs <  a.obs AND b.abd <= a.abd )
  )
ORDER BY a.abd

还有这个:

SELECT a.obs
     , a.abd
FROM flow a
WHERE NOT EXISTS
  ( SELECT 1
    FROM flow b
    WHERE b.obs <= a.obs
      AND b.abd <  a.abd
  )
  AND NOT EXISTS
  ( SELECT 1
    FROM flow b
    WHERE b.obs < a.obs
      AND b.abd = a.abd
  )
ORDER BY a.abd

或者这个:

SELECT a.obs
     , a.abd
FROM flow a
WHERE NOT EXISTS
  ( SELECT 1
    FROM flow b
    WHERE b.obs <= a.obs
      AND b.abd <= a.abd
      AND (b.obs, b.abd) <> (a.obs, a.abd)
  )
ORDER BY a.abd

检查 4 个中哪个更快。如果你在obsabd 上有索引,我猜是第四个。或者更好,(正如 Unreason 指出的那样),两个索引:一个在 (obs, abd) 上,一个在 abd 上。


更新: (第三次查询的小修正)。

【讨论】:

如果 (obs, abd) 上有索引,甚至更快 另外,根据explainextended.com/2009/09/18/…(这是简单的和不同的情况)left join/is null 产生更好的计划。 @Unreason:是的,你是对的,(obs,abd)和(abd)上的索引可能在这里最好。对于“左连接与不存在”以及哪个产品最好的计划,我只相信使用真实数据的测试。我认为数据分布可能会对大型数据集产生影响。

以上是关于如何编写查询选择合理的权衡?的主要内容,如果未能解决你的问题,请参考以下文章

如何编写简单的选择查询而不是使用视图?

如何编写涉及多个选择的 Oracle SQL 查询

如何在单个过程中编写多个选择查询?

如何编写基于组合框选择的单个 sql 查询?

如何在 jpa 中为 EmbeddedId 编写选择命名查询?

如何编写查询以通过拆分字段来选择行比较