如何拆分序列并取消忽略此表格?

时间:2016-11-14 12:17:12

标签: sql oracle oracle11g

有一项任务。我需要转置这个表:

    id            value
    1001          param1='afvr';param2='ghx';param3='';...;paramN='zfdf'
    1002          param1='arthr';param2='reger';param3='';....;paramM='zehe'
    1003          param1='qwer';param2='geiew';param3='';...;paramX='qweio'
    ......        ......
    2001          param1='pawoe';param2='eug';param3='';.....;paramn='mds'

进入这个:

    id                param1      param1   param3    paramN
    1001              'afvr'      'ghx'    null      'zfdf'
    1002              'arthr'     'reger'  null      'zehe'
    1003              'qwer'      'geiew'  null      'qweio'
    ......            ......
    2001              'pawoe'     'eug'    null      'mds'

和此:

    id                value
    1001              'afvr'
    1001              'ghx'
    1001               null
    1001              'zfdf'
    1002              'arthr'
    1002              'reger'
    1002               null
    1002              'zehe'
    1003              'qwer'
    1003              'geiew'
    1003               null
    1003              'qweio'
    ......            ......
    2001              'pawoe'
    2001              'eug'
    2001               null
    2001              'mds'

使用SQL和正则表达式。虽然我明白我应该使用循环,但我不明白。有没有办法使用Oracle?

3 个答案:

答案 0 :(得分:1)

像这样的东西。我稍微更改了输入数据 - 参数的数量和名称都是事先给出的。如果不是这种情况,则在运行“t”查询后,必须单独编写“最外层”查询 - 如果要自动执行整个过程,则需要在PL / SQL中编写过程(或者可能是Java或C# - 甚至是SQL本身,我喜欢的方式。)

with
     test_data ( id, value ) as (
       select 1001, q'#param1='afvr';param2='ghx';param3='';paramN='zfdf'#'    from dual union all
       select 1002, q'#param1='arthr';param2='reger';param3='';paramN='zehe'#' from dual union all
       select 1003, q'#param1='qwer';param2='geiew';param3='';paramN='qweio'#' from dual union all
       select 2001, q'#param1='pawoe';param2='eug';param3='';paramN='mds'#'    from dual
     ),
     t ( id, param, token ) as (
       select id, 
              regexp_substr(value, '(;|^)([^=]*?)=', 1, level, null, 2),
              regexp_substr(value, '=''([^;]*?)''(;|$)', 1, level, null, 1)
       from   test_data
       connect by level <= regexp_count(value, '=')
           and prior id = id
           and prior sys_guid() is not null
     )
select * from t
pivot (min(token) for param in ('param1' as param1, 'param2' as param2,
                                'param3' as param3, 'paramN' as paramN))
order by id
;

如果您只是单独运行t,它将为您提供第二个表格。此外,如果性能存在问题(对于该格式的数据总是存在问题),您可以完全避免使用regexp函数 - 只需回写即可,我们将向您展示如何使用{{1 },substr()instr()(而不是translate()

答案 1 :(得分:1)

如果您只想尝试第三个表中显示的最终结果,并且实际上并不需要数据的中间视图,则可以将该值视为虚拟XML节点中的参数,然后使用XMLTable提取所有参数值:

select yt.id, x.value
from your_table yt
cross join xmltable('/tmp/@*'
  passing xmltype('<tmp ' || translate(value, ';', ' ') || ' />')
  columns value varchar2(30) path '.'
) x;

快速演示,CTE提供您问题的部分样本数据,另外还有一行,用于演示评论中提出的几个场景:

with your_table (id, value) as (
  select 1001, q'[param1='afvr';param2='ghx';param3='';paramN='zfdf']' from dual
  union all select 1002, q'[param1='arthr';param2='reger';param3='';paramM='zehe']' from dual
  union all select 1003, q'[param1='qwer';param2='geiew';param3='';paramX='qweio']' from dual
  union all select 2001, q'[param1='pawoe';param2='eug';param3='';paramn='mds']' from dual
  union all select 2002, q'[param1='abc def';param2='ghi;jkl';param3='m=n']' from dual
)
select yt.id, x.value
from your_table yt
cross join xmltable('/tmp/@*'
  passing xmltype('<tmp ' || translate(value, ';', ' ') || ' />')
  columns value varchar2(30) path '.'
) x;

        ID VALUE                         
---------- ------------------------------
      1001 afvr                          
      1001 ghx                           
      1001                               
      1001 zfdf                          
      1002 arthr                         
      1002 reger                         
      1002                               
      1002 zehe                          
      1003 qwer                          
      1003 geiew                         
      1003                               
      1003 qweio                         
      2001 pawoe                         
      2001 eug                           
      2001                               
      2001 mds                           
      2002 abc def                       
      2002 ghi jkl                       
      2002 m=n                           

19 rows selected. 

我猜测了该值的最大值,如有必要,请调整(30)。这会将分号分隔符转换为空格,因此它们在XML中有效;你也可以使用replace()。 XMLType构造函数中的位最终为:

        ID TMP_VALUE                                                    
---------- -------------------------------------------------------------
      1001 <tmp param1='afvr' param2='ghx' param3='' paramN='zfdf' />   
      1002 <tmp param1='arthr' param2='reger' param3='' paramM='zehe' />
      1003 <tmp param1='qwer' param2='geiew' param3='' paramX='qweio' />
      2001 <tmp param1='pawoe' param2='eug' param3='' paramn='mds' />   
      2002 <tmp param1='abc def' param2='ghi jkl' param3='m=n' />       

并且XMLTable查找虚拟tmp节点内的所有参数,无论它们被调用。

如果您在中有分号,而不仅仅是它们之间的分隔符,那么您需要更加小心,因为它会丢失 - 就像倒数第二个价值高于。您可以使用正则表达式将其置空,这将更慢(可能有更好的模式):

select yt.id, x.value
from your_table yt
cross join xmltable('/tmp/@*'
  passing xmltype('<tmp ' || regexp_replace(value, q'[';param]', q'[' param]') || ' />')
  columns value varchar2(30) path '.'
) x;

        ID VALUE                         
---------- ------------------------------
...
      2002 abc def                       
      2002 ghi;jkl                       
      2002 m=n                           

您甚至可以将每个参数转换为自己的节点并更改XPath以匹配,但不确定是否能获得任何收益。

但是,转换后的字符串仍需要少于11g中的4000字节,包括虚节点构造(<tmp ... />)。这似乎不是一个问题,但如果您当前的表格列是4000个字符(或字节)并且可能已完全填充,那么您最终会得到一个字符串&## 39;太长了;在这种情况下,您可以更改单词&#39; param&#39;在正则表达式版本中缩短的东西。实际名称从未使用过。

答案 2 :(得分:0)

您必须事先知道的是最大参数数量

select  id      
       ,regexp_substr (value,'.+?=''(.*?)''',1,1,'',1)  as param1      
       ,regexp_substr (value,'.+?=''(.*?)''',1,2,'',1)  as param2      
       ,regexp_substr (value,'.+?=''(.*?)''',1,3,'',1)  as param3      

from    t
;

select  id
       ,value

from   (select  id      
               ,regexp_substr (value,'.+?=''(.*?)''',1,1,'',1)  as param1      
               ,regexp_substr (value,'.+?=''(.*?)''',1,2,'',1)  as param2      
               ,regexp_substr (value,'.+?=''(.*?)''',1,3,'',1)  as param3      

        from    t
        ) t unpivot exclude nulls (value for col in (param1,param2,param3)) t
;