PostgreSQL - 奇怪的查询计划程序行为

时间:2015-12-03 11:20:05

标签: postgresql postgresql-9.3 query-planner

假设我有这样的查询:

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秒(“客户端”有大约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语句开始,并且必须仅执行两行的连接。但是,在慢速情况下,它将首先连接所有表,然后按EXISTS过滤。

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

Postgres版本在CentOS 6.3上为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";

让它运行得很快,但这是不可接受的,因为帐户过滤参数是可选的,如果该表中没有匹配项,我仍然需要结果。使用“LEFT JOIN accounts”代替“INNER JOIN accounts”可以再次杀死表演。

1 个答案:

答案 0 :(得分:0)

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