sql >> Databasteknik >  >> RDS >> PostgreSQL

SQL-gruppering av skärande/överlappande rader

Förutsatt att alla par finns i deras speglade kombination också (4,5) och (5,4) . Men följande lösningar fungerar lika bra utan speglade duper.

Enkelt fall

Alla anslutningar kan radas upp i en en stigande sekvens och komplikationer som jag lade till i fiol inte är möjliga kan vi använda den här lösningen utan dubbletter i rCTE:

Jag börjar med att få minsta a_sno per grupp, med minsta associerade 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;

Detta behöver bara en enskild frågenivå eftersom en fönsterfunktion kan byggas på ett aggregat:

Resultat:

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

Jag undviker grenar och dubblerade (multiplicerade) rader - potentiellt mycket dyrare med långa kedjor. Jag använder ORDER BY b_sno LIMIT 1 i en korrelerad underfråga för att få detta att flyga i en rekursiv CTE.

Nyckeln till prestanda är ett matchande index, som redan finns tillhandahållet av PK-begränsningen PRIMARY KEY (a_sno,b_sno) :inte tvärtom (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;

Mindre enkla fall

Alla noder kan nås i stigande ordning med en eller flera grenar från roten (minsta sno ).

Den här gången får du alla större sno och de-duplicera noder som kan besökas flera gånger med UNION i slutet:

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;

Till skillnad från den första lösningen får vi inte en sista rad med NULL här (orsakad av den korrelerade underfrågan).

Båda ska prestera mycket bra - speciellt med långa kedjor / många grenar. Resultat som önskat:

SQL Fiddle (med tillagda rader för att visa svårighetsgraden).

Oriktat diagram

Om det finns lokala minima som inte kan nås från roten med stigande traversering, fungerar inte ovanstående lösningar. Överväg Farhęgs lösning i det här fallet.



  1. Exportera och ladda ner frågeresultat till Excel-fil i PHP från Oracle

  2. Jag vill använda CASE-satsen för att uppdatera några poster i sql server 2005

  3. SQLAlchemy StaleDataError vid radering av objekt som infogats via ORM sqlalchemy.orm.exc.StaleDataError

  4. Mysql2::Fel:Felaktigt strängvärde