Как выбрать рекурсивный xmltree? - PullRequest
2 голосов
/ 16 марта 2011

Я дал следующую таблицу. Эта таблица содержит тег и значение 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>

Так что для этого конкретного случая ручной SQL, использующий xmlelement, не будет проблемой. Но как мне выбрать дерево 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 голосов
/ 18 марта 2011

Марчин привел меня в правильном направлении, спасибо!

Если у кого-то есть похожая проблема, вы можете использовать функцию вместо построения большого оператора 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;
/
0 голосов
/ 17 марта 2011

ОК, это некрасиво, но, вероятно, дает желаемый результат

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.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...