【问题标题】:Optimize BETWEEN date statement优化 BETWEEN 日期语句
【发布时间】:2013-04-09 00:16:58
【问题描述】:

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

我有两张桌子:

ONE(int id_one(PK), datetime cut_time, int f1 . . .) 

包含大约 3394 行

TWO(int id_two(PK), int id_one(FK), int f2 . . .) 

包含大约 4000000 行

在 PK id_oneid_two 上,在 FK id_onecut_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"

未使用时间戳列上的索引。如何优化这个查询?

【问题讨论】:

  • 您是否禁用了规划器选项? cost=10000000000.00 的成本看起来非常可疑。
  • 如何设置规划器选项的默认值?
  • 我在想您可能会将其中一项或多项更改为关闭:postgresql.org/docs/9.2/static/…

标签: sql postgresql indexing between postgresql-performance


【解决方案1】:

正确的 DDL 脚本

不确定您在问题中使用了哪种表示法。这不是 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%'

【讨论】:

  • BETWEENtimestsamp 上取得了不错的成绩,并且非常好,内容丰富的帖子。我学到了一些东西:)
  • 感谢 Erwin 提供的索引提示。抱歉,我错过了向您提供我正在使用 Postgresql 9.2 的信息。
【解决方案2】:

查询在不到一秒的时间内执行。其他 6 秒以上的时间用于服务器和客户端之间的流量。

【讨论】:

  • 谢谢Neto,好的,查询执行的有效时间不到一秒。当我也将开发我的解决方案时,我会在客户端和服务器之间的流量中看到这种延迟吗?
  • @Nko 当然。可能更糟。您需要 170 万行吗?
  • @Nko...Neto 是对的。在这一点上,你不会获得很多优化日期子句的东西。您检索的记录数量是主要问题。
  • 好的,谢谢,这只是我对此类数据的第一种方法,当然我需要聚合这些检索到的数据,从而最大限度地减少总执行时间。谢谢
猜你喜欢
  • 2012-11-09
  • 2014-01-25
  • 2010-10-08
  • 2016-11-04
  • 2021-11-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多