给定一个包含时间范围和 TZ 的表,是否有一种快速方法可以查找当前时间在时间范围内的行,同时尊重 TZ 和 DST?

问题描述 投票:0回答:1

假设我希望用户能够创建包含在自己的时区中指定的时间范围(但没有日期)的记录。然后,在任何时候,当我的应用程序中发生某些事情时,它需要查找当前时间在此时间范围内的所有记录,并尊重与记录一起存储的时区。假设我有一个这样的表设置:

schedules table

id | start_time | end_time | timezone
---------------------------------------
1  | 08:00:00   | 17:00:00 | US/Pacific
2  | 06:00:00   | 13:30:00 | US/Arizona
3  | 13:00:00   | 22:00:00 | US/Eastern
... etc

start_time
end_time
是没有时区的时间类型。
timezone
是 varchar 类型,但我们假设它保证是
name
视图中
pg_timezone_names
列中的值。

我有一个查询,可以查找当前时间位于行的

start_time
end_time
之间的行:

SELECT
    *
FROM
    schedules
WHERE
    schedules.start_time <= (CURRENT_TIMESTAMP AT TIME ZONE schedules.timezone)::time
    AND schedules.end_time >= (CURRENT_TIMESTAMP AT TIME ZONE schedules.timezone)::time;

注意表达方式

(CURRENT_TIMESTAMP AT TIME ZONE schedules.timezone)::time
。此表达式旨在获取行指定时区的当前本地时间。据我所知,它应该尊重该时区的 DST 当前状态,因为
schedules.timezone
不是一个小时偏移,而是一个命名时区,这意味着 Postgres 知道在给定的该时区 DST 是打开还是关闭时间戳。

现在的问题是,我认为无论我如何索引表,这个查询都保证会进行全表扫描,这意味着该表增长得越多,它就会明显变慢。有没有更好的方法来做到这一点,保留在任何时区自动调整 DST 的能力,而不需要全表扫描?

postgresql datetime optimization timezone
1个回答
0
投票

有趣的问题!查询不能使用索引的技术原因是比较的双方都包含列引用,即它们都依赖于当前行。您需要相当恒定的东西来进行索引扫描。

但我认为这不仅仅是您编写查询的方式的错误(查询是正确的!):您想要的基本上是不可能的。美国/东部时区 13:00:00 的含义取决于当前日期。要使用索引,您必须将“美国/东部时区今天 13:00:00”与当前时间戳进行比较,因此您必须对该表达式建立索引。但是您索引的表达式无法更改其当前日期的含义。

我能想到的唯一解决方法是创建一个像这样的物化视图:

CREATE MATERIALIZED VIEW schedules_today AS
SELECT id,
       (start_time + current_date) AT TIME ZONE timezone AS start_timestamp,
       (end_time + current_date) AT TIME ZONE timezone AS end_timestamp
FROM schedules;

您可以在

start_timestamp
end_timestamp
上创建索引以加快查询速度。您必须每天午夜之后刷新物化视图。

如果您使用范围类型,您可能会获得更好的性能:

CREATE MATERIALIZED VIEW schedules_today AS
SELECT id,
       tstzrange(
          (start_time + current_date) AT TIME ZONE timezone,
          (end_time + current_date) AT TIME ZONE timezone,
          '[]'
       ) AS start_end
FROM schedules;

CREATE INDEX ON schedules_today USING gist (start_end);

GiST 索引将加速以下查询:

SELECT * FROM schedules_today
WHERE start_end @> current_timestamp;
© www.soinside.com 2019 - 2024. All rights reserved.