it-swarm.com.de

Können PostgreSQL Array-Spalten indizieren?

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

Ich habe eine einfache Tabelle mit einem int[] Spalte, und legen Sie einen eindeutigen Index darauf. Mir ist aufgefallen, dass ich nicht das gleiche Array von Ints hinzufügen konnte, was mich glauben lässt, dass der Index eine Zusammenstellung der Array-Elemente und nicht ein Index für jedes Element ist.

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?

124
IamIC

Ja, Sie können ein Array indizieren, müssen jedoch die Operatoren Array und 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

es scheint, dass in vielen Fällen die Option gin__int_ops erforderlich ist

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

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

151
Frank Heikens

@Tregoreg hat eine Frage in dem Kommentar zu seiner angebotenen Prämie aufgeworfen:

Die aktuellen Antworten haben nicht funktioniert. Die Verwendung des GIN-Index für Array-typisierte Spalten erhöht die Leistung des ANY () -Operators nicht. Gibt es wirklich keine Lösung?

@ Frank akzeptiert answer weist Sie an, Array-Operatoren zu verwenden , das ist noch richtig für Postgres 11. Das Handbuch:

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

<@
@>
=
&&

Die vollständige Liste der integrierten Operator-Klassen für GIN-Indizes in der Standarddistribution finden Sie hier.

In Postgres sind Indizes an Operatoren gebunden (die für bestimmte Typen implementiert sind), nicht an Datentypen allein oder an Funktionen oder etwas anderem. Das ist ein Erbe aus dem ursprünglichen Berkeley-Design 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 Prinzip zu verstoßen, aber das ist nicht so. Diese Funktionen werden intern umgeschrieben, um die entsprechenden Operatoren zu verwenden.

Der indizierte Ausdruck muss sich am left des Operators befinden. Für die meisten Operatoren ( einschließlich aller oben genannten) kann der Abfrageplaner dies durch Spiegeln von Operanden erreichen, wenn Sie den indizierten Ausdruck nach rechts platzieren - vorausgesetzt, ein COMMUTATOR wurde definiert. Das ANY Konstrukt ​​kann in Kombination mit verschiedenen Operatoren verwendet werden und ist selbst kein Operator. Bei Verwendung als constant = ANY (array_expression) qualifizieren sich nur Indizes, die den Operator = Für Array-Elemente unterstützen, und wir benötigen einen Kommutator für = ANY(). GIN-Indizes sind out.

Postgres ist derzeit nicht intelligent genug, um daraus einen GIN-indexierbaren Ausdruck abzuleiten. Für den Anfang ist constant = ANY (array_expression) nicht vollständig äquivalent zu array_expression @> ARRAY[constant]. Array-Operatoren geben einen Fehler zurück, wenn NULL elements beteiligt ist, während das ANY -Konstrukt auf beiden Seiten mit NULL umgehen kann. Und es gibt unterschiedliche Ergebnisse für Datentypenkonflikte.

Verwandte Antworten:

Nebenbei

Bei der Arbeit mit integer Arrays (int4, Nicht int2 Oder int8) Ohne NULL Werte (wie in Ihrem Beispiel impliziert) berücksichtigen das zusätzliche Modul intarray , das spezielle, schnellere Operatoren und Indexunterstützung bietet. Sehen:

Bezüglich der UNIQUE -Einschränkung in Ihrer Frage, die unbeantwortet blieb: Diese wurde mit einem Btree-Index für den Wert gesamtes Array implementiert (wie Sie vermutet haben) und hilft nicht bei der Suche nach Elemente überhaupt. Einzelheiten:

79

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.

34
Ed4