sql >> Databáze >  >> RDS >> PostgreSQL

Přidejte omezení datetime do vícesloupcového částečného indexu PostgreSQL

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.




  1. Pokročilé připojení k MySQL. Zrychlení dotazu

  2. SQL:Akumulativní sloupce a součet napříč řádkem

  3. Proč se můj float ukládá v MYSQL jako 0,9999, když je větší než 1?

  4. Dotaz se vrátil jako logický?