Postgres左外连接似乎没有使用表索引

时间:2014-02-24 19:07:04

标签: sql postgresql indexing query-optimization outer-join

如果这应该发布在DBA.stackexchange.com上,请告诉我......

我有以下查询:

SELECT DISTINCT "court_cases".*
FROM "court_cases"
LEFT OUTER JOIN service_of_processes
  ON service_of_processes.court_case_id = court_cases.id
LEFT OUTER JOIN jobs
  ON jobs.service_of_process_id = service_of_processes.id
WHERE
  (jobs.account_id = 250093
  OR court_cases.account_id = 250093)
ORDER BY
  court_cases.court_date DESC NULLS LAST,
  court_cases.id DESC
LIMIT 30
OFFSET 0;

但是运行需要2-4秒,而在Web应用程序中,单个查询是不可接受的。

我按照建议on the PostgreSQL wiki对查询运行EXPLAIN (ANALYZE, BUFFERS),并将结果放在此处:http://explain.depesz.com/s/Yn6

查询中涉及的那些表的表定义(包括外键关系的索引):

http://sqlfiddle.com/#!15/114c6

使用索引是否有问题,因为WHERE子句正在从两个不同的表中查询?我可以使用哪种索引或更改来使其运行更快?

这些是当前相关表格的大小:

PSQL=# select count(*) from service_of_processes;
 count  
--------
 103787
(1 row)

PSQL=# select count(*) from jobs;
 count  
--------
 108995
(1 row)

PSQL=# select count(*) from court_cases;
 count 
-------
 84410
(1 row)
编辑:我正在使用Postgresql 9.3.1,如果这很重要。

2 个答案:

答案 0 :(得分:3)

or子句可以使查询优化变得困难。一种想法是将查询的两个部分分成两个独立的子查询。这实际上简化了其中一个(court_cases.account_id上的那个)。

试试这个版本:

(SELECT cc.*
 FROM "court_cases" cc
 WHERE cc.account_id = 250093
 ORDER BY cc.court_date DESC NULLS LAST,
          cc.id DESC
 LIMIT 30
) UNION ALL
(SELECT cc.*
 FROM "court_cases" cc LEFT OUTER JOIN
      service_of_processes sop
      ON sop.court_case_id = cc.id LEFT OUTER JOIN
      jobs j
      ON j.service_of_process_id = sop.id
 WHERE (j.account_id = 250093 AND cc.account_id <> 250093)
 ORDER BY cc.court_date DESC NULLS LAST, id DESC
 LIMIT 30
)
ORDER BY court_date DESC NULLS LAST,
         id DESC
LIMIT 30 OFFSET 0;

并添加以下索引:

create index court_cases_accountid_courtdate_id on court_cases(account_id, court_date, id);
create index jobs_accountid_sop on jobs(account_id, service_of_process_id);

请注意,第二个查询使用and cc.count_id <> 250093,这可以防止重复记录。这样就不需要distinctunion(没有union all)。

答案 1 :(得分:0)

我将尝试按以下方式修改查询:

SELECT DISTINCT "court_cases".*
FROM "court_cases"
LEFT OUTER JOIN service_of_processes
  ON service_of_processes.court_case_id = court_cases.id
LEFT OUTER JOIN jobs
  ON jobs.service_of_process_id = service_of_processes.id and jobs.account_id = 250093
WHERE
  (court_cases.account_id = 250093)
ORDER BY
  court_cases.court_date DESC NULLS LAST,
  court_cases.id DESC
LIMIT 30
OFFSET 0;

我认为问题在于查询规划器优化器没有正确分解where过滤器,这是一个非常奇怪的性能错误