我有一个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
答案 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)
取消相关子查询,改为使用JOIN
- 就像
已提到的其他答案。
在 WHERE
子句中,请勿在列上使用不能使用索引的表达式。 @Frank已经提到了它。查询规划器只能重写最基本的稳定表达式才能使用索引。看看我如何改写它。
创建合适的索引。
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.id
和company.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