Erreur de taille maximale de la ligne d'index

12

Y a-t-il une limite supérieure pour une arraycolonne?

Je reçois cette erreur lors de l'insertion dans le champ du tableau -

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

Voici ma définition de table -

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

J'ai besoin d'un index sur le champ du tableau, car je fais des recherches dessus.


la source
Se pourrait-il qu'il datacontienne une liste de balises comme démontré dans ce billet de blog connexe de Scott Snyder ? Si tel est le cas, je pourrais avoir une meilleure solution pour vous.
Erwin Brandstetter
user310525, j'appuie la suggestion d'Erwin que ce serait mieux sur dba.se, si vous êtes prêt à créer un compte là-bas et à signaler la migration d'un modérateur?
Jack dit d'essayer topanswers.xyz le

Réponses:

14

Le problème

Voici un cas très similaire discuté sur pgsql.general . Il s'agit de la limitation dans un index b-tree, mais c'est la même chose car un index GIN utilise un index b-tree pour les clés en interne et se heurte donc à la même limitation pour la taille de la clé (au lieu de la taille de l' élément dans un b-tree ordinaire indice).

Je cite le manuel sur la mise en œuvre de l'index GIN :

En interne, un index GIN contient un index B-tree construit sur des clés, où chaque clé est un élément d'un ou plusieurs éléments indexés

Dans tous les cas, au moins un élément de tableau de votre colonne dataest trop grand pour être indexé. S'il ne s'agit que d'une valeur singulière ou d'un accident, vous pourrez peut-être tronquer la valeur et en finir avec elle.

Pour les besoins de la démo suivante, je vais supposer le contraire: beaucoup de longues valeurs de texte dans le tableau.

Solution simple

Vous pouvez remplacer les éléments de votre tableau datapar des valeurs de hachage correspondantes . Et envoyez des valeurs de recherche via la même fonction de hachage. Bien sûr, vous voudrez probablement stocker vos originaux en plus quelque part. Avec cela, nous arrivons presque à ma deuxième variante ...

Solution avancée

Vous pouvez créer une table de correspondance pour les éléments du tableau avec une serialcolonne comme clé primaire de substitution (en fait un type radical de valeur de hachage) - ce qui est d'autant plus intéressant si les valeurs des éléments impliqués ne sont pas uniques:

CREATE TABLE elem (
  elem_id serial NOT NULL PRIMARY KEY
, elem    text UNIQUE NOT NULL
);

Puisque nous voulons rechercher elem, nous ajoutons un index - mais un index sur une expression cette fois, avec seulement les 10 premiers caractères du texte long. Cela devrait suffire dans la plupart des cas pour limiter la recherche à un ou à quelques résultats. Adaptez la taille à votre distribution de données. Ou utilisez une fonction de hachage plus sophistiquée.

CREATE INDEX elem_elem_left10_idx ON elem(left(elem,10));

Votre colonne dataserait alors de type int[]. J'ai renommé la table dataet je me suis débarrassé du sinistre que varchar(50)vous aviez dans votre exemple:

CREATE TEMP TABLE data(
  data_id serial PRIMARY KEY
, data int[]
);

Chaque élément du tableau datafait référence à a elem.elem_id. À ce stade, vous pouvez envisager de remplacer la colonne du tableau par une table n: m, normalisant ainsi votre schéma et permettant à Postgres d'appliquer l'intégrité référentielle. L'indexation et la manipulation générale deviennent plus faciles ...

Cependant, pour des raisons de performances, la int[]colonne associée à un indice GIN peut être supérieure. La taille de stockage est beaucoup plus petite. Dans ce cas, nous avons besoin de l'indice GIN:

CREATE INDEX data_data_gin_idx ON data USING GIN (data);

Maintenant, chaque clé de l'index GIN (= élément de tableau) est un integerau lieu d'un longish text. L'indice sera plus petit de plusieurs ordres de grandeur, les recherches seront donc beaucoup plus rapides.

L'inconvénient: avant de pouvoir effectuer une recherche, vous devez rechercher le elem_iddans le tableau elem. En utilisant mon index fonctionnel nouvellement introduit elem_elem_left10_idx, cela aussi sera beaucoup plus rapide.

Vous pouvez tout faire en une seule requête :

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

Vous pouvez être intéressé par l'extension intarray, qui fournit des opérateurs et des classes d'opérateurs supplémentaires.

Démo en direct entièrement fonctionnelle sur sqlfiddle.

Erwin Brandstetter
la source
2

L'erreur concerne l'index ix_data, pas le text[]champ. La taille maximale d'une ligne dans ce type d'index particulier est limitée à des 2712octets. Si vous supprimez votre index et réessayez l'insertion, cela devrait fonctionner pour vous. Si vous devez indexer un champ plus grand, vous souhaiterez peut-être examiner les fonctionnalités d'indexation de texte intégral de postgres.

jcern
la source
2

J'obtenais ceci sur une colonne de géographie de PostGIS. C'est parce que j'ai accidentellement créé l'index de manière incorrecte. Vous devez inclure le paramètre USING GIST lors de la création de tels index.

Brad Mathews
la source
Merci - c'était tout! Wow, jusqu'ici tiré par les cheveux. Cela m'a peut-être fait gagner des heures. Surtout que je pensais que GiST était utilisé par défaut mais je me suis trompé et il essaie d'utiliser b-tree.
Jonas