Problema
Nota: Me refiero a las secuencias matemáticas , no al mecanismo de secuencias de PostgreSQL .
Tengo una tabla que representa secuencias de enteros. La definición es:
CREATE TABLE sequences
(
id serial NOT NULL,
title character varying(255) NOT NULL,
date date NOT NULL,
sequence integer[] NOT NULL,
CONSTRAINT "PRIM_KEY_SEQUENCES" PRIMARY KEY (id)
);
Mi objetivo es encontrar filas usando una subsecuencia dada. Es decir, las filas donde el sequence
campo es una secuencia que contiene la subsecuencia dada (en mi caso, la secuencia está ordenada).
Ejemplo
Supongamos que la tabla contiene los siguientes datos:
+----+-------+------------+-------------------------------+
| id | title | date | sequence |
+----+-------+------------+-------------------------------+
| 1 | BG703 | 2004-12-24 | {1,3,17,25,377,424,242,1234} |
| 2 | BG256 | 2005-05-11 | {5,7,12,742,225,547,2142,223} |
| 3 | BD404 | 2004-10-13 | {3,4,12,5698,526} |
| 4 | BK956 | 2004-08-17 | {12,4,3,17,25,377,456,25} |
+----+-------+------------+-------------------------------+
Entonces, si la subsecuencia dada es {12, 742, 225, 547}
, quiero encontrar la fila 2.
Del mismo modo, si la subsecuencia dada es {3, 17, 25, 377}
, quiero encontrar la fila 1 y la fila 4.
Finalmente, si la subsecuencia dada es {12, 4, 3, 25, 377}
, entonces no hay filas devueltas.
Investigaciones
Primero, no estoy completamente seguro de que representar secuencias con un tipo de datos de matriz sea inteligente. Aunque esto parece apropiado para la situación; Me temo que hace que el manejo sea más complicado. Quizás sea mejor representar las secuencias de manera diferente, utilizando un modelo de relaciones con otra tabla.
De la misma manera, pienso en expandir las secuencias usando la unnest
función de matriz y luego agregar mis criterios de búsqueda. Sin embargo, el número de términos en la secuencia es variable. No veo cómo hacerlo.
Sé que también es posible cortar mi secuencia en subsecuencia usando la subarray
función del módulo intarray , pero no veo cómo me beneficia en mi búsqueda.
Restricciones
Incluso si en este momento mi modelo todavía se está desarrollando, la tabla está compuesta de muchas secuencias, entre 50,000 y 300,000 filas. Entonces tengo una fuerte restricción de rendimiento.
En mi ejemplo usé enteros relativamente pequeños. En la práctica, es posible que estos enteros se vuelvan mucho más grandes, hasta desbordarse bigint
. En tal situación, creo que lo mejor es almacenar números como cadenas (ya que no es necesario realizar estas secuencias de operaciones matemáticas). Sin embargo, al optar por esta solución, esto hace que sea imposible usar el módulo intarray , mencionado anteriormente.
bigint
, debe usarlosnumeric
como tipo para almacenarlos. Sin embargo, es mucho más lento y ocupa mucho más espacio.numeric
y no una cadena (text
por ejemplo)? No necesito realizar operaciones matemáticas en mis secuencias.text
, y evita que almacene datos no numéricos falsos. Depende, si solo está haciendo E / S, es posible que desee que el texto reduzca el procesamiento de E / S.SELECT ARRAY[12, 4, 3, 17, 25, 377, 456, 25] @> ARRAY[12, 4, 3, 25, 377];
devolverá verdadero, porque el orden no es considerado por este operador.Respuestas:
Si está buscando mejoras significativas en el rendimiento de la respuesta de dnoeth , considere usar una función C nativa y crear el operador apropiado.
Aquí hay un ejemplo para las matrices int4. ( Una variante de matriz genérica y el script SQL correspondiente ).
Ahora puede filtrar filas como esta.
He realizado un pequeño experimento para encontrar qué tan rápida es esta solución.
Entonces, es aproximadamente 16 veces más rápido. Si no es suficiente, puede agregar soporte para índices GIN o GiST, pero esta será una tarea mucho más difícil.
fuente
numeric
para representar mis datos porque pueden desbordarsebigint
. Sería bueno editar su respuesta para que coincida con las restricciones de la pregunta. De todos modos, haré un rendimiento comparativo que publicaré aquí.numeric
ytext
y la mejora varió de 20 a 50 veces dependiendo de la longitud de las matrices.numeric
.bigint
.bigint
, por lo que parece que no tengo otra opción. Pero si tienes una idea, estoy interesado :).Puede encontrar fácilmente la subsecuencia cuando convierte las matrices en cadenas y reemplaza las llaves con comas:
Haga lo mismo para la matriz que está buscando y agregue un inicio y un final
%
:Ahora lo compara usando
LIKE
:Editar:
Fiddle está trabajando de nuevo.
Si las matrices se normalizan en una fila por valor, puede aplicar la lógica basada en conjuntos:
n
debe ser secuencial, sin duplicados, sin huecos. Ahora únete a valores comunes y explota el hecho de que las secuencias son secuenciales :-)Finalmente cuente el número de filas con el mismo ficticio y verifique si es el número correcto:
Pruebe un índice en secuencias (val, id, n).
fuente
TEXT
campo (varchar
es una mala idea en mi opinión, las secuencias pueden ser largas, como los números, por lo que el tamaño es bastante impredecible), para evitar el lanzamiento; pero todavía no es posible usar índices para mejorar el rendimiento (además, usar un campo de cadena no parece necesariamente juicioso, vea el comentario de @CraigRinger arriba).25
existe dos vecesid=4
, ¿es esto realmente posible? ¿Cuántas coincidencias existen en promedio / máximo para una secuencia buscada?{1, 1, 1, 1, 12, 2, 2, 12, 12, 1, 1, 5, 4}
es bastante posible. Con respecto al número de coincidencias, normalmente se piensa que las subsecuencias utilizadas limitan el número de resultados. Sin embargo, algunas secuencias son muy similares, y a veces puede ser interesante usar una subsecuencia más corta para obtener más resultados. Estimo que el número de coincidencias para la mayoría de los casos está entre 0 y 100. Siempre existe la posibilidad de que ocasionalmente la subsecuencia coincida con muchas secuencias cuando es corta o muy común.