PostgreSQL può indicizzare le colonne di array?


144

Non riesco a trovare una risposta definitiva a questa domanda nella documentazione. Se una colonna è di tipo array, tutti i valori immessi verranno indicizzati singolarmente?

Ho creato una tabella semplice con una int[]colonna e vi ho inserito un indice univoco. Ho notato che non potevo aggiungere lo stesso array di ints, il che mi porta a credere che l'indice sia un composto degli elementi dell'array, non un indice di ciascun elemento.

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

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

L'indice aiuta questa query?


Risposte:


181

Sì, puoi indicizzare un array, ma devi utilizzare gli operatori di array e il tipo di indice GIN .

Esempio:

    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];

Risultato:

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
Nota

sembra che in molti casi sia richiesta l'opzione gin__int_ops

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

Non ho ancora visto un caso in cui funzionerebbe con l'operatore && e @> senza le opzioni gin__int_ops


19
Come ipotizza l'OP, questo non indicizza effettivamente i singoli valori dell'array, ma invece l'indice dell'intero array. Pertanto, sebbene ciò possa aiutare la query in questione (vedere spiegazione del piano), ciò significa che non è possibile creare vincoli univoci (facilmente) su singoli valori di array. Ciò detto, se si utilizzano array di numeri interi, è possibile utilizzare il modulo contrib "intarray" per indicizzare i singoli valori di array, che possono essere molto più veloci in molti casi. (IIRC sta facendo del lavoro su questo per i valori del testo, ma i contributori sarebbero probabilmente i benvenuti per aiutarlo a finire).
Xzilla,

15
Si prega di non usare lettere maiuscole negli identificatori PostgreSQL negli esempi di codice, confonde solo le persone che non hanno familiarità con le regole di quotatura / piegatura dei casi, in particolare le persone che non conoscono PostgreSQL.
intgr,

6
Per ripetere il mio commento qui: dalla mia esperienza, questi indici offrono poco o nessun aumento di velocità a meno che non gin__int_ops venga utilizzato per le integer[]colonne. Mi ci sono voluti anni di frustrazione e cercare altre soluzioni prima di scoprire questa classe operativa. È un miracoloso borderline.
IamIC,

1
@IamIC significa che non dovrei preoccuparmi di indicizzare un array di stringhe? E dovrei solo indicizzare array di numeri interi?
ryan2johnson9,

93

@Tregoreg ha sollevato una domanda nel commento alla sua generosità offerta:

Non ho trovato le risposte attuali funzionanti. L'uso dell'indice GIN sulla colonna tipizzata da array non aumenta le prestazioni dell'operatore ANY (). Non c'è davvero nessuna soluzione?

@ La risposta accettata da Frank ti dice di usare gli operatori di array , che è ancora corretto per Postgres 11. Il manuale:

... la distribuzione standard di PostgreSQL include una classe operatore GIN per array, che supporta query indicizzate utilizzando questi operatori:

<@
@>
=
&&

L'elenco completo delle classi di operatori integrate per gli indici GIN nella distribuzione standard è qui.

In Postgres gli indici sono associati ad operatori (che sono implementati per determinati tipi), non solo a tipi di dati o funzioni o altro. Questa è un'eredità dal design originale Berkeley di Postgres e molto difficile da cambiare ora. E generalmente funziona bene. Ecco una discussione su pgsql-bugs con Tom Lane che commenta questo.

Alcune funzioni di PostGis (come ST_DWithin()) sembrano violare questo principio, ma non è così. Tali funzioni vengono riscritte internamente per utilizzare i rispettivi operatori .

L'espressione indicizzata deve essere alla sinistra dell'operatore. Per la maggior parte degli operatori ( incluso tutto quanto sopra ) il pianificatore di query può ottenere questo risultato capovolgendo gli operandi se si posiziona l'espressione indicizzata a destra, dato che a COMMUTATORè stato definito. Il ANYcostrutto può essere utilizzato in combinazione con vari operatori e non è un operatore stesso. Se utilizzato come constant = ANY (array_expression)solo indici che supportano l' =operatore sugli elementi dell'array, si qualificherebbe e avremmo bisogno di un commutatore = ANY(). Gli indici GIN sono fuori.

Postgres non è attualmente abbastanza intelligente da derivarne un'espressione indicizzabile GIN. Per cominciare, nonconstant = ANY (array_expression) è del tutto equivalente a array_expression @> ARRAY[constant]. Gli operatori di matrice restituiscono un errore se sono coinvolti elementi NULL , mentre il ANYcostrutto può gestire NULL su entrambi i lati. E ci sono risultati diversi per le discrepanze tra i tipi di dati.

Risposte correlate:

asides

Mentre si lavora con integerarray ( int4, non int2o int8) senza NULLvalori (come suggerisce l'esempio), prendere in considerazione il modulo aggiuntivo intarray, che fornisce agli operatori specializzati, più veloci e supporto agli indici. Vedere:

Per quanto riguarda il UNIQUEvincolo della tua domanda che è rimasto senza risposta: è implementato con un indice btree sull'intero valore dell'array (come sospettavi) e non aiuta affatto nella ricerca di elementi . Dettagli:


1
Aaaaaaah, mi sento abbastanza imbarazzato in questo momento, ma non mi è venuto in mente che Postgres non avrebbe usato l'indice, anche se teoricamente possibile. Forse è anche perché la mia mancanza di conoscenza dei postgres, come ad esempio che gli indici sono legati agli operatori. Grazie per aver dedicato del tempo a rispondere alla mia domanda mal posta e condividere le tue conoscenze!
Tregoreg,

6
@Tregoreg: non essere troppo imbarazzato, non è davvero troppo ovvio. Ricordo di essere stato confuso da me stesso quando l'ho incontrato per la prima volta. La domanda e il chiarimento aggiunti dovrebbero essere piuttosto utili al grande pubblico.
Erwin Brandstetter,

1
Dalla mia esperienza, questi indici offrono poco o nessun aumento di velocità a meno che non gin__int_ops venga utilizzato per le integer[]colonne. Mi ci sono voluti anni di frustrazione e cercare altre soluzioni prima di scoprire questa classe operativa. È un miracoloso borderline.
IamIC,

2
@IamIC: ho aggiunto i puntatori a Intarray. Sembra degno di nota, come hai sottolineato.
Erwin Brandstetter,

Per le ANY (array_expression) = constantespressioni, gli indici GIN funzionano bene?
user10375

37

Ora è possibile indicizzare i singoli elementi dell'array. Per esempio:

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)

Questo funziona almeno su Postgres 9.2.1. Si noti che è necessario creare un indice separato per ciascun indice dell'array, nel mio esempio ho indicizzato solo il primo elemento.


28
Non perderlo: questo approccio è senza speranza per array di lunghezza variabile in cui si desidera utilizzare l'operatore ANY ().
Καrτhικ,

24
Questo non è davvero molto utile. Se hai un numero fisso di elementi dell'array, preferiresti utilizzare singole colonne per ciascun elemento (e indici btree semplici) invece di creare un indice di espressione più costoso per ciascun elemento dell'array. La memorizzazione di singole colonne è molto più economica senza sovraccarico di array.
Erwin Brandstetter,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.