我的数据库中有 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
如何为连接添加索引以正确使用它并更快地执行连接?
该函数有多个(重载)版本
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)) 的索引也是不可能的。
解决方法包括:
timestamp without time zone
。 (仅当确实合适时,大多数情况下您都想要timestamp with time zone
!)也就是说,我怀疑你的索引会有多大帮助,甚至一开始就不会被使用。
如果您希望进行仅索引扫描,请将成功移至索引的
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)。现在索引太大了,我不确定它会有帮助。
参见: