【问题标题】:Performance difference: condition placed at INNER JOIN vs WHERE clause性能差异:条件放在 INNER JOIN 与 WHERE 子句
【发布时间】:2012-06-06 14:49:40
【问题描述】:

假设我有一张桌子order as

id | clientid | type | amount | itemid | date
---|----------|------|--------|--------|-----------
23 | 258      | B    | 150    | 14     | 2012-04-03
24 | 258      | S    | 69     | 14     | 2012-04-03
25 | 301      | S    | 10     | 20     | 2012-04-03
26 | 327      | B    | 54     | 156    | 2012-04-04
  • clientid 是返回 client 表的外键
  • itemid 是返回 item 表的外键
  • type 仅是 BS
  • amount 是一个整数

还有一张桌子processed作为

id | orderid | processed | date
---|---------|-----------|---------
41 | 23      | true      | 2012-04-03
42 | 24      | true      | 2012-04-03
43 | 25      | false     | <NULL>
44 | 26      | true      | 2012-04-05     

我需要从order 中获取相同clientid 上相同date 具有相反type 值的所有行。请记住 type 只能有两个值之一 - BS。在上面的示例中,这将是行 2324

另一个约束是processed 中的对应行必须为true 以用于orderid

到目前为止我的查询

SELECT c1.clientid,
       c1.date,
       c1.type,
       c1.itemid,
       c1.amount,
       c2.date,
       c2.type,
       c2.itemid,
       c2.amount

FROM   order c1
INNER JOIN order c2 ON c1.itemid    =  c2.itemid AND
                       c1.date      =  c2.date   AND
                       c1.clientid  =  c2.clientid AND
                       c1.type     <>  c2.type AND
                       c1.id        <  c2.id

INNER JOIN processed p1 ON p1.orderid   =  c1.id AND
                         p1.processed =  true
INNER JOIN processed p2 ON p2.orderid   =  c2.id AND
                         p2.processed =  true

问题:processed = true 作为连接子句的一部分会减慢查询速度。如果我将其移至 WHERE 子句,则性能会好得多。这激起了我的兴趣,我想知道原因

主键和相应的外键列被索引,而值列(valueprocessed 等)不被索引。

免责声明:我继承了这个DB结构,性能差异大约是6秒。

【问题讨论】:

  • 你能显示两个语句的执行计划(最好使用explain analyze)吗?但这听起来确实像一个错误。您可能希望将执行计划上传到explain.depesz.com,而不是内联发布。
  • c1.type = 'b' and c2.type = 's' 替换c1.type &lt;&gt; c2.type 会改善一切吗?
  • @TokenMacGuy 从语义上讲,这会不会有所不同,即只有当和's'出现在'b'之后? c1.id
  • 您可能会看到它们是等价的,但数据库不太可能知道它们只能以一种顺序出现。
  • @Insectatorious:回答@Token 的问题:不,但(c1.type = 'b' and c2.type = 's') OR (c1.type = 's' and c2.type = 'b') 可能比c1.type &lt;&gt; c2.type 快。

标签: sql performance postgresql query-optimization


【解决方案1】:

您看到差异的原因是由于规划器正在组合的执行计划,这显然是不同的查询(可以说,它应该优化 2 个查询是相同的,这可能成为一个错误)。这意味着计划者认为它必须以特定方式工作才能获得每个语句中的结果。

当您在 JOIN 中执行此操作时,规划器可能必须从表中进行选择,按“True”部分进行过滤,然后加入结果集。我会想象这是一个大表,因此需要查看大量数据,并且不能有效地使用索引。

我怀疑如果您在 WHERE 子句中执行此操作,则规划器会选择更有效的路线(即基于索引或预过滤数据集)。

您可以通过在两列上添加索引(不确定 Postgres 是否支持包含的列和多列索引)来使连接工作尽可能快(如果不是更快的话)。

简而言之,规划器的问题在于它选择 2 条不同的路线来获得结果集,其中一条路线的效率不如另一条路线。如果没有完整的表信息和 EXPLAIN ANALYZE 信息,我们是不可能知道原因的。

如果您想详细了解您的特定查询为什么会这样做,您需要提供更多信息。然而原因是规划者选择了不同的路线。

其他阅读材料:

http://www.postgresql.org/docs/current/static/explicit-joins.html

刚刚略读,似乎 postgres 规划器不会重新排序连接以优化它。尝试更改语句中连接的顺序,看看是否获得相同的性能......只是一个想法。

【讨论】:

  • 对....有道理...问题是我已经简化了表格及其各自的结构来发布这个问题..我会尝试获取explain analyse
  • 您不会通过在ONWHERE 子句中添加条件来强制查询计划程序。一个体面的优化器/查询计划器应该能够将两个版本识别为等效(如果它们是)并从各种执行计划中进行选择。
  • @ypercube 优化器通常会将它们推到尽可能低的位置以尽快降低基数,但显然当它导致表操作而不是索引操作时,这并不好。然后也许它不够聪明,无法在工作集较小时将其拉起并稍后使用。最有趣的是优化器不会将 WHERE 版本中的子句推向相同。
  • @CadeRoux:是的,但我认为 Postgres 已经足够成熟,可以做到这一点。可能会使优化器感到困惑的是它必须连接 4 个表(那里有很多计划)并且只有几个索引。如果有有用的索引,我认为它会在两种情况下选择相同的计划。
  • 也许“力”这个词不合适,但是,这个概念是正确的。也许“告诉”是这个词,但这意味着对不熟悉计划者的人进行描述。通过做他正在做的事情(JOIN 与 WHERE),规划者正在走另一条路,因此在性能上存在差异。
猜你喜欢
  • 2019-01-15
  • 2014-02-07
  • 1970-01-01
  • 1970-01-01
  • 2013-08-11
  • 2010-11-04
  • 2012-04-25
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多