我有一个存储在clob中的XML。我需要运行各种substr和instr函数,以便显示XML中的特定数据。
我当前的代码使用varchar2(32000)变量处理它,如下所示:
select xml_data into xmlclob
from test.test_data_v6
where test_id =r1.test_id;
start_position := dbms_lob.instr(xmlclob,'<DataSheet>',1,1)+11;
end_position := dbms_lob.instr(xmlclob,'</DataSheet>',start_position,1);
l_str := (dbms_lob.substr(xmlclob, end_position - start_position, start_position));
l_str := substr(l_str, instr(l_str, '<SummaryParameters>'),instr(l_str, '</SummaryParameters>')-instr(l_str, '<SummaryParameters>')+20);
l_str是varchar2。
如果长度大于32000,则不显示子串,我假设是varchar的限制。所以我将l_str更改为clob变量并执行以下操作:
select xml_data into xmlclob
from test.test_data_v6_temp
where test_id =r1.test_id;
start_position := dbms_lob.instr(xmlclob,'<DataSheet>',1,1)+11;
end_position := dbms_lob.instr(xmlclob,'</DataSheet>',start_position,1);
l_str := dbms_lob.substr(xmlclob, end_position - start_position, start_position);
l_str1 := substr(l_str, instr(l_str, '<SummaryParameters>'), instr(l_str, '</SummaryParameters>')-instr(l_str, '<SummaryParameters>')+20);
如果xmls的end_position-start_position也超过32000,则为null。
我编写了一个匿名块来测试子字符串的限制,如下所示:
declare
error_reason varchar2(32000);
start_position number;
end_position number;
str_length number;
str_length2 number;
l_clob2 clob;
l_str varchar2(32000);
e_clob clob;
temp_clob clob;
begin
select xml_data into l_clob2 from test.test_data_v6_temp where test_id=(hard coded the test id);
start_position := dbms_lob.instr(l_clob2,'<DataSheet>',1,1)+11;
dbms_output.put_line('startposition='||start_position);
end_position := dbms_lob.instr(l_clob2,'</DataSheet>',start_position,1);
dbms_output.put_line('end position='||end_position);
str_length := end_position-start_position;
str_length2 := 30000;
dbms_output.put_line('str_length='||str_length);
dbms_output.put_line('str_length2='||str_length2);
dbms_output.put_line('str_length='||str_length);
temp_clob := dbms_lob.substr(l_clob2, str_length2, start_position);
--dbms_output.put_line('temp_clob1='||temp_clob);
--temp_clob := substr(temp_clob, instr(temp_clob, '<SummaryParameters>'), instr(temp_clob, '</SummaryParameters>')-instr(temp_clob, '<SummaryParameters>')+20);
insert into test.temp values(temp_clob);
commit;
end;
--select * from test.temp
我注释掉第二个子字符串,只是为了看看我的表是否填充了数据,如果我将str_length更改为低于32000的任何值并且确实填充了它。
这是我的输出
startposition=202134
end position=237746
str_length=35612
str_length2=30000
如何克服substr和clob的限制以获取正确的数据?
收到答案后编辑: 我尝试了复制解决方案,但我遇到的问题是SummaryParameters位于XML中的DataSheet标记内。将它放在循环中,SummaryParametes的第二次迭代无法找到相同的结束标记。同样在XML提取函数中,我使用了getStringVal()方法,并且还返回了另一个具有大小问题的varchar。我不知道如何使用复制功能来获取DataSheet标签内的摘要参数(我无法直接查找SummaryParameters标签,因为DataSheet标签之外还有其他标签。这是我的代码:
declare
start_position number;
end_position number;
str_length number;
str_length2 number;
sp2 number;
ep2 number;
str_len number;
str_len2 number;
l_clob2 clob;
temp_clob clob;
temp_clob2 clob;
begin
select xml_data into l_clob2 from test.test_data_v6_temp where test_id=211230309;
start_position := dbms_lob.instr(l_clob2,'<DataSheet>',1,1)+11;
dbms_output.put_line('startposition='||start_position);
end_position := dbms_lob.instr(l_clob2,'</DataSheet>',start_position,1);
dbms_output.put_line('end position='||end_position);
str_length := end_position-start_position;
dbms_output.put_line('Original clob size: ' || dbms_lob.getlength(l_clob2));
dbms_lob.createtemporary(temp_clob, false);
while str_length > 0
loop
str_length2 := least(str_length, 32768);
str_length := str_length - str_length2;
dbms_lob.copy(temp_clob, l_clob2, str_length2,dbms_lob.getlength(temp_clob) + 1, start_position);
start_position := start_position + str_length;
end loop;
dbms_output.put_line('Copied clob 1 size: ' || dbms_lob.getlength(temp_clob));
sp2 := dbms_lob.instr(temp_clob,'<SummaryParameters>')+20;
dbms_output.put_line('sp2 = '|| sp2);
ep2 := instr(temp_clob, '</SummaryParameters>');
dbms_output.put_line('ep2 = '|| ep2);
str_len :=ep2 - sp2;
dbms_lob.createtemporary(temp_clob2, false);
while str_len > 0
loop
str_len2 := least(str_len, 32768);
str_len := str_len - str_len2;
dbms_lob.copy(temp_clob2, temp_clob, str_len2,dbms_lob.getlength(temp_clob2) + 1, sp2);
sp2 := sp2 + str_len;
end loop;
dbms_output.put_line('Copied 2 clob size: ' || dbms_lob.getlength(temp_clob2));
insert into test.temp values(temp_clob2);
commit;
end;
这是我的输出:
startposition=202134
end position=237746
Original clob size: 1723831
Copied clob 1 size: 35612
sp2 = 703
ep2 = 0
Copied 2 clob size: 0
答案 0 :(得分:1)
以下是使用COPY
程序避免切换为VARCHAR2
的示例:
dbms_output.put_line('Original clob size: ' || dbms_lob.getlength(l_clob2));
dbms_lob.createtemporary(temp_clob, false);
while str_length > 0 loop
str_length2 := least(str_length, 32768);
str_length := str_length - str_length2;
dbms_lob.copy(temp_clob, l_clob2, str_length2,
dbms_lob.getlength(temp_clob) + 1, start_position);
start_position := start_position + str_length2;
end loop;
dbms_output.put_line('Copied clob size: ' || dbms_lob.getlength(temp_clob));
我填充了'XML'值,这给了我输出:
startposition=17
end position=40456
str_length=40439
str_length2=30000
str_length=40439
Original clob size: 40473
Copied clob size: 40439
所以temp_clob
包含原始CLOB的40k +'子串'。
@ user272735是正确的,如果列保存XML数据,那么它应该这样处理(并且最好也以那种方式存储,如XMLType而不是CLOB,现在看来是这样)。
您可以通过以下方式从XML中提取相关数据来避免这项工作:
select xmlquery('*/DataSheet/SummaryParameters/*'
passing xmltype(xml_data) returning content))
from ...
或旧版本(11gR2中弃用):
select extract(xmltype(xml_data), '*/DataSheet/SummaryParameters/*')
from ...
其中任何一个都会为您提供<SummaryParameters>...</SummaryParameters>
内的部分。从您的问题不清楚DataSheet
是否是顶部元素,或者SummaryParameters
是否直接位于该元素内部,因此您可能需要尝试将通配符放在何处。 More about XMLQuery in the documentation
使用两个复制语句可以获取内部SummaryParameters
部分:
declare
start_position number;
end_position number;
str_length number;
str_length2 number;
l_clob2 clob;
temp_clob clob;
temp_clob2 clob;
begin
select xml_data into l_clob2 from t42 where id = 1;
start_position := dbms_lob.instr(l_clob2,'<DataSheet>',1,1)+11;
dbms_output.put_line('startposition='||start_position);
end_position := dbms_lob.instr(l_clob2,'</DataSheet>',start_position,1);
dbms_output.put_line('end position='||end_position);
str_length := end_position-start_position;
dbms_lob.createtemporary(temp_clob, false);
while str_length > 0 loop
str_length2 := least(str_length, 32768);
str_length := str_length - str_length2;
dbms_lob.copy(temp_clob, l_clob2, str_length2,
dbms_lob.getlength(temp_clob) + 1, start_position);
start_position := start_position + str_length2;
end loop;
dbms_output.put_line('temp_clob length: ' || dbms_lob.getlength(temp_clob));
start_position := dbms_lob.instr(temp_clob,'<SummaryParameters>',1,1)+19;
dbms_output.put_line('startposition='||start_position);
end_position := dbms_lob.instr(temp_clob,'</SummaryParameters>',start_position,1);
dbms_output.put_line('end position='||end_position);
str_length := end_position-start_position;
dbms_lob.createtemporary(temp_clob2, false);
while str_length > 0 loop
str_length2 := least(str_length, 32768);
str_length := str_length - str_length2;
dbms_lob.copy(temp_clob2, temp_clob, str_length2,
dbms_lob.getlength(temp_clob2) + 1, start_position);
start_position := start_position + str_length2;
end loop;
dbms_output.put_line('temp_clob2 length: ' || dbms_lob.getlength(temp_clob2));
insert into t42 values (2, temp_clob2);
end;
/
给出(用我的虚拟数据):
startposition=17
end position=40456
temp_clob length: 40439
startposition=220
end position=420
temp_clob2 length: 200
答案 1 :(得分:0)
您是否尝试过XML功能,例如UPDATEXML修改XML,而不是在CLOB上使用纯字符串函数。
也许是这样的:
DECLARE
xmlval XMLType;
BEGIN
SELECT xml_data INTO xmlval
FROM TEST.test_data_v6_temp
WHERE test_id =r1.test_id;
xmlval := UPDATEXML(xmlval, '/DataSheet', 'SummaryParameters');
END;
您只需找到正确的X-Path表达式并替换。
其他XML函数
DELETEXML
INSERTCHILDXML
INSERTCHILDXMLAFTER
INSERTCHILDXMLBEFORE
INSERTXMLAFTER
INSERTXMLBEFORE
XMLELEMENT
XMLCONCAT
也许其中一个可以帮助你。