Können PostgreSQL-Index-Array-Spalten?

144

Ich kann in der Dokumentation keine eindeutige Antwort auf diese Frage finden. Wenn eine Spalte ein Array-Typ ist, werden alle eingegebenen Werte einzeln indiziert?

Ich habe eine einfache Tabelle mit einer int[]Spalte erstellt und einen eindeutigen Index darauf gesetzt. Mir ist aufgefallen, dass ich nicht dasselbe Ints-Array hinzufügen konnte, was mich zu der Annahme führt, dass der Index aus den Array-Elementen zusammengesetzt ist und nicht aus jedem Index.

INSERT INTO "Test"."Test" VALUES ('{10, 15, 20}');
INSERT INTO "Test"."Test" VALUES ('{10, 20, 30}');

SELECT * FROM "Test"."Test" WHERE 20 = ANY ("Column1");

Hilft der Index bei dieser Abfrage?

IamIC
quelle
Ist es möglich, den Datentyp jsonbund die Indizes zu verwenden? postgresql.org/docs/9.5/static/functions-json.html und postgresql.org/docs/9.5/static/datatype-json.html#JSON-INDEXING
user3791372

Antworten:

179

Ja, Sie können ein Array indizieren, müssen jedoch die Array-Operatoren und den GIN-Indextyp verwenden .

Beispiel:

    CREATE TABLE "Test"("Column1" int[]);
    INSERT INTO "Test" VALUES ('{10, 15, 20}');
    INSERT INTO "Test" VALUES ('{10, 20, 30}');

    CREATE INDEX idx_test on "Test" USING GIN ("Column1");

    -- To enforce index usage because we have only 2 records for this test... 
    SET enable_seqscan TO off;

    EXPLAIN ANALYZE
    SELECT * FROM "Test" WHERE "Column1" @> ARRAY[20];

Ergebnis:

Bitmap Heap Scan on "Test"  (cost=4.26..8.27 rows=1 width=32) (actual time=0.014..0.015 rows=2 loops=1)
  Recheck Cond: ("Column1" @> '{20}'::integer[])
  ->  Bitmap Index Scan on idx_test  (cost=0.00..4.26 rows=1 width=0) (actual time=0.009..0.009 rows=2 loops=1)
        Index Cond: ("Column1" @> '{20}'::integer[])
Total runtime: 0.062 ms
Hinweis

In vielen Fällen scheint die Option gin__int_ops erforderlich zu sein

create index <index_name> on <table_name> using GIN (<column> gin__int_ops)

Ich habe noch keinen Fall gesehen, in dem es mit den Operatoren && und @> ohne die Optionen gin__int_ops funktionieren würde

Frank Heikens
quelle
19
Wie das OP vermutet, indiziert dies nicht einzelne Array-Werte, sondern das gesamte Array. Dies hilft zwar der fraglichen Abfrage (siehe Plan erläutern), bedeutet jedoch, dass Sie keine eindeutigen Einschränkungen (einfach) für einzelne Array-Werte erstellen können. Wenn Sie jedoch ganzzahlige Arrays verwenden, können Sie das Contrib-Modul "intarray" verwenden, um einzelne Array-Werte zu indizieren, was in vielen Fällen viel schneller sein kann. (IIRC: Es werden einige Arbeiten für Textwerte durchgeführt, aber Mitwirkende wären wahrscheinlich willkommen, um das Problem zu lösen.)
Xzilla
15
Bitte verwenden Sie keine Großbuchstaben in PostgreSQL-Bezeichnern in Codebeispielen. Dies verwirrt nur Personen, die mit den Anführungszeichen- / Groß- / Kleinschreibregeln nicht vertraut sind, insbesondere Personen, die PostgreSQL noch nicht kennen.
Intgr
6
Um meinen Kommentar hier zu wiederholen: Nach meiner Erfahrung bieten diese Indizes nur eine geringe bis keine Beschleunigung, es gin__int_ops sei denn, sie werden für integer[]Spalten verwendet. Es dauerte Jahre der Frustration und der Suche nach anderen Lösungen, bis ich diese Op-Klasse entdeckte. Es ist ein Borderline-Wundertäter.
IamIC
1
@IamIC bedeutet das, dass ich mich nicht die Mühe machen sollte, ein Array von Strings zu indizieren? Und ich sollte nur Integer-Arrays indizieren?
Ryan2Johnson9
93

@Tregoreg stellte im Kommentar zu seinem angebotenen Kopfgeld eine Frage :

Ich fand die aktuellen Antworten nicht funktionierend. Die Verwendung des GIN-Index für eine Spalte vom Typ Array erhöht die Leistung des Operators ANY () nicht. Gibt es wirklich keine Lösung?

@ Frank ist akzeptiert Antwort fordert Sie auf,Array-Operatoren zu verwenden, wasfür Postgres 11immernoch korrekt ist.Das Handbuch:

... die Standarddistribution von PostgreSQL enthält eine GIN-Operatorklasse für Arrays, die indizierte Abfragen mit diesen Operatoren unterstützt:

<@
@>
=
&&

Die vollständige Liste der integrierten Operatorklassen für GIN-Indizes in der Standardverteilung finden Sie hier.

In Postgres sind Indizes an Operatoren gebunden (die für bestimmte Typen implementiert sind), nicht nur an Datentypen oder Funktionen oder irgendetwas anderes. Das ist ein Erbe des ursprünglichen Berkeley-Designs von Postgres und jetzt sehr schwer zu ändern. Und es funktioniert im Allgemeinen gut. Hier ist ein Thread zu pgsql-Bugs, in dem Tom Lane dies kommentiert.

Einige PostGis Funktionen (wie ST_DWithin()) scheinen gegen dieses zu verstoßen, aber das ist nicht so. Diese Funktionen werden intern neu geschrieben, um die jeweiligen Operatoren zu verwenden .

Der indizierte Ausdruck muss sich links vom Operator befinden. Für die meisten Operatoren ( einschließlich aller oben genannten ) kann der Abfrageplaner dies erreichen, indem er Operanden umdreht, wenn Sie den indizierten Ausdruck rechts platzieren - vorausgesetzt, a COMMUTATORwurde definiert. DasANY Konstrukt kann in Kombination mit verschiedenen Operatoren verwendet werden und ist selbst kein Operator. Bei Verwendung als constant = ANY (array_expression)nur Indizes, die den =Operator für Array-Elemente unterstützen, würde sich qualifizieren und wir würden einen Kommutator für benötigen = ANY(). GIN-Indizes sind out.

Postgres ist derzeit nicht intelligent genug, um daraus einen GIN-indizierbaren Ausdruck abzuleiten. Für den Anfang constant = ANY (array_expression)ist nicht ganz gleichbedeutend mit array_expression @> ARRAY[constant]. Array-Operatoren geben einen Fehler zurück, wenn NULL- Elemente beteiligt sind, während das ANYKonstrukt auf beiden Seiten mit NULL umgehen kann. Und es gibt unterschiedliche Ergebnisse für Datentyp-Fehlanpassungen.

Verwandte Antworten:

Nebenbei

Berücksichtigen Sie bei der Arbeit mit integerArrays ( int4, nicht int2oder int8) ohne NULLWerte (wie in Ihrem Beispiel angegeben) das zusätzliche Modul intarray, das spezialisierte, schnellere Operatoren und Indexunterstützung bietet. Sehen:

Was die UNIQUEEinschränkung in Ihrer Frage betrifft, die unbeantwortet blieb: Diese wird mit einem btree-Index für den gesamten Array- Wert implementiert (wie Sie vermutet haben) und hilft bei der Suche nach Elementen überhaupt nicht. Einzelheiten:

Erwin Brandstetter
quelle
1
Aaaaaaah, ich fühle mich im Moment ziemlich verlegen, aber es kam mir einfach nicht in den Sinn, dass Postgres den Index nicht verwenden würden, selbst wenn dies theoretisch möglich wäre. Vielleicht liegt es auch daran, dass ich keinen Einblick in Postgres habe, zum Beispiel, dass Indizes an Operatoren gebunden sind. Vielen Dank, dass Sie sich die Zeit genommen haben, meine schlecht gestellte Frage zu beantworten und Ihr Wissen zu teilen!
Tregoreg
6
@ Regoreg: Sei nicht zu verlegen, es ist wirklich nicht zu offensichtlich. Ich erinnere mich, dass ich selbst verwirrt war, als ich das erste Mal darauf stieß. Die hinzugefügte Frage und Klarstellung sollte für die breite Öffentlichkeit sehr nützlich sein.
Erwin Brandstetter
1
Nach meiner Erfahrung bieten diese Indizes nur eine geringe bis keine Beschleunigung, es gin__int_ops sei denn, sie werden für integer[]Spalten verwendet. Es dauerte Jahre der Frustration und der Suche nach anderen Lösungen, bis ich diese Op-Klasse entdeckte. Es ist ein Borderline-Wundertäter.
IamIC
2
@IamIC: Ich habe Zeiger auf Intarray hinzugefügt. Scheint bemerkenswert, wie Sie betont haben.
Erwin Brandstetter
Für ANY (array_expression) = constantAusdrücke funktionieren GIN-Indizes einwandfrei?
user10375
37

Es ist jetzt möglich, die einzelnen Array-Elemente zu indizieren. Beispielsweise:

CREATE TABLE test (foo int[]);
INSERT INTO test VALUES ('{1,2,3}');
INSERT INTO test VALUES ('{4,5,6}');
CREATE INDEX test_index on test ((foo[1]));
SET enable_seqscan TO off;

EXPLAIN ANALYZE SELECT * from test WHERE foo[1]=1;
                                                QUERY PLAN                                                    
------------------------------------------------------------------------------------------------------------------
 Index Scan using test_index on test  (cost=0.00..8.27 rows=1 width=32) (actual   time=0.070..0.071 rows=1 loops=1)
   Index Cond: (foo[1] = 1)
 Total runtime: 0.112 ms
(3 rows)

Dies funktioniert mindestens auf Postgres 9.2.1. Beachten Sie, dass Sie für jeden Array-Index einen eigenen Index erstellen müssen. In meinem Beispiel habe ich nur das erste Element indiziert.

Ed4
quelle
28
Lassen Sie es nicht verloren gehen - dieser Ansatz ist für Arrays mit variabler Länge, bei denen Sie den Operator ANY () verwenden möchten, hoffnungslos.
Καrτhικ
24
Das ist wirklich nicht sehr nützlich. Wenn Sie eine feste Anzahl von Array-Elementen haben, verwenden Sie lieber einzelne Spalten für jedes Element (und einfache Btree-Indizes), anstatt für jedes Array-Element einen teureren Ausdrucksindex zu erstellen. Die Speicherung einzelner Spalten ist auch ohne Array-Overhead viel billiger.
Erwin Brandstetter