2011-03-16 76 views
2

我给出了下表。该表包含xml标签和值以及可选的对父标签的引用。如何选择xmltree递归?

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> 

任何想法解决这个问题?

+0

你有可能的标签名称列表? – 2011-03-17 13:49:37

+0

@Marcin Wroblewski:不是最后,但我可以动态生成一个'从xml_tag_val中选择不同的标记,其中obj_id = 1' – chris 2011-03-17 14:51:41

回答

0

OK,这是丑陋的,但可能得到期望的结果

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表达式的限制。

+0

嗯......很好。 CASE表达式中参数的最大数量是255.我认为这足以在保存方面进行计数,并在出现大于255的情况时引发应用程序错误 – chris 2011-03-17 14:15:59

0

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

如果有人有类似的问题,你可以使用一个函数,而不是建立一个大的case语句。所以这是我最后工作的解决方案:

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; 
/