El problema
Aquí hay un caso muy similar discutido en pgsql.general . Se trata de la limitación en un índice b-tree, pero es todo lo mismo porque un índice GIN usa un índice b-tree para las claves internamente y, por lo tanto, se encuentra con la misma limitación para el tamaño de la clave (en lugar del tamaño del elemento en un b-tree simple índice).
Cito el manual sobre la implementación del índice GIN :
Internamente, un índice GIN contiene un índice de árbol B construido sobre claves, donde cada clave es un elemento de uno o más elementos indexados
De cualquier manera, al menos un elemento de matriz en su columna data
es demasiado grande para ser indexado. Si esto es solo un valor anormal singular o algún tipo de accidente, es posible que pueda truncar el valor y acabar de una vez.
Para el propósito de la siguiente demostración, supondré lo contrario: muchos valores de texto largos en la matriz.
Solución simple
Puede reemplazar elementos en su matriz data
con los valores hash correspondientes . Y envíe valores de búsqueda a través de la misma función hash. Por supuesto, es probable que desee almacenar sus originales además en algún lugar. Con eso, casi llegamos a mi segunda variante ...
Solución avanzada
Podría crear una tabla de búsqueda para elementos de matriz con una serial
columna como clave primaria sustituta (efectivamente, un tipo radical de valor hash), que es aún más interesante si los valores de los elementos involucrados no son únicos:
CREATE TABLE elem (
elem_id serial NOT NULL PRIMARY KEY
, elem text UNIQUE NOT NULL
);
Como deseamos buscar elem
, agregamos un índice, pero esta vez un índice en una expresión , con solo los primeros 10 caracteres del texto largo. Eso debería ser suficiente en la mayoría de los casos para limitar una búsqueda a uno o unos pocos resultados. Adapte el tamaño a su distribución de datos. O use una función hash más sofisticada.
CREATE INDEX elem_elem_left10_idx ON elem(left(elem,10));
Su columna data
sería entonces de tipo int[]
. Cambié el nombre de la tabla data
y me deshice de lo ominoso varchar(50)
que tenía en su ejemplo:
CREATE TEMP TABLE data(
data_id serial PRIMARY KEY
, data int[]
);
Cada elemento de matriz se data
refiere a a elem.elem_id
. En este punto, puede considerar reemplazar la columna de matriz con una tabla n: m, normalizando así su esquema y permitiendo que Postgres imponga integridad referencial. La indexación y el manejo general se vuelven más fáciles ...
Sin embargo, por razones de rendimiento, la int[]
columna en combinación con un índice GIN puede ser superior. El tamaño de almacenamiento es mucho más pequeño. En este caso, necesitamos el índice GIN:
CREATE INDEX data_data_gin_idx ON data USING GIN (data);
Ahora, cada clave del índice GIN (= elemento de matriz) es un en integer
lugar de un largo text
. El índice será más pequeño en varios órdenes de magnitud, por lo tanto, las búsquedas serán mucho más rápidas.
La desventaja: antes de poder realizar una búsqueda, debe buscar en elem_id
la tabla elem
. Usando mi índice funcional recién introducido elem_elem_left10_idx
, esto también será mucho más rápido.
Puede hacerlo todo en una consulta simple :
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
Puede interesarle la extensión intarray
, que proporciona operadores adicionales y clases de operadores.
data
contiene una lista de etiquetas como las demostradas en esta publicación de blog relacionada de Scott Snyder ? Si ese es el caso, podría tener una mejor solución para usted.