Il problema
Ecco un caso molto simile discusso su pgsql.general . Riguarda la limitazione in un indice b-tree, ma è lo stesso perché un indice GIN utilizza un indice b-tree per le chiavi internamente e quindi si imbatte nella stessa limitazione per la dimensione della chiave (invece della dimensione dell'elemento in un semplice b-tree indice).
Cito il manuale sull'implementazione dell'indice GIN :
Internamente, un indice GIN contiene un indice B-tree costruito su chiavi, dove ogni chiave è un elemento di uno o più elementi indicizzati
Ad ogni modo, almeno un elemento dell'array nella colonna data
è troppo grande per essere indicizzato. Se questo è solo un singolare valore anomalo o un qualche tipo di incidente, potresti essere in grado di troncare il valore e finirlo.
Ai fini della seguente demo, suppongo diversamente: molti valori di testo lunghi nella matrice.
Soluzione semplice
È possibile sostituire elementi nell'array data
con valori hash corrispondenti . E invia valori di ricerca tramite la stessa funzione hash. Ovviamente, probabilmente vorrai conservare gli originali in qualche luogo. Detto questo, arriviamo quasi alla mia seconda variante ...
Soluzione avanzata
È possibile creare una tabella di ricerca per gli elementi dell'array con una serial
colonna come chiave primaria surrogata (in effetti un tipo radicale di valore hash), il che è tanto più interessante se i valori degli elementi coinvolti non sono univoci:
CREATE TABLE elem (
elem_id serial NOT NULL PRIMARY KEY
, elem text UNIQUE NOT NULL
);
Dato che vogliamo cercare elem
, aggiungiamo un indice, ma un indice su un'espressione questa volta, con solo i primi 10 caratteri del testo lungo. Questo dovrebbe essere sufficiente nella maggior parte dei casi per restringere la ricerca a uno o più risultati. Adatta le dimensioni alla tua distribuzione di dati. Oppure usa una funzione hash più sofisticata.
CREATE INDEX elem_elem_left10_idx ON elem(left(elem,10));
La tua colonna data
sarebbe quindi di tipo int[]
. Ho rinominato il tavolo data
e mi sono sbarazzato del sinistro che varchar(50)
hai avuto nel tuo esempio:
CREATE TEMP TABLE data(
data_id serial PRIMARY KEY
, data int[]
);
Ogni elemento dell'array in si data
riferisce a elem.elem_id
. A questo punto, potresti considerare di sostituire la colonna dell'array con una tabella n: m, normalizzando così il tuo schema e permettendo a Postgres di imporre l'integrità referenziale. L'indicizzazione e la gestione generale diventano più facili ...
Tuttavia, per motivi di prestazioni, la int[]
colonna in combinazione con un indice GIN può essere superiore. Le dimensioni di archiviazione sono molto più ridotte. In questo caso abbiamo bisogno dell'indice GIN:
CREATE INDEX data_data_gin_idx ON data USING GIN (data);
Ora, ogni chiave dell'indice GIN (= elemento array) è un integer
anziché un longish text
. L'indice sarà più piccolo di diversi ordini di grandezza, di conseguenza le ricerche saranno molto più veloci.
Il rovescio della medaglia: prima di poter effettivamente eseguire una ricerca è necessario cercare elem_id
nella tabella elem
. Utilizzando il mio indice funzionale appena introdotto elem_elem_left10_idx
, anche questo sarà molto più veloce.
Puoi fare tutto in una semplice query :
SELECT d.*, e.*
FROM elem e
JOIN data d ON ARRAY[e.elem_id] <@ d.data
WHERE left(e.elem, 10) = left('word1234word', 10) -- match index condition
AND e.elem = 'word1234word'; -- need to recheck, functional index is lossy
Potresti essere interessato all'estensione intarray
, che fornisce ulteriori operatori e classi di operatori.
data
contiene un elenco di tag come dimostrato in questo post di blog correlato di Scott Snyder ? In tal caso, potrei avere una soluzione migliore per te.