¿Pueden las columnas de matriz de índice de PostgreSQL?

144

No puedo encontrar una respuesta definitiva a esta pregunta en la documentación. Si una columna es un tipo de matriz, ¿se indexarán individualmente todos los valores ingresados?

Creé una tabla simple con una int[]columna y le puse un índice único. Noté que no podía agregar la misma matriz de entradas, lo que me lleva a creer que el índice es una composición de los elementos de la matriz, no un índice de cada elemento.

INSERT INTO "Test"."Test" VALUES ('{10, 15, 20}');
INSERT INTO "Test"."Test" VALUES ('{10, 20, 30}');

SELECT * FROM "Test"."Test" WHERE 20 = ANY ("Column1");

¿El índice está ayudando a esta consulta?

IamIC
fuente
¿Es posible usar el tipo de datos jsonby los índices? postgresql.org/docs/9.5/static/functions-json.html y postgresql.org/docs/9.5/static/datatype-json.html#JSON-INDEXING
usuario3791372

Respuestas:

181

Sí, puede indexar una matriz, pero debe usar los operadores de matriz y el tipo de índice GIN .

Ejemplo:

    CREATE TABLE "Test"("Column1" int[]);
    INSERT INTO "Test" VALUES ('{10, 15, 20}');
    INSERT INTO "Test" VALUES ('{10, 20, 30}');

    CREATE INDEX idx_test on "Test" USING GIN ("Column1");

    -- To enforce index usage because we have only 2 records for this test... 
    SET enable_seqscan TO off;

    EXPLAIN ANALYZE
    SELECT * FROM "Test" WHERE "Column1" @> ARRAY[20];

Resultado:

Bitmap Heap Scan on "Test"  (cost=4.26..8.27 rows=1 width=32) (actual time=0.014..0.015 rows=2 loops=1)
  Recheck Cond: ("Column1" @> '{20}'::integer[])
  ->  Bitmap Index Scan on idx_test  (cost=0.00..4.26 rows=1 width=0) (actual time=0.009..0.009 rows=2 loops=1)
        Index Cond: ("Column1" @> '{20}'::integer[])
Total runtime: 0.062 ms
Nota

parece que en muchos casos se requiere la opción gin__int_ops

create index <index_name> on <table_name> using GIN (<column> gin__int_ops)

Todavía no he visto un caso en el que funcione con el operador && y @> sin las opciones gin__int_ops

Frank Heikens
fuente
19
Como el OP supone, esto en realidad no indexa los valores de una matriz individual, sino que indexa toda la matriz. Entonces, si bien esto ayudará a la consulta en cuestión (consulte el plan de explicación), esto significa que no puede crear restricciones únicas (fácilmente) en valores de matriz individuales. Dicho esto, si está utilizando matrices de enteros, puede usar el módulo de contribución "intarray" para indexar valores de matrices individuales, que pueden ser mucho más rápidos en muchos casos. (IIRC se está trabajando en esto para los valores de texto, pero los contribuyentes probablemente serán bienvenidos para ayudarlo a terminar).
xzilla
15
No utilice letras mayúsculas en los identificadores de PostgreSQL en los ejemplos de código, solo confunde a las personas que no están familiarizadas con las reglas de comillas / plegado de mayúsculas y minúsculas, en particular las personas nuevas en PostgreSQL.
intgr
66
Para repetir mi comentario aquí: desde mi experiencia, estos índices ofrecen poca o ninguna aceleración a menos que gin__int_ops se utilicen para integer[]columnas. Me llevó años de frustración y buscar otras soluciones hasta que descubrí esta clase operativa. Es un hacedor de milagros límite.
IamIC
1
@IamIC, ¿eso significa que no debería molestarme en indexar una serie de cadenas? ¿Y solo debería indexar matrices enteras?
ryan2johnson9
93

@Tregoreg planteó una pregunta en el comentario a su recompensa ofrecida:

No encontré las respuestas actuales funcionando. El uso del índice GIN en una columna de tipo matriz no aumenta el rendimiento de cualquier operador (). ¿Realmente no hay solución?

La respuesta aceptada de @ Frank le dice que use operadores de matriz , que todavía es correcto para Postgres 11. El manual:

... la distribución estándar de PostgreSQL incluye una clase de operador GIN para matrices, que admite consultas indexadas utilizando estos operadores:

<@
@>
=
&&

La lista completa de clases de operador integradas para índices GIN en la distribución estándar está aquí.

En Postgres, los índices están vinculados a operadores (que se implementan para ciertos tipos), no a tipos de datos solos o funciones o cualquier otra cosa. Esa es una herencia del diseño original de Berkeley de Postgres y muy difícil de cambiar ahora. Y generalmente funciona bien. Aquí hay un hilo sobre pgsql-bugs con Tom Lane comentando sobre esto.

Algunas funciones de PostGis (como ST_DWithin()) parecen violar este principio, pero eso no es así. Esas funciones se reescriben internamente para usar los operadores respectivos .

La expresión indexada debe estar a la izquierda del operador. Para la mayoría de los operadores ( incluido todo lo anterior ), el planificador de consultas puede lograr esto volteando los operandos si coloca la expresión indexada a la derecha, dado que a COMMUTATORse ha definido. La ANYconstrucción se puede usar en combinación con varios operadores y no es un operador en sí mismo. Cuando se usa como constant = ANY (array_expression)solo índices que soportan al =operador en elementos de matriz calificarían y necesitaríamos un conmutador para = ANY(). Los índices GIN están fuera.

Postgres actualmente no es lo suficientemente inteligente como para derivar una expresión indexable a GIN. Para empezar, noconstant = ANY (array_expression) es completamente equivalente a array_expression @> ARRAY[constant]. Los operadores de matriz devuelven un error si hay elementos NULL involucrados, mientras que la ANYconstrucción puede tratar con NULL en cualquier lado. Y hay resultados diferentes para los desajustes de tipo de datos

Respuestas relacionadas:

Aparte

Mientras trabaja con integermatrices ( int4, no int2o int8) sin NULLvalores (como su ejemplo lo indica) considere el módulo adicional intarray, que proporciona operadores especializados y más rápidos y soporte de índice. Ver:

En cuanto a la UNIQUErestricción en su pregunta que no recibió respuesta: se implementa con un índice btree en todo el valor de la matriz (como sospechaba) y no ayuda en absoluto en la búsqueda de elementos . Detalles:

Erwin Brandstetter
fuente
1
Aaaaaaah, me siento bastante avergonzado en este momento, pero no me vino a la mente que los postgres no usarían el índice incluso si teóricamente fuera posible. Tal vez también se deba a mi falta de conocimiento sobre los postgres, como que los índices están vinculados a los operadores. ¡Gracias por tomarse el tiempo para responder a mi pregunta mal formulada y compartir su conocimiento!
Tregoreg
66
@Tregoreg: No te avergüences demasiado, realmente no es demasiado obvio. Recuerdo estar confundido por esto mismo cuando me topé por primera vez. La pregunta y la aclaración agregadas deberían ser bastante útiles para el público en general.
Erwin Brandstetter
1
Desde mi experiencia, estos índices ofrecen poca o ninguna aceleración a menos que gin__int_ops se utilicen para integer[]columnas. Me llevó años de frustración y buscar otras soluciones hasta que descubrí esta clase operativa. Es un hacedor de milagros límite.
IamIC
2
@IamIC: agregué punteros a intarray. Parece notable, como usted señaló.
Erwin Brandstetter
Para las ANY (array_expression) = constantexpresiones, ¿funcionan bien los índices GIN?
user10375
37

Ahora es posible indexar los elementos de la matriz individual. Por ejemplo:

CREATE TABLE test (foo int[]);
INSERT INTO test VALUES ('{1,2,3}');
INSERT INTO test VALUES ('{4,5,6}');
CREATE INDEX test_index on test ((foo[1]));
SET enable_seqscan TO off;

EXPLAIN ANALYZE SELECT * from test WHERE foo[1]=1;
                                                QUERY PLAN                                                    
------------------------------------------------------------------------------------------------------------------
 Index Scan using test_index on test  (cost=0.00..8.27 rows=1 width=32) (actual   time=0.070..0.071 rows=1 loops=1)
   Index Cond: (foo[1] = 1)
 Total runtime: 0.112 ms
(3 rows)

Esto funciona al menos en Postgres 9.2.1. Tenga en cuenta que debe crear un índice separado para cada índice de matriz, en mi ejemplo, solo indicé el primer elemento.

Ed4
fuente
28
Que no se pierda: este enfoque es inútil para una matriz de longitud variable donde desea utilizar el operador ANY ().
Καrτhικ
24
Esto realmente no es muy útil. Si tiene un número fijo de elementos de la matriz, preferiría usar columnas individuales para cada elemento (e índices btree simples) en lugar de crear un índice de expresión más costoso para cada elemento de la matriz. El almacenamiento de columnas individuales también es mucho más económico sin sobrecarga de matriz.
Erwin Brandstetter