sql >> Databáze >  >> RDS >> Oracle

Oracle SQL Spárujte pravá levá sekvenční čísla s identifikátory

Zde je řešení, které funguje obecněji, i když se dvojice nemusí nutně nacházet těsně vedle sebe. (Pokud je to ve skutečnosti VYŽADOVÁNO, pokud díly nelze spárovat, pokud jejich ID nejsou po sobě jdoucí, lze tuto podmínku přidat do dotazu.)

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
;

Výstup :

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 

Vysvětlení:Ve vnitřním dotazu přidám k počátečním datům další dva sloupce. Jedna, rn , počítá samostatně (počínaje 1 a zvyšuje se o 1) pro každý identifikátor, zvlášť pro „L“ a pro „R“. To bude použito k vytvoření dvojic. A ct udává nejmenší z celkových počtů pro 'L' a 'R' pro každý identifikátor. Ve vnějším dotazu pouze odfiltruji všechny řádky, kde je rn > ct - to jsou řádky bez páru v úvodní tabulce. Co zbylo, jsou dvojice.

PŘIDÁNO :S další podmínkou, že pár musí být vytvořen z „po sobě jdoucích“ řádků (měřeno pomocí id sloupec), to se stává zajímavější otázkou. Je to problém s mezerami a ostrovy (identifikujte skupiny po sobě jdoucích řádků se stejnou charakteristikou), ale s obratem:LR hodnota se musí v rámci skupiny střídat, nikoli konstantní. Velmi účinnou metodu "tabibitosanu" zde nelze použít (myslím); metoda "start of group", která je obecnější, funguje. To je to, co jsem použil zde. Všimněte si, že nakonec vynechám úplně poslední řádek ve skupině, pokud je počet pro skupinu liché číslo. (Můžeme najít dva, čtyři nebo šest po sobě jdoucích řad, které tvoří jeden nebo dva nebo tři páry, ale ne lichý počet řad se střídajícím se LR). Všimněte si také, že pokud dva řádky mají stejný identifikátor AND LR, druhý řádek vždy zahájí NOVOU skupinu, takže pokud je ve skutečnosti součástí páru (s řádkem ZA ním), bude toto řešení zachyceno správně.

Porovnejte to s řešením MATCH_RECOGNIZE pro Oracle 12 a vyšší, které jsem zveřejnil samostatně – a oceníte, jak je jednodušší!

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. Dotaz MySQL pro nalezení nejlepšího ubytování za nejlepší ceny

  2. SQL – Najděte cenu nejbližší danému argumentu

  3. Certifikace Oracle

  4. Jak volat funkci nebo proceduru Oracle pomocí Hibernate (EntityManager) nebo JPA