Errore dimensione massima riga indice


12

C'è un limite superiore per una arraycolonna?

Ricevo questo errore durante l'inserimento nel campo dell'array -

PG::Error: ERROR:  index row size 3480 exceeds maximum 2712 for index "ix_data"

Ecco la mia definizione di tabella -

create table test_array(id varchar(50), data text[]);

ALTER TABLE test_array ADD PRIMARY KEY (id);

CREATE INDEX ix_data ON test_array USING GIN (data);

Ho bisogno di un indice sul campo dell'array, poiché sto facendo delle ricerche su di esso.


Potrebbe essere che datacontiene 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.
Erwin Brandstetter,

user310525, vorrei secondare il suggerimento di Erwin che questo sarebbe meglio su dba.se, se sei disposto a creare un account lì e segnalare la migrazione di un moderatore?
Jack dice di provare topanswers.xyz il

Risposte:


14

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 datacon 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 serialcolonna 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 datasarebbe quindi di tipo int[]. Ho rinominato il tavolo datae 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 datariferisce 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 integeranziché 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_idnella 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.

Demo live completamente funzionale su sqlfiddle.


2

L'errore è con l'indice ix_data, non con il text[]campo. La dimensione massima per una riga in quel particolare tipo di indice è limitata ai 2712byte. Se elimini l'indice e riprovi a inserire, dovrebbe funzionare per te. Se hai bisogno di indicizzare un campo più grande, potresti voler esaminare le funzionalità di indicizzazione del testo completo di postgres.


2

Lo stavo ottenendo su una colonna geografica Geografia PostGIS. È perché ho accidentalmente creato l'indice in modo errato. È necessario includere il parametro USING GIST durante la creazione di tali indici.


Grazie - era quello! Wow, finora recuperato. Potrebbe avermi risparmiato ore. Soprattutto da quando pensavo che GiST fosse usato di default ma mi sbagliavo e cercava di usare b-tree.
Jonas,
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.