sql >> Base de Datos >  >> RDS >> PostgreSQL

Almacenamiento y consulta del árbol de intervalos en PostgreSQL

Podría usar los tipos de datos de rango y almacenar cada tipo disjunto en una fila.

Para su muestra:

-- The table
CREATE TABLE sets(id text, range int4range);
-- Values of set A
INSERT INTO sets VALUES('A', '[1,1]'),('A','[7,7]'),('A','[9,13]');
-- Values of set B
INSERT INTO sets VALUES('B','[1,1]'),('B','[7,7]'),('B','[10,10]');

Para verificar si B es un subconjunto de A, puede unir ambos con todas las tuplas en las que el rango de A contiene el rango de B:

 SELECT b.range
 FROM sets b JOIN sets a
     ON a.range @> b.range
 WHERE a.id='A' AND b.id='B'

Con eso, puede verificar si todos los valores del conjunto B están en el resultado anterior (lo que significa que todos los rangos de B están contenidos en al menos un rango de A):

 SELECT NOT EXISTS(
     SELECT 1 FROM sets q WHERE q.id='B' AND q.range NOT IN (
         SELECT b.range
         FROM sets b JOIN sets a
             ON a.range @> b.range
         WHERE a.id='A' AND b.id='B'
     ));

Para obtener la intersección, puede unir ambas y excluir las vacías:

 SELECT * FROM (
     SELECT a.range * b.range AS intersec
     FROM sets a CROSS JOIN sets b WHERE  a.id='A' AND b.id='B'
 ) i WHERE NOT isempty(i.intersec);

Un problema de este enfoque es que debe mantener solo rangos disjuntos a través de diferentes tuplas. Por ejemplo, el rango [1,5] y [4,7] de un conjunto debe residir en una tupla con [1,7] solamente. Para asegurarse de ello, puede insertarlos en una tabla temporal (mientras se inserta o actualiza), se cruzan y se unen a la tabla misma con tuplas que se superponen y se unen a ellos y mantienen a los demás como están.