Výjimku získáte pomocí now()
protože funkce není IMMUTABLE
(samozřejmě) a s citací příručky
:
Vidím dva způsoby, jak využít (mnohem efektivnější) částečný index:
1. Částečný index s podmínkou pomocí konstanty datum:
CREATE INDEX queries_recent_idx ON queries_query (user_sid, created)
WHERE created > '2013-01-07 00:00'::timestamp;
Za předpokladu created
je ve skutečnosti definováno jako timestamp
. Nefungovalo by poskytnutí timestamp
konstanta pro timestamptz
sloupec (timestamp with time zone
). Obsazení z timestamp
na timestamptz
(nebo naopak) závisí na aktuálním nastavení časového pásma a není neměnný . Použijte konstantu shodného datového typu. Pochopte základy časových razítek s / bez časového pásma:
Uvolněte a znovu vytvořte tento index v hodinách s nízkou návštěvností, možná s úlohou cron na denní nebo týdenní bázi (nebo cokoli, co je pro vás dost dobré). Vytvoření indexu je velmi rychlé, zejména částečný index, který je poměrně malý. Toto řešení také nemusí do tabulky nic přidávat.
Za předpokladu žádného souběžného přístupu k tabulce, automatické obnovení indexu lze provést pomocí funkce, jako je tato:
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$;
Volejte:
SELECT f_index_recreate();
now()
(jako jste měli vy) je ekvivalentem CURRENT_TIMESTAMP
a vrátí timestamptz
. Odesílat do timestamp
pomocí now()::timestamp
nebo použijte LOCALTIMESTAMP
místo toho.
db<>fiddle zde
Staré sqlfiddle
Pokud musíte řešit souběžný přístup k tabulce, použijte DROP INDEX CONCURRENTLY
a CREATE INDEX CONCURRENTLY
. Tyto příkazy však nemůžete zabalit do funkce, protože podle dokumentace
:
Tedy s dvěma samostatnými transakcemi :
CREATE INDEX CONCURRENTLY queries_recent_idx2 ON queries_query (user_sid, created)
WHERE created > '2013-01-07 00:00'::timestamp; -- your new condition
Potom:
DROP INDEX CONCURRENTLY IF EXISTS queries_recent_idx;
Volitelně přejmenujte na starý název:
ALTER INDEX queries_recent_idx2 RENAME TO queries_recent_idx;
2. Částečný index s podmínkou na značce „archivováno“
Přidejte archived
tag na váš stůl:
ALTER queries_query ADD COLUMN archived boolean NOT NULL DEFAULT FALSE;
UPDATE
sloupec v intervalech podle vašeho výběru "vyřadit" starší řádky a vytvořit index jako:
CREATE INDEX some_index_name ON queries_query (user_sid, created)
WHERE NOT archived;
Přidejte do svých dotazů odpovídající podmínku (i když se zdá být nadbytečná), abyste jí umožnili používat index. Zkontrolujte pomocí EXPLAIN ANALYZE
zda se plánovač dotazů chytí - měl by být schopen použít index pro dotazy k novějšímu datu. Nepochopí však složitější podmínky, které se přesně neshodují.
Nemusíte rušit a znovu vytvářet index, ale UPDATE
na stole může být dražší než indexová rekreace a stůl se o něco zvětší.
Šel bych s prvním možnost (index rekreace). Ve skutečnosti toto řešení používám v několika databázích. Druhý způsobí nákladnější aktualizace.
Obě řešení si zachovávají svou užitečnost v průběhu času, výkon se pomalu zhoršuje, jak jsou do indexu zahrnuty zastaralejší řádky.