您使用now() 得到一个异常,因为该函数不是IMMUTABLE(显然)并且引用the manual:
索引定义中使用的所有函数和运算符都必须是“不可变的”...
我看到了两种利用(更有效的)部分索引的方法:
1。使用常量日期条件的部分索引:
CREATE INDEX queries_recent_idx ON queries_query (user_sid, created)
WHERE created > '2013-01-07 00:00'::timestamp;
假设created实际上定义为timestamp。为timestamptz 列(timestamp with time zone)提供timestamp 常量是行不通的。从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 并返回timestamptz。使用now()::timestamp 转换为timestamp 或改用LOCALTIMESTAMP。
db小提琴here
旧sqlfiddle
如果您必须处理对表的并发访问,请使用DROP INDEX CONCURRENTLY 和CREATE INDEX CONCURRENTLY。但是你不能将这些命令包装到一个函数中,因为per documentation:
... 一个常规的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 可能比重新创建索引更昂贵,并且表会变得稍大。
我会选择 first 选项(索引重建)。事实上,我在几个数据库中使用了这个解决方案。第二个会导致更新成本更高。
随着时间的推移,这两种解决方案都会保持其有用性,但随着索引中包含更多过时的行,性能会慢慢下降。