我的数据库中有 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
!)如果您希望进行仅索引扫描,请将
success
移至索引的
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);
现在索引太大了,我不确定它会有帮助。而且我不确定连接是否允许开始使用索引。参见: