Hier is een oplossing die meer in het algemeen werkt, zelfs als de paren niet noodzakelijk direct naast elkaar worden gevonden. (Als dat in feite VEREIST is, als onderdelen niet kunnen worden gekoppeld als hun ID's niet opeenvolgend zijn, kan die voorwaarde aan de zoekopdracht worden toegevoegd.)
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
;
Uitvoer :
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
Uitleg:In de inner query voeg ik nog twee kolommen toe aan de initiële gegevens. Een, rn
, telt afzonderlijk (beginnend bij 1 en oplopend met 1) voor elke identifier, afzonderlijk voor 'L' en voor 'R'. Dit wordt gebruikt om de paren te vormen. En, ct
geeft de minste van de totale tellingen voor 'L' en 'R' voor elke identifier. In de buitenste query filter ik gewoon alle rijen uit waar rn > ct
- dat zijn de rijen zonder paar in de begintabel. Wat overblijft zijn de paren.
TOEGEVOEGD :Met de aanvullende voorwaarde dat een paar moet worden gevormd uit "opeenvolgende" rijen (zoals gemeten door de id
kolom), wordt dit een interessantere vraag. Het is een gaps-and-islands-probleem (identificeer groepen opeenvolgende rijen met hetzelfde kenmerk), maar met een twist:de LR
waarde moet afwisselend zijn binnen de groep, in plaats van constant. De zeer efficiënte "tabibitosan"-methode kan hier niet worden toegepast (denk ik); de "start of group"-methode, die meer algemeen is, werkt wel. Dit is wat ik hier heb gebruikt. Merk op dat ik uiteindelijk de allerlaatste rij in een groep weglaat, als de telling voor de groep een oneven getal is. (We kunnen twee, of vier, of zes opeenvolgende rijen vinden die een of twee of drie paren vormen, maar geen oneven aantal rijen met afwisselende LR). Merk ook op dat als twee rijen dezelfde identifier EN LR hebben, de tweede rij altijd een NIEUWE groep zal starten, dus als het in feite deel uitmaakt van een paar (met de rij NA erna), wordt dat correct opgevangen door deze oplossing.
Vergelijk dit met de MATCH_RECOGNIZE-oplossing voor Oracle 12 en hoger die ik afzonderlijk heb gepost - en waardeer hoeveel eenvoudiger het is!
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
;