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

Optimalizace dotazu na počet pro PostgreSQL

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.



  1. MySQL Connector C++ 64bit sestavení ze zdroje ve Visual Studiu 2012

  2. Existuje nějaký rozdíl mezi DATE_SUB() a použitím aritmetických operátorů pro výpočet data a času?

  3. Nelze se připojit k MySQL Workbench na Macu. Nelze se připojit k serveru MySQL na '127.0.0.1' (61) Mac Macintosh

  4. Spojte dva stoly (se vztahem 1-M), kde je třeba druhý stůl „srovnat“ do jednoho řádku