sql >> Database teknologi >  >> RDS >> Oracle

Oracle SQL Par højre venstre sekventielle numre med identifikatorer

Her er en løsning, der fungerer mere generelt, selvom parrene ikke nødvendigvis findes lige ved siden af ​​hinanden. (Hvis det faktisk er PÅKRÆVET, hvis dele ikke kan parres, hvis deres ID'er ikke er fortløbende, kan denne betingelse føjes til forespørgslen.)

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
;

Output :

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 

Forklaring:I den indre forespørgsel tilføjer jeg yderligere to kolonner til de indledende data. En, rn , tæller separat (startende fra 1 og øges med 1) for hver identifikator, separat for 'L' og for 'R'. Dette vil blive brugt til at danne parrene. Og ct giver det mindste af det samlede antal for 'L' og 'R' for hver identifikator. I den ydre forespørgsel filtrerer jeg bare alle rækker fra, hvor rn > ct - det er rækkerne uden et par i den indledende tabel. Hvad der er tilbage er parrene.

TILFØJET :Med den yderligere betingelse, at et par skal dannes ud fra "konsekutive" rækker (som målt ved id kolonne), bliver dette et mere interessant spørgsmål. Det er et hul-og-ø-problem (identificer grupper af på hinanden følgende rækker med samme karakteristik), men med et twist:LR værdi skal være vekslende inden for gruppen, snarere end konstant. Den meget effektive "tabibitosan" metode kan ikke anvendes her (tror jeg); metoden "start af gruppe", som er mere generel, virker. Det er hvad jeg brugte her. Bemærk, at jeg i sidste ende udelader den allersidste række i en gruppe, hvis optællingen for gruppen er et ulige tal. (Vi kan finde to, fire eller seks på hinanden følgende rækker, der danner et eller to eller tre par, men ikke et ulige antal rækker med skiftevis LR). Bemærk også, at hvis to rækker har samme identifikator OG LR, vil den anden række altid starte en NY gruppe, så hvis den faktisk er en del af et par (med rækken EFTER det), vil den blive fanget korrekt af denne løsning.

Sammenlign dette med MATCH_RECOGNIZE-løsningen til Oracle 12 og nyere, jeg postede separat - og forstå, hvor meget enklere det er!

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. Hvad er spørgsmålstegnets betydning i MySQL ved kolonnen WHERE =??

  2. Sådan trækkes et år fra en dato i MariaDB

  3. AWS Python Lambda med Oracle

  4. Hvordan tilføjes kolonne ved hjælp af alter i mysql?