我需要帮助优化PostgreSQL查询,该查询将BETWEEN
子句与timestamp
字段一起使用。
我有两张桌子:
ONE(int id_one(PK), datetime cut_time, int f1 . . .)
包含约3394行
TWO(int id_two(PK), int id_one(FK), int f2 . . .)
包含约4000000行
FK id_one
和id_two
上的PK id_one
和cut_time
都有btree索引。
我想执行以下查询:
select o.id_one, Date(o.cut_time), o.f1, t.f2
from one o
inner join two t ON (o.id_one = t.id_one)
where o.cut_time between '2013-01-01' and '2013-01-31';
此查询在大约7秒内检索大约1.700.000行。
在解释分析报告下面报告:
"Merge Join (cost=20000000003.53..20000197562.38 rows=1680916 width=24) (actual time=0.017..741.718 rows=1692345 loops=1)"
" Merge Cond: (c.coilid = hf.coilid)"
" -> Index Scan using pk_coils on coils c (cost=10000000000.00..10000000382.13 rows=1420 width=16) (actual time=0.008..4.539 rows=1404 loops=1)"
" Filter: ((cut_time >= '2013-01-01 00:00:00'::timestamp without time zone) AND (cut_time <= '2013-01-31 00:00:00'::timestamp without time zone))"
" Rows Removed by Filter: 1990"
" -> Index Scan using idx_fk_lf_data on hf_data hf (cost=10000000000.00..10000166145.90 rows=4017625 width=16) (actual time=0.003..392.535 rows=1963386 loops=1)"
"Total runtime: 768.473 ms"
未使用timestamp列上的索引。如何优化此查询?
答案 0 :(得分:5)
不确定您在问题中使用的是哪种表示法。这不是Postgres语法。正确的设置可能如下所示:
SQL Fiddle.
关于这个小提琴的更多信息
假设列datetime
的数据类型为timestamp
。
BETWEEN
在timestamp
列的主体上几乎总是错误。这个相关答案的更多细节:
在您的查询中:
SELECT o.one_id, date(o.cut_time), o.f1, t.f2
FROM one o
JOIN two t USING (one_id)
WHERE o.cut_time BETWEEN '2013-01-01' AND '2013-01-31';
...字符串常量'2013-01-01'和'2013-01-31'被强制转换为时间戳'2013-01-01 00:00'和'2013-01-31 00:00' 。这不包括1月31日的大部分时间。时间戳'2013-01-31 12:00'不符合条件,这肯定是错误。
如果您使用'2013-02-01'作为上边框,则包含'2013-02-01 00:00'。还是错的。
要获取“2013年1月”的所有时间戳,需要:
SELECT o.one_id, date(o.cut_time), o.f1, t.f2
FROM one o
JOIN two t USING (one_id)
WHERE o.cut_time >= '2013-01-01'
AND o.cut_time < '2013-02-01';
排除上边框。
@Clodoaldo已经提到了对性能的主要拖累:检索1.7 mio行可能毫无意义。在之前聚合以检索结果。
由于表two
要大得多,关键是行,你必须从那里检索。只要您检索表的大部分内容(超过约5%),就不会使用two.one_id
上的普通索引,因为立即扫描表格会更快
您的表统计信息已过时,或者您已经弄乱了成本常量和其他参数(您显然有这些参数,见下文)以强制Postgres使用索引。
我在two
上看到索引的唯一机会是使用PostgreSQL 9.2 的覆盖索引。但是你没有透露你的版本号。
CREATE INDEX two_one_id_f2 on two(one_id, f2);
这样,如果满足一些先决条件,Postgres可以直接读取索引。可能会快一点,不多。没试过。
EXPLAIN
输出中的奇怪数字关于EXPLAIN ANALYZE
中的奇怪数字。这SQL Fiddle应该解释一下。
好像你有这些调试设置:
SET enable_seqscan = off;
SET enable_indexscan = off;
SET enable_bitmapscan = off;
除调试外,所有这些都应为on
。会削弱性能!检查:
SELECT * FROM pg_settings WHERE name ~~ 'enable%'
答案 1 :(得分:3)
查询在不到一秒的时间内执行。其他6秒以上用于服务器和客户端之间的流量。