PostgreSQL
 sql >> база данни >  >> RDS >> PostgreSQL

SQL групиране на интересни/припокриващи се редове

Ако приемем, че всички двойки съществуват и в тяхната огледална комбинация (4,5) и (5,4) . Но следните решения работят без огледални дупки също толкова добре.

Прост случай

Всички връзки могат да бъдат подредени в единична възходяща последователност и усложнения като тези, които добавих в цигулката не са възможни, можем да използваме това решение без дубликати в rCTE:

Започвам с получаване на минимум a_sno на група, с минималния свързан 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;

Това изисква само едно ниво на заявка, тъй като прозоречна функция може да бъде изградена върху агрегат:

Резултат:

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

Избягвам разклонения и дублирани (умножени) редове - потенциално много по-скъпи с дълги вериги. Използвам ORDER BY b_sno LIMIT 1 в корелирана подзаявка, за да накара това да лети в рекурсивен CTE.

Ключът към производителността е съвпадащ индекс, който вече е наличен, предоставен от PK ограничението PRIMARY KEY (a_sno,b_sno) :не обратното (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;

По-малко прост случай

Всички възли могат да бъдат достигнати във възходящ ред с едно или повече разклонения от корена (най-малкото sno ).

Този път вземете всички по-голямо sno и премахване на дублиращите се възли, които могат да бъдат посещавани многократно с UNION в края:

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;

За разлика от първото решение, тук не получаваме последен ред с NULL (причинен от корелираната подзаявка).

И двете трябва да се представят много добре - особено с дълги вериги / много разклонения. Резултат по желание:

SQL Fiddle (с добавени редове за демонстриране на трудността).

Неориентирана графика

Ако има локални минимуми, които не могат да бъдат достигнати от корена с възходящо преминаване, горните решения няма да работят. Помислете за решението на Farheg в този случай.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Поправяне на повредена TOAST маса

  2. Заобикаляне на уникалните ограничения на Sequelize в асоциации на belongsToMany

  3. Как да рендирате обект в Django Views?

  4. Групирайте по и добавете колони

  5. Postgres GROUP BY във вътрешното поле на jsonb