PostgreSQL
 sql >> Datenbank >  >> RDS >> PostgreSQL

Intervallbaum in PostgreSQL speichern und abfragen

Sie könnten die Bereichsdatentypen verwenden und jeden disjunkten Typ in einer Zeile speichern.

Für Ihr Beispiel:

-- 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]');

Um zu überprüfen, ob B eine Teilmenge von A ist, können Sie beide mit allen Tupeln verknüpfen, deren Bereich von A den Bereich von B enthält:

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

Damit können Sie überprüfen, ob alle Werte aus Satz B im obigen Ergebnis enthalten sind (was bedeutet, dass alle Bereiche von B in mindestens einem Bereich von A enthalten sind):

 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'
     ));

Um die Schnittmenge zu erhalten, können Sie beide über Kreuz verbinden und die leeren ausschließen:

 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);

Ein Problem bei diesem Ansatz besteht darin, dass Sie nur disjunkte Bereiche über verschiedene Tupel hinweg behalten müssen. Beispielsweise müssen die Bereiche [1,5] und [4,7] aus einem Satz in einem Tupel nur mit [1,7] enthalten sein. Um dies sicherzustellen, können Sie sie in eine temporäre Tabelle einfügen (während des Einfügens oder Aktualisierens), sie verbinden die Tabelle selbst mit Tupeln, die sich überschneiden, und sie verbinden diese und behalten die anderen bei, wie sie sind.