【问题标题】:Postgres left outer join appears to not be using table indicesPostgres 左外连接似乎没有使用表索引
【发布时间】:2014-03-26 15:22:18
【问题描述】:

让我知道是否应该将其发布在 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。

【问题讨论】:

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


    【解决方案1】:

    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 &lt;&gt; 250093,它可以防止重复记录。这消除了对distinctunion 的需要(没有union all)。

    【讨论】:

    • 最后一次创建索引的语法错误,您缺少索引的名称和on。应该类似于create index jobs_account_id_sop_id on jobs(account_id, service_of_process_id);
    【解决方案2】:

    我将尝试将查询修改如下:

    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 过滤器,这是一个非常奇怪的性能错误

    【讨论】:

    • 这行不通:\它不包括jobs.account_id = 250093court_cases.account_id != 250093的法庭案件,这就是为什么在原始查询中有OR的原因:(
    • 你说得对...其实问题是where条件只有在计算完完整的join之后才能求值,因为表很大,所以会请求很长时间。这不是索引问题...我将尝试将查询分解为两个子查询,拆分 where 过滤器,然后应用与两个查询的并集不同的选择
    猜你喜欢
    • 2016-09-19
    • 2015-08-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-24
    • 1970-01-01
    相关资源
    最近更新 更多