Oracle
 sql >> база данни >  >> RDS >> Oracle

Oracle SQL сдвоява дясно ляво последователни числа с идентификатори

Ето едно решение, което работи по-общо, дори ако двойките не са непременно една до друга. (Ако това всъщност е ЗАДЪЛЖИТЕЛНО, ако частите не могат да бъдат сдвоени, ако техните идентификатори не са последователни, това условие може да се добави към заявката.)

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
;

Изход :

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 

Обяснение:Във вътрешната заявка добавям още две колони към първоначалните данни. Едно, rn , брои отделно (започвайки от 1 и увеличавайки с 1) за всеки идентификатор, отделно за „L“ и за „R“. Това ще се използва за формиране на двойките. И ct дава най-малкото от общия брой за 'L' и 'R' за всеки идентификатор. Във външната заявка просто филтрирам всички редове, където rn > ct - това са редовете без двойка в началната таблица. Остават двойките.

ДОБАВЕНО :С допълнителното условие, че двойка трябва да бъде формирана от "последователни" редове (както е измерено чрез id колона), това става по-интересен въпрос. Това е проблем с пропуски и острови (идентифицирайте групи от последователни редове с една и съща характеристика), но с обрат:LR стойността трябва да се редува в групата, а не да е постоянна. Много ефикасният метод "табибитозан" не може да се приложи тук (мисля); методът "начало на група", който е по-общ, работи. Това е, което използвах тук. Обърнете внимание, че в крайна сметка пропускам последния ред в група, ако броят за групата е нечетно число. (Може да намерим два, или четири, или шест последователни реда, които образуват една или две или три двойки, но не и нечетен брой редове с редуващи се LR). Обърнете внимание също, че ако два реда имат един и същ идентификатор И LR, вторият ред винаги ще започва НОВА група, така че ако всъщност е част от двойка (с реда СЛЕД него), това ще бъде уловено правилно от това решение.

Сравнете това с решението MATCH_RECOGNIZE за Oracle 12 и по-нова версия, което публикувах отделно – и оценете колко по-просто е!

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. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Грешка в изхода на SQL Fiddle

  2. Oracle - създаване на временен набор от резултати за използване в заявка

  3. Четене на clob променлива ред по ред

  4. SQL разработчик:Не може да се събере системна статистика:недостатъчни привилегии

  5. Потребителят на схемата на Oracle не може да създаде таблица в процедура