增强Postgresql查询(许多子查询)

时间:2012-12-01 12:04:54

标签: sql postgresql

我有一个postgresql查询,由于我认为是子查询,需要花费大量时间来执行(5分钟)。我想找到一种方法来增强这个查询:

select v.id, v.pos, v.time,  v.status,  vi.name,vi.type,
        (select c.fullname
           from company c
          where vi.registered_owner_code = c.owcode ) AS registered_owner
       ,(select c.fullname
           from company c
         where vi.group_beneficial_owner_code=c.owcode) AS group_beneficial_owner
       ,(select c.fullname
           from company c
          where vi.operator_code = c.owcode ) AS operator
       ,(select c.fullname
           from company c
          where vi.manager_code = c.owcode ) AS manager
  from (car_pos v left join cars vi on v.id = vi.id)
 where age(now(), v.time::time with time zone) < '1 days'::interval

5 个答案:

答案 0 :(得分:2)

  

因为子查询我认为

这不是一个猜谜游戏。您可以在pgadmin或控制台

下获取查询执行计划说明

http://www.pgadmin.org/docs/1.4/query.html

http://www.postgresql.org/docs/current/static/sql-explain.html

然后你可以看到发生了什么以及花了那么多时间。

分析后,您可以添加索引或更改其他内容,但至少您会知道需要更改的内容。

答案 1 :(得分:1)

WHERE条件不能使用索引,您必须更改该索引。 v.time不应该在volatile函数中,在这种情况下为age()。

答案 2 :(得分:1)

3个关键成分:

  1. 取消相关子查询,改为使用JOIN - 就像 已提到的其他答案。

  2. WHERE子句中,请勿在列上使用不能使用索引的表达式。 @Frank已经提到了它。查询规划器只能重写最基本的稳定表达式才能使用索引。看看我如何改写它。

  3. 创建合适的索引

  4. SELECT v.id, v.pos, v.time, v.status, c.name, c.type
          ,r.fullname AS registered_owner
          ,g.fullname AS group_beneficial_owner
          ,o.fullname AS operator
          ,m.fullname AS manager
    FROM   car_pos v
    LEFT   JOIN cars    c ON USING (id)
    LEFT   JOIN company r ON r.owcode = c.registered_owner_code
    LEFT   JOIN company g ON g.owcode = c.group_beneficial_owner_code
    LEFT   JOIN company o ON o.owcode = c.operator_code
    LEFT   JOIN company m ON m.owcode = c.manager_code
    WHERE  v.time > (now() - interval '1 day');
    

    您需要cars.idcompany.owcode上的唯一索引(主键也可以完成工作)。

    您需要car_pos.time上的索引,如:

    CREATE INDEX car_pos_time_idx ON car_pos (time DESC);
    

    也可以不按降序排列。如果您有批次行( - &gt;大表,大索引),您可能需要创建仅包含最近历史记录的部分索引重新创建< / em>每天或每周在非工作时间:

    CREATE INDEX car_pos_time_idx ON car_pos (time DESC);
    WHERE time > $mydate
    

    $ mydate是(now() - interval '1 day')的结果。这在任何时候都逻辑地匹配您的查询条件。随着时间的推移,效果会逐渐恶化。

    除此之外:不要将类型为timestamp“时间”的列命名,这从文档的角度来看是误导性的。实际上,请不要将time作为列名使用。它是每个SQL标准中的reserved word和PostgreSQL中的类型名称。

答案 3 :(得分:0)

一个简单的解决方案是将其转换为连接

select v.id, v.pos, v.time,  v.status,  vi.name,vi.type, 
reg_owner.fullname AS registered_owner, 
gr_ben_owner.fullname AS group_beneficial_owner, 
op.fullname AS operator, 
man.fullname AS manager
from  
  car_pos v 
  left join cars vi on v.id = vi.id
  left join company reg_owner on vi.registered_owner_code = reg_owner.owcode
  left join company gr_ben_owner on vi.group_beneficial_owner_code = gr_ben_owner.owcode
  left join company op on vi.operator_code = op.owcode
  left join company man on vi.manager_code  = man.owcode
where age(now(), v.time::time with time zone) < '1 days'::interval

我怀疑,通过表公司只有一个连接可能是可能的...我不是100%确定确切的语法,我怀疑这会提高性能(因为所有CASE-WHEN,GROUP by等)与四次加入解决方案相比,但我认为这也应该有效。 (我假设,car-car_pos是一对一的关系)

select v.id, MAX(v.pos) as pos, MAX(v.time) as vtime,  MAX(v.status) as status,  MAX(vi.name) as name,MAX(vi.type) as type, 
MAX(CASE WHEN c.owcode = vi.registered_owner_code THEN c.fullname END) AS registered_owner, 
MAX(CASE WHEN c.owcode = vi.group_beneficial_owner_code THEN c.fullname END) AS group_beneficial_owner, 
MAX(CASE WHEN c.owcode = vi.operator_code THEN op.fullname END) AS operator, 
MAX(CASE WHEN c.owcode = vi.manager_code THEN man.fullname END) AS manager
from  
  car_pos v 
  left join cars vi on v.id = vi.id
  left join company c on c.owcode IN (vi.registered_owner_code, vi.group_beneficial_owner_code, vi.operator_code, vi.manager_code)
group by v.id
having age(now(), vtime::time with time zone) < '1 days'::interval

如果您可以将表创建DDL脚本和一些插入到问题中,那么在SQL小提琴中尝试就很容易...

答案 4 :(得分:0)

select v.id, v.pos, v.time,  v.status,  vi.name,vi.type,
c1.fullname as Registered_owner,
c2.fullname as group_beneficial_owner,          
c3.fullname AS operator,   
c4.fullname AS manager 

from car_pos v 
left outer join cars vi on v.id = vi.id
left outerjoin company c1 on vi.registered_owner_code=c1.owcode
left outerjoin company c2 on vi.group_beneficial_owner_code=c2.owcode
left outerjoin company c3 on vi.operator_code=c3.owcode
left outerjoin company c4 on vi.manager_code=c4.owcode
 where age(now(), v.time::time with time zone) < '1 days'::interval