优化BETWEEN日期声明

Nko*_*Nko 3 sql postgresql indexing between postgresql-performance

我需要帮助优化PostgreSQL查询,该查询使用BETWEEN带有timestamp字段的子句.

我有2张桌子:

ONE(int id_one(PK), datetime cut_time, int f1 . . .) 
Run Code Online (Sandbox Code Playgroud)

包含大约3394行

TWO(int id_two(PK), int id_one(FK), int f2 . . .) 
Run Code Online (Sandbox Code Playgroud)

包含大约4000000行

有两个的PK B树索引id_oneid_two,在FK id_onecut_time.

我想执行如下查询:

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';
Run Code Online (Sandbox Code Playgroud)

此查询在大约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"
Run Code Online (Sandbox Code Playgroud)

不使用timestamp列上的索引.如何优化此查询?

Erw*_*ter 5

适当的DDL脚本

不确定您在问题中使用的是哪种表示法.这不是Postgres语法.正确的设置可能如下所示:
SQL Fiddle.

关于这个小提琴的更多信息.
假设timestamp列的数据类型datetime.

查询不正确

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';
Run Code Online (Sandbox Code Playgroud)

...字符串常量'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';
Run Code Online (Sandbox Code Playgroud)

排除上边框.

优化查询

@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);
Run Code Online (Sandbox Code Playgroud)

这样,如果满足某些先决条件,Postgres可以直接读取索引.可能会快一点,不多.没试过.

EXPLAIN输出中的数字奇怪

至于你的陌生号码EXPLAIN ANALYZE.这个SQL小提琴应该解释它.

好像你有这些调试设置:

SET enable_seqscan = off;
SET enable_indexscan = off;
SET enable_bitmapscan = off;
Run Code Online (Sandbox Code Playgroud)

on除调试外,所有这些都应该是.会削弱性能!检查:

SELECT * FROM pg_settings WHERE name ~~ 'enable%'
Run Code Online (Sandbox Code Playgroud)