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 data
est 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 data
par 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 serial
colonne 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 data
serait alors de type int[]
. J'ai renommé la table data
et 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 data
fait 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 integer
au 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_id
dans 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.
data
contienne 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.