如何选择xmltree递归?

时间:2011-03-16 18:15:40

标签: xml oracle oracle10g

我已经给出了下表。此表包含xml标记和值以及可选的父标记引用。

CREATE GLOBAL TEMPORARY TABLE XML_TAG_VAL
(
  OBJ_ID     NUMBER, -- unique for one whole xml document
  ID         NUMBER, -- unique for every tag
  TAG        VARCHAR2(1000 BYTE), -- tags name
  VAL        CLOB, -- tags value
  LVL        NUMBER, -- depth of the tag
  ATTR_ID    NUMBER, -- foreign key to tag_attr table (do this later)
  PARENT_ID  NUMBER -- id of parent tag (xml_tag_val.id)
)
ON COMMIT DELETE ROWS
NOCACHE;

让我们在此表中插入一些非常简单的测试数据......

insert into xml_tag_val values(1,1,'a',null,1,null,null);
insert into xml_tag_val values(1,2,'b','b-value',2,null,1);
insert into xml_tag_val values(1,3,'b','b-value 2',2,null,1);

我现在要做的是为给定的客户端接口生成XML。所以结果应该是:

<a>
 <b>b-value</b>
 <b>b-value 2</b>
</a>

因此对于这个特定情况,使用xmlelement的手工制作的sql不会成为问题。但是如何在不知道路径有多深的情况下选择xml树呢?我所知道的只是每个孩子都指向其父母。

编辑1:
我发现有一种方法可以使用递归查询来构建XML,我有以下查询:

declare
  l_qry_ctx dbms_xmlgen.ctxhandle;
  l_result clob;
  l_obj_id number := 1;
begin
  l_qry_ctx := dbms_xmlgen.newcontextFromHierarchy('
    select level,xmlelement(tag, val) 
       from oranetted_plugin.xml_tag_val 
         where obj_id = ' || l_obj_id || '
         start with parent_id is null 
           connect by parent_id = prior id'
  );
  l_result:=dbms_xmlgen.getxml(l_qry_ctx);

  dbms_output.put_line(l_result);           
end;
/

问题是无法从sqls结果中提供标签名称。标签不是“a”或“b”,而是显示为“标签”。

<TAG>
  <TAG>b-value</TAG>
  <TAG>b-value 2</TAG>
</TAG>

有什么想法来解决这个问题吗?

2 个答案:

答案 0 :(得分:0)

好的,这很难看,但可能会得到理想的结果

declare
   l_qry_ctx    dbms_xmlgen.ctxhandle;
   l_result     clob;
   l_expression varchar2(32767);
   l_obj_id     number := 1;
begin
   for r in (select distinct tag from xml_tag_val) loop
      l_expression := l_expression || ' when ''' || r.tag ||
                      ''' then xmlelement(' || r.tag || ',val)';
   end loop;
   l_expression := 'case tag ' || l_expression || ' end';
   l_qry_ctx := dbms_xmlgen.newcontextFromHierarchy('
    select level,' ||
                                                    l_expression || '
       from xml_tag_val 
         where obj_id = ' ||
                                                    l_obj_id || '
         start with parent_id is null 
           connect by parent_id = prior id');
   l_result := dbms_xmlgen.getxml(l_qry_ctx);

   dbms_output.put_line(l_result);
end;
/

...但是你必须记住CASE表达式限制。

答案 1 :(得分:0)

Marcin带领我走向正确的方向,谢谢!

如果有人遇到类似问题,您可以使用函数而不是构建大案例语句。所以这是我最后的工作解决方案:

  create or replace
  function calc_xml_element(
     i_tag varchar2
    ,i_val clob
    ,i_attr_id number := null
  ) return xmltype
  is
    l_res      xmltype;
    l_sql      varchar2(4000);
    l_attr_sql varchar2(4000) := 'XMLATTRIBUTES(';
  begin
    if i_attr_id is not null then
      for tupl in 
        (select * from xml_tag_attr where id = i_attr_id)
      loop
        l_attr_sql := l_attr_sql || '''' || substr(tupl.attr_val,1,255) || ''' as "' || tupl.attr || '",';  
      end loop; 

      l_attr_sql := rtrim(l_attr_sql,',') || ')';
      l_sql := 'begin select xmlelement("' || i_tag || '",' || l_attr_sql || ',:val) into :a from dual; end;';
    else
      l_sql := 'begin select xmlelement("' || i_tag || '",:val) into :a from dual; end;';
    end if;

    execute immediate l_sql
      using in i_val, in out l_res; 

    return l_res;

    exception when others then
      raise_application_error(-20000,'Calc XML failed ' || l_sql, true);
  end calc_xml_element;
/

现在你可以做到:

insert into oranetted_plugin.xml_tag_val values(1,1,'a',null,1,null,null);
insert into oranetted_plugin.xml_tag_val values(1,2,'b','b-value',2,null,1);
insert into oranetted_plugin.xml_tag_val values(1,3,'b','b-value 2',null,1,1);

declare
   l_qry_ctx    dbms_xmlgen.ctxhandle;
   l_result     clob;
   l_obj_id     number := 1;
begin    
   l_qry_ctx := dbms_xmlgen.newcontextFromHierarchy('
    select level, oranetted_plugin.calc_xml_element(tag,val,attr_id) ' ||
       'from oranetted_plugin.xml_tag_val 
         where obj_id = ' || l_obj_id || '
         start with parent_id is null 
           connect by parent_id = prior id');

   l_result := dbms_xmlgen.getxml(l_qry_ctx);
   dbms_output.put_line(l_result);
end;
/