PostgreSQL - 奇怪的查询规划器行为

PostgreSQL - weird query planner behavior

假设我有这样的查询:

SELECT * 
  FROM clients c
  INNER JOIN clients_balances cb ON cb.id_clients = c.id
  LEFT JOIN clients com ON com.id = c.id_companies
  LEFT JOIN clients com_real ON com_real.id = c.id_companies_real
  LEFT JOIN rate_tables rt_orig ON rt_orig.id = c.orig_rate_table
  LEFT JOIN rate_tables rt_term ON rt_term.id = c.term_rate_table
  LEFT JOIN payment_terms pt ON pt.id = c.id_payment_terms
  LEFT JOIN paygw_clients_profiles cpgw ON (cpgw.id_clients = c.id AND cpgw.id_companies = c.id_companies_real)
WHERE 
  EXISTS (SELECT * FROM accounts WHERE (name LIKE 'x' OR accname LIKE 'x' OR ani LIKE 'x') AND id_clients = c.id)
  AND c."type" = '0'
  AND c."id" > 0 
ORDER BY c."name";

此查询在生产环境中使用时 运行 大约需要 35 秒("clients" 有大约 100 万条记录)。但是,如果我取出任何连接 - 查询将只需要大约 300 毫秒来执行。

我试过查询计划器设置,但无济于事。

这里有一些解释分析输出:

http://explain.depesz.com/s/hzy (slow - 48049.574 ms)
http://explain.depesz.com/s/FWCd (fast - 286.234 ms, rate_tables JOIN removed)
http://explain.depesz.com/s/MyRf (fast - 539.733 ms, paygw_clients_profiles JOIN removed)

看起来在快速情况下,规划器从 EXISTS 语句开始,总共只需要对两行执行连接。然而,在缓慢的情况下,它会首先连接所有 table,然后通过 EXISTS 过滤。

我需要做的是在合理的时间内完成此查询 运行,所有七个连接都已到位。

CentOS 6.3 上的 Postgres 版本是 9.3.10。

谢谢。

更新

像这样重写查询:

SELECT * 
  FROM clients c
  INNER JOIN clients_balances cb ON cb.id_clients = c.id
  INNER JOIN accounts a ON a.id_clients = c.id AND (a.name = 'x' OR a.accname = 'x' OR a.ani = 'x')
  LEFT JOIN clients com ON com.id = c.id_companies
  LEFT JOIN clients com_real ON com_real.id = c.id_companies_real
  LEFT JOIN rate_tables rt_orig ON rt_orig.id = c.orig_rate_table
  LEFT JOIN rate_tables rt_term ON rt_term.id = c.term_rate_table
  LEFT JOIN payment_terms pt ON pt.id = c.id_payment_terms
  LEFT JOIN paygw_clients_profiles cpgw ON (cpgw.id_clients = c.id AND cpgw.id_companies = c.id_companies_real)
WHERE 
  c."type" = '0' AND c.id > 0 
ORDER BY c."name";

使其运行快,但是,这不是acceptable,因为帐户过滤参数是可选的,如果没有匹配,我仍然需要结果table.使用 "LEFT JOIN accounts" 而不是 "INNER JOIN accounts" 会再次破坏性能。

按照 Tome Lane 的建议,我将以下两个参数:join_collapse_limit 和 from_collapse_limit 更改为 10 而不是默认的 8,这解决了问题。