Oracle
 sql >> Base de Dados >  >> RDS >> Oracle

Oracle SQL Pair Números Seqüenciais Direito Esquerdo com Identificadores


Aqui está uma solução que funciona de forma mais geral, mesmo que os pares não sejam necessariamente encontrados um ao lado do outro. (Se isso for de fato NECESSÁRIO, se as partes não puderem ser emparelhadas se seus IDs não forem consecutivos, essa condição pode ser adicionada à consulta.)
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
;

Saída :
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 

Explicação:Na consulta interna, adiciono mais duas colunas aos dados iniciais. Um, rn , conta separadamente (começando em 1 e incrementando em 1) para cada identificador, separadamente para 'L' e para 'R'. Isso será usado para formar os pares. E, ct dá a menor das contagens totais para 'L' e 'R' para cada identificador. Na consulta externa, apenas filtro todas as linhas em que rn > ct - essas são as linhas sem par na tabela inicial. O que resta são os pares.

ADICIONADO :Com a condição adicional de que um par deve ser formado a partir de linhas "consecutivas" (conforme medido pelo id coluna), esta se torna uma questão mais interessante. É um problema de lacunas e ilhas (identifique grupos de linhas consecutivas com a mesma característica), mas com uma diferença:o LR valor deve ser alternado dentro do grupo, em vez de constante. O método "tabibitosan" muito eficiente não pode ser aplicado aqui (eu acho); o método "início do grupo", que é mais geral, funciona. Isso é o que eu usei aqui. Observe que, no final, deixo de fora a última linha de um grupo, se a contagem do grupo for um número ímpar. (Podemos encontrar duas, quatro ou seis fileiras consecutivas que formam um ou dois ou três pares, mas não um número ímpar de fileiras com LR alternado). Observe também que se duas linhas tiverem o mesmo identificador AND LR, a segunda linha sempre iniciará um NOVO grupo, portanto, se de fato fizer parte de um par (com a linha APÓS ela), isso será capturado corretamente por esta solução.

Compare isso com a solução MATCH_RECOGNIZE para Oracle 12 e superior que postei separadamente - e aprecie o quanto é mais simples!
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
;