有一项任务。我需要转置这个表:
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?
答案 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
;