PostgreSQL
 sql >> Baza danych >  >> RDS >> PostgreSQL

SQL grupowanie interesujących/nakładających się wierszy

Zakładając, że wszystkie pary istnieją również w swojej lustrzanej kombinacji (4,5) i (5,4) . Ale poniższe rozwiązania działają równie dobrze bez duplikatów.

Prosty przypadek

Wszystkie połączenia można ustawić w jednej kolejności rosnącej i komplikacje, które dodałem w skrzypce nie są możliwe, możemy zastosować to rozwiązanie bez duplikatów w rCTE:

Zaczynam od uzyskania minimalnego a_sno na grupę, z minimalnym powiązanym 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;

Wymaga to tylko jednego poziomu zapytania, ponieważ funkcję okna można zbudować na agregacie:

Wynik:

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

Unikam rozgałęzień i zduplikowanych (zwielokrotnionych) wierszy – potencjalnie dużo droższe z długimi łańcuchami. Używam ORDER BY b_sno LIMIT 1 w skorelowanym podzapytaniu, aby to poleciało w rekurencyjnym CTE.

Kluczem do wydajności jest dopasowany indeks, który jest już obecny dostarczony przez ograniczenie PK PRIMARY KEY (a_sno,b_sno) :nie odwrotnie (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;

Mniej prosty przypadek

Wszystkie węzły można osiągnąć w kolejności rosnącej z jedną lub kilkoma gałęziami od korzenia (najmniejsze sno ).

Tym razem zdobądź wszystkie większe sno i deduplikuj węzły, które mogą być odwiedzane wielokrotnie za pomocą UNION na końcu:

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;

W przeciwieństwie do pierwszego rozwiązania, nie otrzymujemy tutaj ostatniego wiersza z wartością NULL (spowodowane przez skorelowane podzapytanie).

Obydwa powinny działać bardzo dobrze - zwłaszcza przy długich łańcuchach / wielu gałęziach. Wynik zgodny z oczekiwaniami:

Skrzypce SQL (z dodanymi wierszami w celu wykazania trudności).

Wykres nieskierowany

Jeśli istnieją lokalne minima, których nie można osiągnąć od korzenia z przechodzeniem w górę, powyższe rozwiązania nie będą działać. Rozważ rozwiązanie Farhęga w tym przypadku.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. HikariCP Postgresql Driver twierdzi, że nie akceptuje adresu URL JDBC

  2. Ogranicz relację klucza obcego do wierszy powiązanych podtypów

  3. Niestandardowe warunki wyjątków PostgreSQL

  4. Pl/pgSQL nie ma parametru $1 w instrukcji EXECUTE

  5. Obliczenia n-tego percentyla w postgresql