Строка разделения и последовательности - Oracle SQL - PullRequest
0 голосов
/ 13 марта 2019

У меня есть строка в одной из строк:

PID|1||123456789^^^^VV||PIZZA^KEVIN^^^^^L||98765432||

Я написал SQL-запрос, который разделит это на

select regexp_substr('PPID|1||123456789^^^^VV||PIZZA^KEVIN^^^^^L||98765432||','[^|^]+', 1, level) col1 from dual
connect by regexp_substr('PID|1||123456789^^^^VV||PIZZA^KEVIN^^^^^L||98765432||', '[^|^]+', 1, level)
is not null

Это дает вывод как:

col1

PID
1
123456789
VV
PIZZA
KEVIN
L
98765432

В то время как я ищу следующие условия:

если разделить трубу, затем использовать последовательность, если отделена крышка, затем использовать подпоследовательность

Вывод, который я ищу:

col1                  col_seq

PID                   PID00
1                     PID01
(NULL)                PID02
123456789             PID03-01
(NULL)                PID03-02
(NULL)                PID03-03          
VV                    PID03-04
PIZZA                 PID04-01
KEVIN                 PID04-02
(NULL)                PID04-03
(NULL)                PID04-04
(NULL)                PID04-05
(NULL)                PID04-06
L                     PID04-07
(NULL)                PID05
98765432              PID06
(NULL)                PID07
(NULL)                PID08

Может кто-нибудь помочь мне с SQL для этого?

1 Ответ

2 голосов
/ 13 марта 2019

Конечно. Вот простая версия, которая дает вам col1. Это работает намного лучше, если вы используете разделение регулярных выражений substr из этого вопроса .

with t as (select 'PPID|1||123456789^^^^VV||PIZZA^KEVIN^^^^^L||98765432||' as str from dual)
select regexp_substr(t.str,'(.*?)(\||\^|$)', 1, level, null, 1) col1
from t
connect by level <= regexp_count(t.str, '(.*?)(\||\^|$)');

Добавление вашего второго столбца создает значительную сложность. Вероятно, есть изящный способ сделать это, соединив два иерархических запроса, но я не могу сделать это хорошо, поэтому я просто использовал некоторые аналитические функции.

with t as (select 'PPID|1||123456789^^^^VV||PIZZA^KEVIN^^^^^L||98765432||' as str from dual)
select col1,
    'PID'
      -- count pipes seen so far
    || trim(to_char(nvl(sum(case when sep = '|' then 1 else 0 end) 
                         over (order by lev rows between unbounded preceding and 1 preceding)
                     ,0)
              ,'00')) 
    -- count hats (within a partition defined by the number of pipes seen so far)
    || CASE when sep = '^' or lag(sep) over (order by lev) = '^' THEN
        '-' || trim(to_char(row_number() over (partition by regexp_count(seen, '\|') 
                                       order by lev) - 1, '00'))
        ELSE null end as col2
from (        
    select regexp_substr(t.str,'(.*?)(\||\^|$)', 1, level, null, 1) col1,
        regexp_substr(t.str,'(.*?)(\||\^|$)', 1, level, null, 2) sep,
        level as lev,
        substr(t.str,1,regexp_instr(t.str,'(.*?)(\||\^|$)', 1, level, 0)) as seen
    from t
    connect by level <= regexp_count(t.str, '(.*?)(\||\^|$)')
    ) s
;

Выход:

col1      col2
PPID      PID00
1         PID01
          PID02
123456789 PID03-01
          PID03-02
          PID03-03
          PID03-04
VV        PID03-05
          PID04
PIZZA     PID05-01
KEVIN     PID05-02
          PID05-03
          PID05-04
          PID05-05
          PID05-06
L         PID05-07
          PID06
98765432  PID07
          PID08
          PID09

Дайте мне знать, если у вас есть какие-либо вопросы.

РЕДАКТИРОВАТЬ: хорошо, regexp_substr и иерархические запросы являются довольно медленными. Я переписал его, используя рекурсивный ответ CTE no-regex на этот вопрос . Это все еще довольно небрежно, я уверен, что это могло быть убрано.

WITH ex as (select 'PPID|1||123456789^^^^VV||PIZZA^KEVIN^^^^^L||98765432||' as str from dual),
  t ( str, start_pos, end_pos ) AS
  ( SELECT str, 1, LEAST(INSTR(str, '|'),INSTR(str, '^')) FROM ex
  UNION ALL
  SELECT str,
    end_pos + 1,
    CASE WHEN INSTR(str, '|', end_pos + 1) > 0 and INSTR(str, '^', end_pos + 1) > 0 THEN
        LEAST(INSTR(str, '|', end_pos + 1),INSTR(str, '^', end_pos + 1))
        ELSE GREATEST(INSTR(str, '|', end_pos + 1),INSTR(str, '^', end_pos + 1)) END
  FROM t
  WHERE end_pos > 0
  )
select col1,
    'PID' 
    -- count pipes
    || trim(to_char(nvl(sum(case when rsep = '|' then 1 else 0 end) 
                         over (order by start_pos rows between unbounded preceding and 1 preceding)
                     ,0)
              ,'00'))
    -- count hats 
    || CASE when '^' in (lsep,rsep) THEN
        '-' || trim(to_char(row_number() over (partition by (length(seen)-length(replace(seen, '|')))
                                       order by start_pos), '00'))
        ELSE null end
              as col_seq
from (              
    SELECT str, start_pos, end_pos, 
      SUBSTR( str, start_pos, DECODE( end_pos, 0, LENGTH(str) + 1, end_pos ) - start_pos ) AS col1,
      SUBSTR( str, start_pos-1, 1) as lsep, SUBSTR(str, DECODE( end_pos, 0, LENGTH(str) + 1, end_pos ), 1) as rsep,
      SUBSTR( str, 1, DECODE( end_pos, 0, LENGTH(str) + 1, end_pos )-1 ) as seen
    FROM t) s
order by start_pos;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...