Oracle
 sql >> Teknologi Basis Data >  >> RDS >> Oracle

Oracle SQL Memasangkan Angka Sekuensial Kanan Kiri dengan Pengidentifikasi

Berikut adalah solusi yang bekerja lebih umum, bahkan jika pasangan tidak selalu ditemukan tepat di sebelah satu sama lain. (Jika itu memang DIBUTUHKAN, jika bagian tidak dapat dipasangkan jika ID mereka tidak berurutan, kondisi itu dapat ditambahkan ke kueri.)

with
     test_data ( id, lr, identifier ) as (
       select '001', 'L', 'B15A' from dual union all
       select '002', 'R', 'A15C' from dual union all
       select '003', 'L', 'A15C' from dual union all
       select '004', 'R', 'A15C' from dual union all
       select '005', 'L', 'A15C' from dual union all
       select '006', 'R', 'D5A2' from dual union all
       select '009', 'R', 'D5A2' from dual union all
       select '010', 'L', 'E5A6' from dual union all
       select '011', 'R', 'E5A6' from dual union all
       select '012', 'L', 'E5A6' from dual union all
       select '013', 'R', 'E5A6' from dual union all
       select '014', 'R', 'H9S5' from dual union all
       select '017', 'L', 'EE5A' from dual union all
       select '018', 'R', 'EE5A' from dual
     )
-- end of test data, the solution (SQL query) begins below this line
select id, lr, identifier
from ( select id, lr, identifier,
              row_number() over (partition by identifier, lr order by id) as rn,
              least( count(case when lr = 'L' then 1 end) over (partition by identifier),
                     count(case when lr = 'R' then 1 end) over (partition by identifier)
                   ) as least_count
       from   test_data
)
where rn <= least_count
order by id               --  ORDER BY is optional
;

Keluaran :

ID  LR IDENTIFIER
--- -- ----------
002 R  A15C
003 L  A15C
004 R  A15C
005 L  A15C
010 L  E5A6
011 R  E5A6
012 L  E5A6
013 R  E5A6
017 L  EE5A
018 R  EE5A

 10 rows selected 

Penjelasan:Dalam kueri dalam, saya menambahkan dua kolom lagi ke data awal. Satu, rn , menghitung secara terpisah (mulai dari 1 dan bertambah dengan 1) untuk setiap pengenal, secara terpisah untuk 'L' dan untuk 'R'. Ini akan digunakan untuk membentuk pasangan. Dan, ct memberikan paling sedikit jumlah total untuk 'L' dan 'R' untuk setiap pengenal. Di kueri luar, saya hanya memfilter semua baris di mana rn > ct - itu adalah baris tanpa pasangan di tabel awal. Yang tersisa adalah pasangan.

DITAMBAHKAN :Dengan syarat tambahan bahwa pasangan harus dibentuk dari baris "berturut-turut" (diukur dengan id kolom), ini menjadi pertanyaan yang lebih menarik. Ini adalah masalah gaps-and-islands (identifikasi grup baris berurutan dengan karakteristik yang sama), tetapi dengan twist:LR nilai harus bergantian dalam kelompok, bukan konstan. Metode "tabibitosan" yang sangat efisien tidak dapat diterapkan di sini (saya pikir); metode "mulai dari grup", yang lebih umum, berhasil. Inilah yang saya gunakan di sini. Perhatikan bahwa pada akhirnya saya meninggalkan baris terakhir dalam sebuah grup, jika hitungan untuk grup adalah angka ganjil. (Kita mungkin menemukan dua, atau empat, atau enam baris berurutan yang membentuk satu atau dua atau tiga pasang, tetapi bukan jumlah baris ganjil dengan LR bergantian). Perhatikan juga bahwa jika dua baris memiliki pengenal AND LR yang sama, baris kedua akan selalu memulai grup BARU, jadi jika itu sebenarnya bagian dari pasangan (dengan baris SETELAHnya), itu akan ditangkap dengan benar oleh solusi ini.

Bandingkan ini dengan solusi MATCH_RECOGNIZE untuk Oracle 12 dan di atasnya yang saya posting secara terpisah - dan hargai betapa lebih sederhananya!

with
     prep ( id, lr, identifier, flag ) as (
       select id, lr, identifier,
              case when identifier = lag(identifier) over (order by id) 
                    and lr        != lag(lr)         over (order by id)
                   then null else 1 end
       from test_data    --  replace "test_data" with actual table name
     ), 
     with_groups ( id, lr, identifier, gp ) as (
       select id, lr, identifier,
              sum(flag) over (order by id)
       from   prep
     ),
     with_rn ( id, lr, identifier, rn, ct ) as (
       select id, lr, identifier,
              row_number() over (partition by identifier, gp order by id),
              count(*)     over (partition by identifier, gp)
       from   with_groups
     )
select   id, lr, identifier
from     with_rn
where    rn < ct or mod(rn, 2) = 0
order by id               --  ORDER BY is optional
;


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Cara mengganti nama simpul Oracle XMLTYPE

  2. ORA-00947 :Nilai Tidak Cukup

  3. Tanda seru dalam SQL (Oracle)

  4. Bagaimana cara terhubung ke Database Oracle menggunakan cx_Oracle dengan nama layanan dan login?

  5. Cara mendapatkan Nilai Kunci sebagai hasil dari kolom Oracle JSON menggunakan JSON_TABLE