2011-03-16 47 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; 
/