Postgres 哈希连接与嵌套循环决策

Posted

技术标签:

【中文标题】Postgres 哈希连接与嵌套循环决策【英文标题】:Postgres hash join vs nested loop decision making 【发布时间】:2017-11-28 20:12:22 【问题描述】:

我有一个很长的查询,我将在这里总结并粘贴在底部:

select * from
a
left join b t1 on a.x = b.x
left join b t2 on a.y = b.x
left join b t3 on a.z = b.x
left join c on a.1 = c.1 and a.2 = c.2 and a.3 = c.3 --call this predicate 1
where c.z is null

a 和 c 的主键 1,2,3 未聚集 a.x y 或 z 可以为空 您将在下面链接的内容中看到 a 是 40k 行,c 是 500k 行,b 是 7k 行。此查询需要 10 分钟。手动在excel中完成会更快。即使在我运行了 Vacuum 完整分析之后,我的行数估计也是错误的,并且它在不应该的地方有嵌套循环

这里是完整的 https://explain.depesz.com/s/w2uN

当我删除谓词 1 时,所有嵌套循环都消失了,行估计仍然错误。在我 RESET postgre 之后运行也需要不到一秒钟的时间,所以没有缓存

https://explain.depesz.com/s/O7R

关于如何强制散列连接的任何想法?或者我可以在我一直截断并加载到的 40k 表上构建 3 个索引,因为这实际上是一个每周都会刷新的临时表。看起来有点矫枉过正,但可能不会伤害任何人。除了真空分析之外,关于如何在计划器中正确计算行数的内容也很少。对此有何想法?

最后,这是完整的代码

SELECT
    ar.cocd,
    ar.customer,
    ar.sales_doc,
    ar.documentno,
    ar.headertext,
    ar.clrng_doc,
    ar.typ,
    ar.net_due_dt,
    ar.amt,
    ar.lcurr,
    ar.amount_in_dc,
    ar.curr,
    ar.text,
    ar.doc_date,
    ar.clearing,
    ar.po_number,
    ar.payt,
    ar.st,
    ar.arrear,
    ar.gl,
    ar.user_name,
    ar.tcod,
    ar.itm,
    ar.inv_ref,
    ar.amount_in_loccurr2,
    ar.pmnt_date,
    ar.pk,
    ar.pstng_date,
    ar.account,
    ar.accty,
    ar.aging_bucket,
    ar.billdoc,
    ar.ftyp,
    ar.general_ledger_amt,
    ar.offstacct,
    ar.pmtmthsu,
    ar.purchdoc,
    ar.rcd,
    ar.transtype,
    ar.ym,
    COALESCE(ar_f2.branch, ar_f2.subbranch, ar_f2.account) AS forecast_company,
    ar_f.customer_name AS paying_company,
    ar_f3.customer_name AS shipping_company
   FROM h_ar_open ar
     LEFT JOIN h_ar_forecast ar_f ON ar.customer = ar_f.customer
     LEFT JOIN h_ar_forecast ar_f2 ON ar.soldto::double precision = ar_f2.customer
     LEFT JOIN h_ar_forecast ar_f3 ON ar.shipto::double precision = ar_f3.customer
     LEFT JOIN h_ar_hist hist ON ar.cocd = hist.cocd AND ar.itm = hist.itm AND ar.documentno = hist.documentno
  WHERE hist.documentno IS NULL;

【问题讨论】:

我是否做了如此令人反感的事情以至于值得投反对票?我只是无法想象为什么一个查询在几秒钟内运行,而另一个可能在几小时内运行? 我在 b.x(或 h_ar_forecast.customer,如果您阅读了长代码)上添加了一个索引,它仍然是嵌套循环,但现在是 3 秒。索引真的那么灵敏吗? explain.depesz.com/s/y9VY ON ar.soldto::double precision = ar_f2.customer 为什么选演员?在加入条件! 我知道,我不知道该怎么办。我真的只想制作 h_ar_forecast.customer 文本,所以没有强制转换,但是我必须删除并创建 10 个依赖于该表的不同视图,并且必须以正确的顺序删除并重新创建它们。也许这本身就是一个问题,但有没有比从 pgadmin 复制所有定义并移动它们更简单的方法? 【参考方案1】:

您评论中的索引很有帮助,因为它使嵌套循环连接变得更快。超过 7000 行的 46417 次顺序扫描很糟糕。

您的问题是估计错误。

也许您可以通过以下技巧强制加入顺序:

SELECT ...
FROM (SELECT ...
      FROM a
         LEFT JOIN b t1 ...
         LEFT JOIN b t3 ...
         LEFT JOIN b t3 ...
      OFFSET 0) x
   LEFT JOIN c ...;

如果 c 最后加入,错误估计不会造成伤害。

【讨论】:

以上是关于Postgres 哈希连接与嵌套循环决策的主要内容,如果未能解决你的问题,请参考以下文章

SQL Server 2008 中嵌套循环连接和哈希连接的区别

哈希联接与嵌套循环

在使用非 Equi 连接条件时避免使用嵌套循环连接

Mysql算法内部算法 - 嵌套循环连接算法

Firebird hash join

Python字典的嵌套——用于决策树存储树结构