sql >> Databasteknik >  >> RDS >> Oracle

Oracle SQL Para ihop höger-vänster sekventiella nummer med identifierare

Här är en lösning som fungerar mer generellt, även om paren inte nödvändigtvis finns precis bredvid varandra. (Om det i själva verket KRÄVS, om delar inte kan paras om deras ID inte är konsekutiva, kan det villkoret läggas till i frågan.)

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
;

Utdata :

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 

Förklaring:I den inre frågan lägger jag till ytterligare två kolumner till de initiala uppgifterna. En, rn , räknas separat (börjar från 1 och ökar med 1) för varje identifierare, separat för "L" och för "R". Detta kommer att användas för att bilda paren. Och, ct ger det minsta av det totala antalet för 'L' och 'R' för varje identifierare. I den yttre frågan filtrerar jag bara bort alla rader där rn > ct - det är raderna utan ett par i den initiala tabellen. Det som är kvar är paren.

TILLlagt :Med det ytterligare villkoret att ett par måste bildas från "på varandra följande" rader (mätt med id kolumn), blir detta en mer intressant fråga. Det är ett problem med luckor och öar (identifiera grupper av på varandra följande rader med samma egenskap), men med en twist:LR värdet måste vara alternerande inom gruppen snarare än konstant. Den mycket effektiva "tabibitosan"-metoden kan inte tillämpas här (tror jag); metoden "start av grupp", som är mer generell, fungerar. Detta är vad jag använde här. Observera att jag i slutändan utelämnar den allra sista raden i en grupp, om antalet för gruppen är ett udda tal. (Vi kan hitta två, fyra eller sex på varandra följande rader som bildar ett eller två eller tre par, men inte ett udda antal rader med omväxlande LR). Observera också att om två rader har samma identifierare OCH LR kommer den andra raden alltid att starta en NY grupp, så om den faktiskt är en del av ett par (med raden EFTER det), kommer den att fångas korrekt av denna lösning.

Jämför detta med MATCH_RECOGNIZE-lösningen för Oracle 12 och högre som jag postade separat - och uppskatta hur mycket enklare det är!

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. Välj den första instansen av en post

  2. VBA-kod för att lägga till länkad tabell med primärnyckel

  3. Hur man skapar jobb i SQL Server Express-utgåvan

  4. Privat meddelandesystem. Listar sista meddelandet för varje konversation