PostgreSQL
 sql >> Base de Dados >  >> RDS >> PostgreSQL

Armazenando e consultando a árvore de intervalos no PostgreSQL


Você pode usar os tipos de dados de intervalo e armazenar cada tipo disjunto em uma linha.

Para sua amostra:
-- 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 se B é um subconjunto de A, você pode juntar ambos com todas as tuplas que o intervalo de A contém o intervalo de B:
 SELECT b.range
 FROM sets b JOIN sets a
     ON a.range @> b.range
 WHERE a.id='A' AND b.id='B'

Com isso, você pode verificar se todos os valores do conjunto B estão no resultado acima (o que significa que todos os intervalos de B estão contidos em pelo menos um intervalo 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 obter a interseção, você pode cruzar as duas e excluir as vazias:
 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);

Um problema sobre essa abordagem é que você deve manter apenas intervalos disjuntos em diferentes tuplas. Por exemplo, o intervalo [1,5] e [4,7] de um conjunto deve residir em uma tupla apenas com [1,7]. Para ter certeza disso, você pode inseri-los em uma tabela temporária (durante a inserção ou atualização), eles cruzam a própria tabela com tuplas que se sobrepõem e eles unem essas e mantêm as outras do jeito que estão.