Du får ett undantag med now()
eftersom funktionen inte är IMMUTABLE
(uppenbarligen) och citerar manualen
:
Jag ser två sätt att använda ett (mycket effektivare) delindex:
1. Partiellt index med villkor som använder konstant datum:
CREATE INDEX queries_recent_idx ON queries_query (user_sid, created)
WHERE created > '2013-01-07 00:00'::timestamp;
Förutsatt created
är faktiskt definierad som timestamp
. Det skulle inte fungera att tillhandahålla en timestamp
konstant för en timestamptz
kolumn (timestamp with time zone
). Cast från timestamp
till timestamptz
(eller vice versa) beror på den aktuella tidszonsinställningen och är inte oföränderlig . Använd en konstant för matchande datatyp. Förstå grunderna för tidsstämplar med/utan tidszon:
Släpp och återskapa det indexet på timmar med låg trafik, kanske med ett cron-jobb dagligen eller veckovis (eller vad som är tillräckligt bra för dig). Att skapa ett index går ganska snabbt, särskilt ett partiellt index som är relativt litet. Denna lösning behöver inte heller lägga till något i tabellen.
Förutsatt att ingen samtidig åtkomst till tabellen kan automatisk indexåterskapning göras med en funktion som denna:
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$;
Ring:
SELECT f_index_recreate();
now()
(som du hade) motsvarar CURRENT_TIMESTAMP
och returnerar timestamptz
. Casta till timestamp
med now()::timestamp
eller använd LOCALTIMESTAMP
istället.
Om du måste hantera samtidig åtkomst i tabellen, använd DROP INDEX CONCURRENTLY
och CREATE INDEX CONCURRENTLY
. Men du kan inte slå in dessa kommandon i en funktion eftersom, per dokumentation
:
Så med två separata transaktioner :
CREATE INDEX CONCURRENTLY queries_recent_idx2 ON queries_query (user_sid, created)
WHERE created > '2013-01-07 00:00'::timestamp; -- your new condition
Sedan:
DROP INDEX CONCURRENTLY IF EXISTS queries_recent_idx;
Om du vill kan du byta namn till det gamla namnet:
ALTER INDEX queries_recent_idx2 RENAME TO queries_recent_idx;
2. Delvis index med villkor på "arkiverad"-tagg
Lägg till en archived
tagga till ditt bord:
ALTER queries_query ADD COLUMN archived boolean NOT NULL DEFAULT FALSE;
UPDATE
kolumnen med intervaller som du väljer att "gå tillbaka" på äldre rader och skapa ett index som:
CREATE INDEX some_index_name ON queries_query (user_sid, created)
WHERE NOT archived;
Lägg till ett matchande villkor i dina frågor (även om det verkar överflödigt) så att det kan använda indexet. Kontrollera med EXPLAIN ANALYZE
om frågeplaneraren hakar på - den ska kunna använda indexet för frågor på ett nyare datum. Men det kommer inte att förstå mer komplexa förhållanden som inte matchar exakt.
Du behöver inte släppa och återskapa indexet, utan UPDATE
på bordet kan vara dyrare än indexrekreation och bordet blir något större.
Jag skulle gå med den första alternativ (index rekreation). Faktum är att jag använder den här lösningen i flera databaser. Den andra medför dyrare uppdateringar.
Båda lösningarna behåller sin användbarhet över tid, prestandan försämras långsamt när fler föråldrade rader ingår i indexet.