我必须在课上解决一个关于postgresql中查询优化的问题。
我必须优化以下查询。
select ol_number,
sum(ol_quantity) as sum_qty,
sum(ol_amount) as sum_amount,
avg(ol_quantity) as avg_qty,
avg(ol_amount) as avg_amount,
count(*) as count_order
from orderline
where ol_delivery_d > '2007-01-02 00:00:00.000000'
group by ol_number order by ol_number
对我来说,它似乎已经被优化了,但我对此并不确定。
是否可以通过索引或其他方式优化查询(也可以使用物化视图)?
执行计划(也在注释中链接)
"Sort (cost=63652.89..63652.92 rows=11 width=13) (actual time=4026.270..4026.272 rows=15 loops=1)" " Sort Key: ol_number" " Sort Method: quicksort Memory: 26kB" " -> HashAggregate (cost=63652.54..63652.70 rows=11 width=13) (actual time=4026.218..4026.247 rows=15 loops=1)" " -> Seq Scan on order_line (cost=0.00..44671.46 rows=1265405 width=13) (actual time=0.008..486.399 rows=1259054 loops=1)" " Filter: (ol_delivery_d > '2007-01-02 00:00:00'::timestamp without time zone)" " Rows Removed by Filter: 538903" "Total runtime: 4026.346 ms"
我意识到下面的技巧,因为我认为平均是和总和/计数一样的东西,所以我认为这是相关的,意识到这一点还是我错了?
select ol_number, sum_qty, sum_amount,
sum_qty/count_order as avg_qty,
sum_amount/count_order as avg_amount, count_order
from (select ol_number, sum(ol_quantity) as sum_qty, sum(ol_amount) as sum_amount, count(*) as count_order
from order_line
where ol_delivery_d > '2007-01-02 00:00:00.000000'
group by ol_number
order by ol_number) as t
当我看到这样的执行计划时,我突然想到的是“Seq Scan”。训练你的眼睛,让“Seq Scan”跳出来。
WHERE子句中使用的索引列
表设计的一条经验法则是,“索引WHERE子句中使用的每一列。”一方面,我希望看到“ol_delivery_d”上的索引。另一方面,我认为查询计划器不会在这种特定情况下使用它。
在WHERE子句中,您计算了130万行的聚合,但仅排除了50万行。即使有索引,如果查询规划器在这种情况下选择顺序扫描,我也不会感到惊讶,这里的快速测试表明就是这种情况。(不过,我的快速测试可能是错误的。我的数据不是你的数据;我的服务器不是你的服务器。)
如果“ol_delivery_d”上没有索引,请创建一个。它可能不会帮助此查询,但会帮助返回更少行数的查询。
使用正确的数据类型
您有一个名为“ol_delivery_d”的列,这表明表设计器打算存储某种交付日期。缺少时间数据“00:00:000.000000”也表明您是按日期查询,而不是按时间戳查询。但执行计划显示您的值被转换为“不带时区的时间戳”。
如果仅查询日期,请将此列的数据类型更改为“日期”。“date”数据类型比“timestamp”更窄 - 页面中适合的数据更多,索引更小,查询应该运行得更快一些。
避免外部排序
最后,此查询的排序方法是最佳的。内存中排序比外部排序快得多。
你可以做的其他事情
有很多其他的事情可以让查询运行得更快。(参见PostgreSQL wiki。)让你的服务器速度翻倍会让他们更快。在SSD上创建新的表空间并将表和索引移动到这些新的表空间通常会有所帮助。实体化视图可能有帮助,也可能没有帮助;我认为在这种情况下不会有帮助,但试一试也无妨。