Oracle
 sql >> Baza danych >  >> RDS >> Oracle

Pary prawych i lewych liczb sekwencyjnych Oracle SQL z identyfikatorami

Oto rozwiązanie, które działa bardziej ogólnie, nawet jeśli pary niekoniecznie znajdują się tuż obok siebie. (Jeśli jest to faktycznie WYMAGANE, jeśli części nie mogą być sparowane, jeśli ich identyfikatory nie są następujące po sobie, warunek ten można dodać do zapytania.)

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
;

Wyjście :

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 

Wyjaśnienie:W zapytaniu wewnętrznym dodaję dwie dodatkowe kolumny do danych początkowych. Jeden, rn , liczy się osobno (zaczynając od 1 i zwiększając o 1) dla każdego identyfikatora, osobno dla 'L' i dla 'R'. Będzie to używane do tworzenia par. I ct daje najmniejszą z całkowitych zliczeń dla „L” i „R” dla każdego identyfikatora. W zewnętrznym zapytaniu po prostu odfiltrowuję wszystkie wiersze, w których rn > ct - są to wiersze bez pary w tabeli początkowej. Pozostały tylko pary.

DODANO :Z dodatkowym warunkiem, że para musi być utworzona z „kolejnych” wierszy (zmierzonych przez id kolumna), to staje się bardziej interesujące pytanie. Jest to problem z lukami i wyspami (zidentyfikuj grupy kolejnych wierszy o tej samej charakterystyce), ale z niespodzianką:LR wartość musi być naprzemienna w grupie, a nie stała. Bardzo wydajna metoda „tabibitosan” nie może być tutaj zastosowana (chyba); bardziej ogólna metoda „początku grupy” działa. Tego tutaj użyłem. Zauważ, że na końcu pomijam ostatni wiersz w grupie, jeśli liczba dla grupy jest liczbą nieparzystą. (Możemy znaleźć dwa, cztery lub sześć kolejnych rzędów, które tworzą jedną lub dwie lub trzy pary, ale nie nieparzystą liczbę rzędów z naprzemiennymi LR). Zwróć też uwagę, że jeśli dwa wiersze mają ten sam identyfikator ORAZ LR, to drugi wiersz zawsze będzie rozpoczynał NOWĄ grupę, więc jeśli faktycznie jest częścią pary (z wierszem PO nim), zostanie ona poprawnie przechwycona przez to rozwiązanie.

Porównaj to z rozwiązaniem MATCH_RECOGNIZE dla Oracle 12 i nowszych, które opublikowałem osobno - i doceń, o ile jest prostsze!

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. Przeszukaj wszystkie kolumny, wszystkie tabele pod kątem określonej wartości

  2. Czy ta implementacja jest zgodna z SQL-92?

  3. Tworzysz program obsługi rejestrowania do łączenia się z Oracle?

  4. Oracle SQL — Konwertuj wartości kolumn z N wierszy na N kolumn w 1 wierszu

  5. jak uzyskać ten wynik za pomocą zapytania oracle w strukturze hierarchicznej