你得到一个异常使用now()
,因为该功能不是(显然),并且IMMUTABLE
引用手册:
索引定义中使用的所有函数和运算符都必须是“不可变的”...
我看到了两种利用(更有效的)部分索引的方法:
1. 以固定日期为条件的部分索引:
CREATE INDEX queries_recent_idx ON queries_query (user_sid, created)
WHERE created > '2013-01-07 00:00'::timestamp;
假设 created
实际上定义为timestamp
。为列 ( ) 提供timestamp
常量是行不通的。转换from to (反之亦然)取决于当前时区设置,并且不是 immutable。使用匹配数据类型的常量。了解带/不带时区的时间戳的基础知识:timestamptz
timestamp with time zone
timestamp
timestamptz
在流量较低的几个小时删除并重新创建该索引,可能每天或每周使用一个 cron 作业(或任何对您来说足够好的东西)。创建索引非常快,尤其是相对较小的部分索引。此解决方案也不需要向表中添加任何内容。
假设没有对表的并发访问,可以使用如下函数完成自动索引重新创建:
CREATE OR REPLACE FUNCTION f_index_recreate()
RETURNS void
LANGUAGE plpgsql AS
$func$
BEGIN
DROP INDEX IF EXISTS queries_recent_idx;
EXECUTE format('
CREATE INDEX queries_recent_idx
ON queries_query (user_sid, created)
WHERE created > %L::timestamp'
, LOCALTIMESTAMP - interval '30 days'); -- timestamp constant
-- , now() - interval '30 days'); -- alternative for timestamptz
END
$func$;
称呼:
SELECT f_index_recreate();
now()
(就像你一样) 相当于CURRENT_TIMESTAMP
and returns timestamptz
。改用或timestamp
使用。now()::timestamp
LOCALTIMESTAMP
db<>fiddle here
旧sqlfiddle
如果您必须处理对表的并发访问,请使用DROP INDEX CONCURRENTLY
and CREATE INDEX CONCURRENTLY
。但是您不能将这些命令包装到一个函数中,因为根据文档:
CREATE INDEX
...可以在事务块内执行常规命令,但CREATE INDEX CONCURRENTLY
不能。
因此,有两个单独的交易:
CREATE INDEX CONCURRENTLY queries_recent_idx2 ON queries_query (user_sid, created)
WHERE created > '2013-01-07 00:00'::timestamp; -- your new condition
然后:
DROP INDEX CONCURRENTLY IF EXISTS queries_recent_idx;
(可选)重命名为旧名称:
ALTER INDEX queries_recent_idx2 RENAME TO queries_recent_idx;
2.“归档”标签条件下的部分索引
将archived
标签添加到您的表中:
ALTER queries_query ADD COLUMN archived boolean NOT NULL DEFAULT FALSE;
UPDATE
每隔您选择“淘汰”旧行并创建索引的列,如:
CREATE INDEX some_index_name ON queries_query (user_sid, created)
WHERE NOT archived;
向您的查询添加匹配条件(即使它看起来是多余的)以允许它使用索引。检查EXPLAIN ANALYZE
查询计划器是否赶上 - 它应该能够在较新的日期使用索引进行查询。但它不会理解不完全匹配的更复杂的条件。
您不必删除并重新创建索引,但UPDATE
表上的索引可能比重新创建索引更昂贵,并且表会稍大一些。
我会选择第一个选项(索引娱乐)。事实上,我在几个数据库中使用了这个解决方案。第二个会导致更昂贵的更新。
随着时间的推移,这两种解决方案都保留了它们的有用性,随着索引中包含更多过时的行,性能会慢慢下降。