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
;