Otrzymasz wyjątek za pomocą now()
ponieważ funkcja nie jest IMMUTABLE
(oczywiście) i cytując podręcznik
:
Widzę dwa sposoby wykorzystania (znacznie wydajniejszego) indeksu częściowego:
1. Indeks częściowy z warunkiem przy użyciu stałej data:
CREATE INDEX queries_recent_idx ON queries_query (user_sid, created)
WHERE created > '2013-01-07 00:00'::timestamp;
Zakładając created
jest faktycznie zdefiniowany jako timestamp
. Nie działałoby dostarczenie timestamp
stała dla timestamp
kolumna (timestamp with time zone
). Obsada z timestamp
do timestamp
(lub odwrotnie) zależy od aktualnego ustawienia strefy czasowej i nie jest niezmienne . Użyj stałej pasującego typu danych. Zapoznaj się z podstawami znaczników czasu ze strefą czasową / bez:
Upuść i utwórz ponownie ten indeks w godzinach o małym natężeniu ruchu, może z pracą crona na co dzień lub co tydzień (lub cokolwiek, co jest dla ciebie wystarczające). Tworzenie indeksu jest dość szybkie, zwłaszcza częściowy indeks, który jest stosunkowo mały. To rozwiązanie również nie musi niczego dodawać do tabeli.
Zakładając brak równoczesnego dostępu do tabeli, automatyczne odtwarzanie indeksu można wykonać za pomocą funkcji takiej jak ta:
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$;
Zadzwoń:
SELECT f_index_recreate();
now()
(tak jak ty) jest odpowiednikiem CURRENT_TIMESTAMP
i zwraca timestamptz
. Przesyłaj do timestamp
z now()::timestamp
lub użyj LOCALTIMESTAMP
zamiast tego.
db<>fiddle tutaj
Stary sqlfiddle
Jeśli masz do czynienia z dostępem równoczesnym do tabeli użyj DROP INDEX CONCURRENTLY
i CREATE INDEX CONCURRENTLY
. Ale nie możesz zawinąć tych poleceń w funkcję, ponieważ według dokumentacji
:
Tak więc z dwiema oddzielnymi transakcjami :
CREATE INDEX CONCURRENTLY queries_recent_idx2 ON queries_query (user_sid, created)
WHERE created > '2013-01-07 00:00'::timestamp; -- your new condition
Następnie:
DROP INDEX CONCURRENTLY IF EXISTS queries_recent_idx;
Opcjonalnie zmień nazwę na starą:
ALTER INDEX queries_recent_idx2 RENAME TO queries_recent_idx;
2. Częściowy indeks z warunkiem na tagu „zarchiwizowany”
Dodaj archived
tag do tabeli:
ALTER queries_query ADD COLUMN archived boolean NOT NULL DEFAULT FALSE;
UPDATE
kolumnę w wybranych przez Ciebie odstępach, aby „wycofać” starsze wiersze i utworzyć indeks, taki jak:
CREATE INDEX some_index_name ON queries_query (user_sid, created)
WHERE NOT archived;
Dodaj pasujący warunek do zapytań (nawet jeśli wydaje się to zbędne), aby umożliwić mu użycie indeksu. Sprawdź za pomocą EXPLAIN ANALYZE
czy planer zapytań się łapie - powinien umieć używać indeksu dla zapytań w nowszym terminie. Ale nie zrozumie bardziej złożonych warunków, które nie pasują dokładnie.
Nie musisz upuszczać i ponownie tworzyć indeksu, ale UPDATE
na stole może być droższe niż odtwarzanie indeksu, a stół staje się nieco większy.
Poszedłbym z pierwszym opcja (rekreacja indeksu). W rzeczywistości używam tego rozwiązania w kilku bazach danych. Drugi wiąże się z bardziej kosztownymi aktualizacjami.
Oba rozwiązania zachowują swoją użyteczność w czasie, wydajność powoli się pogarsza, ponieważ w indeksie znajdują się bardziej przestarzałe wiersze.