将索引添加到带时区的时间戳

Gok*_*oku 6 sql postgresql indexing timezone count

我想改进这个慢查询,我想添加一个索引,但我不知道哪种索引类型更适合我的情况。

SELECT COUNT(*) ct FROM events
WHERE dtt AT TIME ZONE 'America/Santiago'
   >= date(now() AT TIME ZONE 'America/Santiago') + interval '1s'  
Run Code Online (Sandbox Code Playgroud)

查询计划:

"Aggregate  (cost=128032.03..128032.04 rows=1 width=0) (actual time=3929.083..3929.083 rows=1 loops=1)"
"  ->  Seq Scan on events  (cost=0.00..125937.68 rows=837742 width=0) (actual time=113.080..3926.972 rows=25849 loops=1)"
"        Filter: (timezone('America/Santiago'::text, dtt) >= (date(timezone('America/Santiago'::text, now())) + '00:00:01'::interval))"
"        Rows Removed by Filter: 2487386"
"Planning time: 0.179 ms"
"Execution time: 3929.136 ms"
Run Code Online (Sandbox Code Playgroud)
  • 查询获取当天事件的计数。
  • dtt 是带有时区列的时间戳。
  • 我正在使用 Postgresql 9.4。

注意:根据 Erwin 的建议,查询运行得更快一些,但我认为还不够快。

"Aggregate  (cost=119667.76..119667.77 rows=1 width=0) (actual time=3687.151..3687.152 rows=1 loops=1)"
"  ->  Seq Scan on vehicle_events  (cost=0.00..119667.14 rows=250 width=0) (actual time=104.635..3687.068 rows=469 loops=1)"
"        Filter: (dtt >= timezone('America/Santiago'::text, date_trunc('day'::text, timezone('America/Santiago'::text, now()))))"
"        Rows Removed by Filter: 2513337"
"Planning time: 0.164 ms"
"Execution time: 3687.204 ms"
Run Code Online (Sandbox Code Playgroud)

Erw*_*ter 7

首先,修复您的查询以使谓词sargable

SELECT count(*) AS ct
FROM   events
WHERE  dtt >= date_trunc('day', now() AT TIME ZONE 'America/Santiago')
                                      AT TIME ZONE 'America/Santiago'
Run Code Online (Sandbox Code Playgroud)

按原样使用列值并将所有计算移至参数。

没错,在导出当天的本地开始后,再次应用以再次AT TIME ZONE转换timestamp回来timestamptz。细节:

一步一步的解释

  1. now()
    .. 是 SQL 标准的 Postgres 实现CURRENT_TIMESTAMP。两者都是 100% 等效的,您可以使用任何一个。它返回当前时间点,因为值timestamptz显示考虑了当前会话的时区,但这与value无关。

  2. now() AT TIME ZONE 'America/Santiago'
    .. 计算给定时区的本地时间。结果数据类型为timestamp. 我们这样做是为了:

  3. date_trunc( now() AT TIME ZONE 'America/Santiago' )
    .. 截断时间组件以获取“美国/圣地亚哥”当天的本地开始时间,与当前时区设置无关。

  4. date_trunc('day', now() AT TIME ZONE 'America/Santiago') AT TIME ZONE 'America/Santiago'
    .. 将 提供timestampAT TIME ZONE构造我们得到相应的timestamptz值(内部UTC)来比较该timestamptzdtt

我删除了+ interval '1s',怀疑你刚才被滥用这一对转换datetimestamp。使用date_trunc()代替产生一个timestamp值。

现在,一个简单的(默认)btree 索引就dtt可以了。当然,只有在谓词具有足够选择性的情况下才会使用索引。

CREATE INDEX events_dtt_idx ON events (dtt);
Run Code Online (Sandbox Code Playgroud)

如果您的重要查询只考虑最近的行,部分索引可能会有更多帮助。细节: