sql >> Database >  >> RDS >> PostgreSQL

SQL-groepering van kruisende/overlappende rijen

Ervan uitgaande dat alle paren ook in hun gespiegelde combinatie bestaan ​​(4,5) en (5,4) . Maar de volgende oplossingen werken net zo goed zonder gespiegelde dupes.

Eenvoudig geval

Alle verbindingen kunnen worden opgesteld in een enkele oplopende reeks en complicaties zoals ik heb toegevoegd in de viool zijn niet mogelijk, kunnen we deze oplossing gebruiken zonder duplicaten in de rCTE:

Ik begin met het verkrijgen van minimaal a_sno per groep, met het minimum geassocieerde b_sno :

SELECT row_number() OVER (ORDER BY a_sno) AS grp
     , a_sno, min(b_sno) AS b_sno
FROM   data d
WHERE  a_sno < b_sno
AND    NOT EXISTS (
   SELECT 1 FROM data
   WHERE  b_sno = d.a_sno
   AND    a_sno < b_sno
   )
GROUP  BY a_sno;

Dit heeft slechts een enkel zoekniveau nodig, aangezien een vensterfunctie op een aggregaat kan worden gebouwd:

Resultaat:

grp  a_sno  b_sno
1    4      5
2    9      10
3    11     15

Ik vermijd vertakkingen en dubbele (vermenigvuldigde) rijen - mogelijk veel duurder met lange kettingen. Ik gebruik ORDER BY b_sno LIMIT 1 in een gecorreleerde subquery om deze in een recursieve CTE te laten vliegen.

De sleutel tot prestatie is een overeenkomende index, die al aanwezig is door de PK-beperking PRIMARY KEY (a_sno,b_sno) :niet andersom (b_sno, a_sno) :

WITH RECURSIVE t AS (
   SELECT row_number() OVER (ORDER BY d.a_sno) AS grp
        , a_sno, min(b_sno) AS b_sno  -- the smallest one
   FROM   data d
   WHERE  a_sno < b_sno
   AND    NOT EXISTS (
      SELECT 1 FROM data
      WHERE  b_sno = d.a_sno
      AND    a_sno < b_sno
      )
   GROUP  BY a_sno
   )

, cte AS (
   SELECT grp, b_sno AS sno FROM t

   UNION ALL
   SELECT c.grp
       , (SELECT b_sno  -- correlated subquery
          FROM   data
          WHERE  a_sno = c.sno
          AND    a_sno < b_sno
          ORDER  BY b_sno
          LIMIT  1)
   FROM   cte  c
   WHERE  c.sno IS NOT NULL
   )
SELECT * FROM cte
WHERE  sno IS NOT NULL   -- eliminate row with NULL
UNION  ALL               -- no duplicates
SELECT grp, a_sno FROM t
ORDER  BY grp, sno;

Minder eenvoudig geval

Alle knooppunten zijn in oplopende volgorde te bereiken met een of meer takken vanaf de wortel (kleinste sno ).

Krijg deze keer alle grotere sno en ontdubbel knooppunten die meerdere keren kunnen worden bezocht met UNION aan het einde:

WITH RECURSIVE t AS (
   SELECT rank() OVER (ORDER BY d.a_sno) AS grp
        , a_sno, b_sno  -- get all rows for smallest a_sno
   FROM   data d
   WHERE  a_sno < b_sno
   AND    NOT EXISTS (
      SELECT 1 FROM data
      WHERE  b_sno = d.a_sno
      AND    a_sno < b_sno
      )
   )   
, cte AS (
   SELECT grp, b_sno AS sno FROM t

   UNION ALL
   SELECT c.grp, d.b_sno
   FROM   cte  c
   JOIN   data d ON d.a_sno = c.sno
                AND d.a_sno < d.b_sno  -- join to all connected rows
   )
SELECT grp, sno FROM cte
UNION                     -- eliminate duplicates
SELECT grp, a_sno FROM t  -- add first rows
ORDER  BY grp, sno;

In tegenstelling tot de eerste oplossing krijgen we hier geen laatste rij met NULL (veroorzaakt door de gecorreleerde subquery).

Beide zouden zeer goed moeten presteren - vooral met lange kettingen / veel takken. Resultaat naar wens:

SQL Fiddle (met toegevoegde rijen om de moeilijkheidsgraad aan te tonen).

Ongerichte grafiek

Als er lokale minima zijn die niet kunnen worden bereikt vanaf de wortel met oplopende traversal, zullen de bovenstaande oplossingen niet werken. Overweeg Farhęg's oplossing in dit geval.



  1. MySQL-database maken vanuit Java

  2. Gegevenstabellen die tabellen samenvoegen zoeken en bestellen zit vast met codeigniter

  3. Splits een groot tekst-/CSV-bestand in meerdere bestanden in PL SQL

  4. ORA-27101:gedeelde geheugenwereld bestaat niet