PostgreSQL ve skutečnosti podporuje indexy GIN na sloupcích pole. Bohužel se nezdá být použitelný pro NOT ARRAY[...] <@ indexed_col
a GIN
indexy jsou stejně nevhodné pro často aktualizované tabulky.
Demo:
CREATE TABLE arrtable (id integer primary key, array_column integer[]);
INSERT INTO arrtable(1, ARRAY[1,2,3,4]);
CREATE INDEX arrtable_arraycolumn_gin_arr_idx
ON arrtable USING GIN(array_column);
-- Use the following *only* for testing whether Pg can use an index
-- Do not use it in production.
SET enable_seqscan = off;
explain (buffers, analyze) select count(id)
from arrtable
where not (ARRAY[1] <@ arrtable.array_column);
Bohužel to ukazuje, že tak, jak je napsáno, nemůžeme index použít. Pokud podmínku nezrušíte, lze ji použít, abyste mohli vyhledávat a počítat řádky, které dělají obsahovat vyhledávací prvek (odstraněním NOT
).
Index můžete použít k počítání položek, které dělají obsahovat cílovou hodnotu, pak výsledek odečíst od počtu všech položek. Od count
Zadání všech řádků v tabulce je v PostgreSQL (9.1 a starší) poměrně pomalé a vyžaduje sekvenční skenování, které bude ve skutečnosti pomalejší než váš aktuální dotaz. Je možné, že ve verzi 9.2 lze k počítání řádků použít pouze indexové skenování, pokud máte index b-stromu na id
, v takovém případě to může být ve skutečnosti v pořádku:
SELECT (
SELECT count(id) FROM arrtable
) - (
SELECT count(id) FROM arrtable
WHERE (ARRAY[1] <@ arrtable.array_column)
);
Je zaručeno, že bude fungovat hůře než vaše původní verze pro Pg 9.1 a nižší, protože kromě seqscan to váš originál vyžaduje také potřebuje skenování indexu GIN. Nyní jsem to testoval na 9.2 a zdá se, že používá index pro počítání, takže stojí za to prozkoumat 9.2. S některými méně triviálními fiktivními daty:
drop index arrtable_arraycolumn_gin_arr_idx ;
truncate table arrtable;
insert into arrtable (id, array_column)
select s, ARRAY[1,2,s,s*2,s*3,s/2,s/4] FROM generate_series(1,1000000) s;
CREATE INDEX arrtable_arraycolumn_gin_arr_idx
ON arrtable USING GIN(array_column);
Všimněte si, že index GIN, jako je tento, velmi zpomalí aktualizace a v první řadě se poměrně pomalu vytváří. Není vhodný pro tabulky, které jsou příliš aktualizovány – jako je váš stůl.
Horší je, že dotaz pomocí tohoto indexu trvá až dvakrát déle než původní dotaz a v nejlepším případě poloviční na stejném datovém souboru. Nejhorší je to v případech, kdy index není příliš selektivní, jako je ARRAY[1]
- 4s vs 2s pro původní dotaz. Kde je index vysoce selektivní (tj.:málo shod, jako ARRAY[199]
) běží asi za 1,2 sekundy oproti původním 3 s. Tento index pro tento dotaz prostě nestojí za to.
Lekce zde? Někdy je správnou odpovědí jen provést sekvenční skenování.
Vzhledem k tomu, že to pro vaši míru návštěvnosti nebude stačit, buď udržujte materializované zobrazení se spouštěčem, jak navrhuje @debenhur, nebo zkuste pole převrátit tak, aby bylo seznamem parametrů, které záznam nedělá mít, takže můžete použít index GiST, jak navrhuje @maniek.