如何在postgres中为带有时区的列类型时间戳正确添加索引并在join中使用

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

我的数据库中有 2 个表,定义如下 -

CREATE TABLE metric_events (
    id uuid PRIMARY KEY,
    metric_id integer NOT NULL,
    event_at timestamp with time zone NOT NULL,
    code text NOT NULL, 
    test boolean DEFAULT false NOT NULL,
    success boolean DEFAULT false NOT NULL
);

CREATE TABLE hourly_metric_event_counters (
    start_at timestamp with time zone NOT NULL,
    metric_id integer NOT NULL,
    code text NOT NULL,
    test boolean NOT NULL,
    total_count integer DEFAULT 0,
    success_count integer DEFAULT 0,
    PRIMARY KEY (metric_id, test, code, start_at)
);

CREATE UNIQUE INDEX metric_events_uuid_index ON metric_events(id);
CREATE INDEX metric_events_event_at_index on metric_events(event_at);
CREATE INDEX metric_events_metric_id_and_event_at_index ON metric_events(metric_id, event_at);
CREATE INDEX hourly_metric_event_counters_start_at_index ON hourly_metric_event_counters(start_at);

然后我必须每天运行一次这个查询 -

SELECT date_trunc('hour', b.event_at),
       b.metric_id,
       b.code,
       b.test,
       COUNT(b.*) - MAX(bec.total_count) as total_diff,
       SUM(CASE WHEN b.success THEN 1 ELSE 0 END) - MAX(bec.success_count) as success_diff
FROM metric_events b
JOIN hourly_metric_event_counters bec
ON bec.metric_id = b.metric_id AND
   bec.test = b.test AND
   bec.code = b.code AND
   bec.start_at = date_trunc('hour', b.event_at)
WHERE event_at >= date_trunc('hour', NOW() - interval '24 hours') AND event_at < date_trunc('hour', NOW() - interval '1 hour')
GROUP BY 1, 2, 3, 4
HAVING COUNT(b.*) - MAX(bec.total_count) != 0 OR
       SUM(CASE WHEN b.success THEN 1 ELSE 0 END) - MAX(bec.success_count) != 0

现在我需要为

metric_events
metric_id, date_trunc('hour', event_at), test, code
表添加索引,以便连接可以更快。

索引查询为-

CREATE INDEX metric_events_composite_index on metric_events (metric_id, code, test, success, date_trunc('hour'::text, event_at));

错误:索引表达式中的函数必须标记为 IMMUTABLE SQL 状态:42P17

如何为连接添加索引以正确使用它并更快地执行连接?

postgresql
1个回答
0
投票

该函数有多个(重载)版本

date_trunc()
。所有这些都是
IMMUTABLE
- 除了采用
timestamptz
输入的那个(必然)只是
STABLE
。某些调用(如
date_trunc('day', event_at)
)取决于当前
time_zone
设置。甚至还有(极其愚蠢的)时间转换,涉及(同样愚蠢的)“夏令时”的不到一小时。因此,即使将时间点截断为小时也可能取决于时区和一年中的时间。

因此,您无法在

date_trunc('hour', event_at)
上创建索引。表达方式不是
IMMUTABLE
。出于类似的原因,转换为
time
timestamp
也只是
STABLE
。因此涉及 date_trunc('hour', (event_at::time)) 的索引也是不可能的。

解决方法包括:

也就是说,我怀疑你的索引会有多大帮助,甚至一开始就不会被使用。

如果您希望进行仅索引扫描,请将成功移至索引的

INCLUDE
部分。您还必须包含原始列
event_at
才能实现这一点(仅索引扫描的弱点):

CREATE INDEX metric_events_composite_index
ON metric_events (metric_id, code, test, fake_immutable_date_trunc('hour', event_at))
INCLUDE (success, event_at);

并在查询中使用相同的表达式:fake_immutable_date_trunc('hour', event_at)。现在索引太大了,我不确定它会有帮助。

参见:

© www.soinside.com 2019 - 2024. All rights reserved.