现在碰到一个问题,有一个存储过程用于解析XML数据。
在表A中有字段CLOB存储XML文本
  TABLE A
  ( id integer,
    text CLOB 
  )
  PROCEDURE proc_import_xml_info
(
p_seqno     IN VARCHAR2,
p_o_retmsg  OUT VARCHAR2
) IS
v_tmp_msg VARCHAR2(4000);
xml_parser    xmlparser.parser;
vs_xml        dat_aas_xml_text.text%TYPE;
vd_doc        xmldom.domdocument;
vn_docnode    xmldom.domnode;
vs_sql        VARCHAR2(8000);
vs_vsql       VARCHAR2(8000);
vi_depest_fno INTEGER;
BEGIN
--获取需要处理的数据
BEGIN
SELECT t.text
INTO vs_xml
FROM A t
 WHERE t.id=p_seqno     ;
EXCEPTION
WHEN OTHERS THEN
v_tmp_msg   := '获取XML文本出错';
ROLLBACK;
END;
--解析XML
xml_parser := xmlparser.newparser;
xmlparser.setvalidationmode(xml_parser, FALSE);
xmlparser.parseclob(xml_parser, vs_xml);
vd_doc := xmlparser.getdocument(xml_parser);
xmlparser.freeparser(xml_parser);
vn_docnode := xmldom.makenode(xmldom.getdocumentelement(vd_doc));
--解析完成,按XML内字段处理
IF NOT xmldom.isnull(vd_doc) THEN
SELECT MAX(t.forest_no)
INTO vi_depest_fno
FROM par_aas_xml_model t
 WHERE t.fcode = p_fcode;
BEGIN
r_split_xml(p_fcode, '', vn_docnode, vd_doc, vi_depest_fno, vs_sql, vs_vsql);
EXCEPTION
WHEN OTHERS THEN
p_o_retmsg  := 'xml解析异常' || SQLERRM;
RETURN;
END;
COMMIT;
EXCEPTION
WHEN OTHERS THEN
ROLLBACK;
END; r_split_xml是解析XML的存储过程。解析的方式使用配好到表的各个XML的TAG进行解析PROCEDURE r_split_xml
(
is_fcode      IN VARCHAR2, --文件类型
ii_pforest_no IN INTEGER, --当前结点层次序号
in_node       IN xmldom.domnode, --当前结点
id_doc        IN xmldom.domdocument, --XML文档对象
ii_depest_fno IN INTEGER, --最末层次序号
vs_sql        IN OUT VARCHAR2, --缓存SQL语句1
vs_value_sql  IN OUT VARCHAR2 --缓存SQL语句2
) AS
vn_node        xmldom.domnode;
len            INTEGER := 0;
vn_nlist       xmldom.domnodelist;
vs_node_name   VARCHAR2(200);
vi_count       INTEGER;
vs_column_name config_table_sub.table_column%TYPE;
vs_this_sql    VARCHAR2(8000) := vs_sql;
vs_this_vsql   VARCHAR2(8000) := vs_value_sql;
BEGIN
FOR vr_splits IN (SELECT *
FROM config_table
 WHERE fcode = is_fcode
 AND (parent_forest = ii_pforest_no OR (parent_forest IS NULL AND ii_pforest_no IS NULL)))
LOOP
IF ii_pforest_no IS NULL THEN
vs_this_sql  := 'INSERT INTO ' || vr_splits.recieve_table_name || '(';
vs_this_vsql := ' VALUES(';
vs_sql       := vs_this_sql;
vs_value_sql := vs_this_vsql;
END IF;
--获取当前层次的子节点集合
vn_nlist := xmldom.getchildnodes(in_node);
len      := xmldom.getlength(vn_nlist);
--如果存在该层子结点的数据,则按config_table_sub中该层的NODE_NAME解析
IF len > 0 THEN
FOR i IN 0 .. len - 1
LOOP
vn_node      := xmldom.item(vn_nlist, i);
vs_node_name := xmldom.getnodename(vn_node);
SELECT COUNT(1)
INTO vi_count
FROM config_table t
 WHERE t.parent_forest = vr_splits.forest_no
 AND t.node_name = vs_node_name;
IF vi_count >= 1 THEN
--如果子节点名,以该节点循环处理该节点下的子结点
r_split_xml(is_fcode, nvl(ii_pforest_no, 0) + 1, vn_node, id_doc, ii_depest_fno, vs_sql, vs_value_sql);
ELSE
--如果是普通结点,判断是否需要插入表中。
vs_column_name := '';
BEGIN
SELECT t.table_column
INTO vs_column_name
FROM config_table_sub t
 WHERE t.f_code = is_fcode
 AND t.forest_no = vr_splits.forest_no
 AND t.node_name = vs_node_name;
EXCEPTION
WHEN no_data_found THEN
NULL;
WHEN OTHERS THEN
ROLLBACK;
RAISE;
END;
IF vs_column_name IS NOT NULL THEN
vs_this_sql  := vs_this_sql || vs_column_name || ',';
vs_this_vsql := vs_this_vsql || '''' || xmldom.getnodevalue(xmldom.getfirstchild(vn_node)) || ''',';
END IF;
IF vr_splits.forest_no < ii_depest_fno THEN
--如果还没到最末节点,缓存
vs_sql       := vs_this_sql;
vs_value_sql := vs_this_vsql;
END IF;
END IF;
END LOOP;
END IF;
IF vr_splits.forest_no = ii_depest_fno THEN
--如果到达最末节点,则拼接完成SQL语句,执行操作。
--接收表都定义了以下字段,所以需要处理
vs_this_sql  := vs_this_sql || 'DATA_DATE,DATA_WAY,DATA_TIME,IS_DEALED )';
vs_this_vsql := vs_this_vsql || 'to_char(sysdate,''YYYYMMDD''),''1'',sysdate,''1'')';
dbms_output.put_line('insert_sql :' || vs_this_sql || vs_this_vsql);
BEGIN
EXECUTE IMMEDIATE vs_this_sql || vs_this_vsql;
EXCEPTION
WHEN OTHERS THEN
ROLLBACK;
RAISE;
END;
END IF;
END LOOP;
EXCEPTION
WHEN OTHERS THEN
ROLLBACK;
RAISE;
END r_split_xml;其中config_tab保存的是每个有子节点的XML TAG。config_tab保存的是每层的子节点
例如对于下面的XML,在config_tab里会配置两条
forest_no  parent_forest node_name
1                        OUT
2          1             DETAIL
在config_tab_sub里会配置OUT和DETAIL下的TAG
forest_no  node_name
2           TAG1
2           TAG2
3           DT1
3           DT2 <OUT>
   <TAG1></TAG1>
   <TAG2></TAG2>
   <DETAIL>
     <DT1></DT1>
     <DT2></DT2>
   </DETAIL>
   <DETAIL>
     <DT1></DT1>
     <DT2></DT2>
   </DETAIL>
   <DETAIL>
     <DT1></DT1>
     <DT2></DT2>
   </DETAIL>
   <DETAIL>
     <DT1></DT1>
     <DT2></DT2>
   </DETAIL>
 </OUT>那么情况是如果这么XML很大,有几百K,并且主要集中是会有近千条的DETAIL。
问题1: 在解析一个XML的过程中,CLOB这个字段什么时候释放;如果以XML为400K为例,占用的内存大小至少是多少;
    2: 解析一个上面情况的XML需要花的时候会不会很久;
    3: 因为在解析的时候用了递归,是不是在处理是会消耗更多的内存;
    4: 直接结束之后是否需要对LOB进行手工FREE。我试了一下FREE的时候会报错。这种情况使用的LOB变量是不是不用FREE了?
    5: 是否有更优的解析方法。
 望各位达人不吝赐教啊。

解决方案 »

  1.   

    临时LOB对象
    LOB对象包括BLOB、CLOB、NCLOB、和BFILE。在PLSQL程序块中,如果定义了LOB变量,则这些LOB变量就是临时LOB对象。临时LOB对象被创建在临时表空间上,直到LOB数据被释放,或者会话结束。
      

  2.   

    那为什么程序里的那个LOB我用dbms_lob.freetemporary的时候报invalid loc locator的错误呢~~~
      

  3.   

    http://blog.mchz.com.cn/?p=955
    看看
      

  4.   

     这个是释放表里这个字段的空间吧?释放变量的呢?我给赋值成empty_clob算吗