拆分和序列字符串-Oracle SQL

时间:2019-03-13 16:49:22

标签: sql oracle

我在其中一行中有一个字符串:

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

有人可以为此提供帮助吗?

1 个答案:

答案 0 :(得分:2)

好的。这是简单的版本,可让您获得col1。如果您使用the splitting regexp substr from this question,效果会更好。

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和分层查询都非常慢。我用MT0's recursive CTE no-regex answer on this question重写了它。它仍然很草率,我敢肯定它可以清理。

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;