PostgreSQL
 sql >> Teknologi Basis Data >  >> RDS >> PostgreSQL

Pengelompokan SQL baris yang saling berpotongan/tumpang tindih

Dengan asumsi bahwa semua pasangan ada dalam kombinasi cermin mereka juga (4,5) dan (5,4) . Tetapi solusi berikut ini juga berfungsi tanpa penipuan yang dicerminkan.

Kasus sederhana

Semua koneksi dapat diatur dalam satu urutan menaik dan komplikasi seperti yang saya tambahkan di biola tidak mungkin, kita dapat menggunakan solusi ini tanpa duplikat di rCTE:

Saya mulai dengan mendapatkan minimum a_sno per grup, dengan minimum terkait 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;

Ini hanya membutuhkan satu tingkat kueri karena fungsi jendela dapat dibangun secara agregat:

Hasil:

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

Saya menghindari cabang dan baris yang digandakan (digandakan) - berpotensi banyak lebih mahal dengan rantai panjang. Saya menggunakan ORDER BY b_sno LIMIT 1 dalam subkueri berkorelasi untuk membuat ini terbang dalam CTE rekursif.

Kunci kinerja adalah indeks yang cocok, yang sudah ada yang disediakan oleh batasan PK PRIMARY KEY (a_sno,b_sno) :bukan sebaliknya (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;

Kasus yang kurang sederhana

Semua node dapat dicapai dalam urutan menaik dengan satu atau lebih cabang dari root (sno terkecil ).

Kali ini, dapatkan semua sno greater yang lebih besar dan hapus duplikat node yang dapat dikunjungi beberapa kali dengan UNION di akhir:

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;

Tidak seperti solusi pertama, kami tidak mendapatkan baris terakhir dengan NULL di sini (disebabkan oleh subquery yang berkorelasi).

Keduanya harus berkinerja sangat baik - terutama dengan rantai panjang / banyak cabang. Hasil sesuai keinginan:

SQL Fiddle (dengan baris tambahan untuk menunjukkan kesulitan).

Grafik tak berarah

Jika ada minimum lokal yang tidak dapat dicapai dari root dengan traversal menaik, solusi di atas tidak akan berfungsi. Pertimbangkan solusi Farhęg dalam hal ini.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Mengakses file XML eksternal sebagai variabel dalam skrip PSQL (bersumber dari skrip bash)

  2. Bagaimana Anda mengubah pengkodean karakter dari database postgres?

  3. PostGIS Beraksi

  4. Driver PostgreSQL 9.2 JDBC menggunakan zona waktu klien?

  5. Bagaimana cara menghubungkan R dengan PostgreSQL di OSX 10.10.2?