PostgreSQL unterstützt tatsächlich GIN-Indizes für Array-Spalten. Unglücklicherweise scheint es für NOT ARRAY[...] <@ indexed_col
nicht verwendbar zu sein, und GIN
Indizes sind ohnehin für häufig aktualisierte Tabellen ungeeignet.
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);
Leider ist dies zeigt, dass geschrieben wurden, können wir den Index nicht nutzen. Wenn Sie die Bedingung nicht negieren, kann sie verwendet werden, sodass Sie nach Zeilen suchen und diese zählen können, die do das Suchelement enthalten (durch Entfernen von NOT
).
Sie können den Index verwenden, um Einträge zu zählen, die do den Zielwert enthalten, dann subtrahieren Sie dieses Ergebnis von einer Anzahl aller Einträge. Da count
alle Zeilen in einer Tabelle in PostgreSQL (9.1 und älter) sehr langsam sind und einen sequenziellen Scan erfordern, ist dies tatsächlich langsamer als Ihre aktuelle Abfrage. Es ist möglich, dass auf 9.2 ein Index-Only-Scan verwendet werden kann, um die Zeilen zu zählen, wenn Sie einen B-Baum-Index auf id
haben, in welchem Fall der eigentlich in Ordnung sein könnte:
SELECT (
SELECT count(id) FROM arrtable
) - (
SELECT count(id) FROM arrtable
WHERE (ARRAY[1] <@ arrtable.array_column)
);
Es garantiert werden schlechter abschneiden als Ihre Originalversion für Pg 9.1 und darunter, weil zusätzlich zu der Seqscan Ihr Original benötigt es auch braucht eine GIN-Index-Scan. Ich habe es jetzt auf 9.2 getestet und es scheint, dass es einen Index für die Zählung verwendet, also lohnt es sich, nach 9.2 zu suchen. Mit etwas weniger trivial Dummy-Daten:
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);
Beachten Sie, dass ein GIN Index wie dieser Updates verlangsamen wird viel nach unten, und ist ziemlich langsam in erster Linie zu erstellen. Es ist nicht geeignet für Tabellen, die viel aktualisiert werden - wie Ihr Tisch.
Schlimmer, Die Abfrage mit diesem Index dauert bis zu zweimal so lange wie Ihre ursprüngliche Abfrage und bestenfalls halb so lang auf demselben Datensatz. Es ist am schlimmsten für Fälle, in denen der Index nicht sehr selektiv ist wie ARRAY[1]
- 4s vs 2s für die ursprüngliche Abfrage. Wenn der Index sehr selektiv ist (dh nicht viele Übereinstimmungen, wie ARRAY[199]
), läuft er in etwa 1,2 Sekunden im Vergleich zu den 3s des Originals. Dieser Index ist für diese Abfrage einfach nicht wert.
Die Lektion hier? Manchmal ist die richtige Antwort nur ein sequentieller Scan.
Da dies für Ihre Trefferquoten nicht, entweder eine materialisierte Ansicht mit einem Trigger halten, wie @debenhur vermuten läßt, oder versuchen, das Array zu invertieren eine Liste von Parametern zu sein, dass der Eintrag nicht hat Du so kann einen GiST-Index verwenden, wie @maniek es vorschlägt.
Nicht sicher, aber ich denke ein GIN-Index auf table.array_column wird helfen, dies zu beschleunigen. Sie müssen EXPLAIN ausführen, um das herauszufinden. Siehe hier: http://dba.stackexchange.com/a/27505/1822 –
Es wird schwierig sein, dies effizient in Postgres zu machen, da der Tisch groß wird. Ein GIN-Index hilft nur beim Testen auf "enthalten in" im Gegensatz zu "nicht enthalten in" in Ihrem Prädikat. Wenn es nicht entscheidend ist, dass die Anzahl 100% genau ist, könnten Sie versuchen, sie mit einem TTL auf der App-Ebene zwischenzuspeichern. Wenn Ihre Schreibrate in der Tabelle nicht zu hoch ist, können Sie sinnvollerweise Trigger verwenden, um eine andere Tabelle mit aktuellen Zählwerten zu aktualisieren. – dbenhur
Am Besten, um Ihre Version zu zeigen und 'explain analyze' zu erklären; siehe http://stackoverflow.com/tags/postgresql-performance/info –