我无法找到解决此任务的方法:我的目标是将游标传递给PL / SQL过程并将结果作为XMLType获取。函数dbms_xmlgen.getxmltype()
使这项任务变得简单
<ROWSET>
<ROW>
<FIRST_NAME>John</FIRST_NAME>
<LAST_NAME>Goodman</LAST_NAME>
<HIRE_DATE>22-JUN-2011</HIRE_DATE>
</ROW>
</ROWSET>
现在我想将游标列数据类型作为属性添加到每个相应的XML元素。
<ROWSET>
<ROW>
<FIRST_NAME type="VARCHAR2">John</FIRST_NAME>
<LAST_NAME type="VARCHAR2">Goodman</LAST_NAME>
<HIRE_DATE type="DATE">22-JUN-2011</HIRE_DATE>
</ROW>
</ROWSET>
这可以使用动态SQL来完成,因此我可以编写一个PL / SQL函数来获取一个关联数组,将每列映射到相应的数据类型。
假设我有上述的associativa数组和XMLType,我如何使用XPATH表达式应用一组转换,例如
-- pseudocode ;)
func(myXMLType, '//FIRST_NAME', ?add attribute to the matching node?)
任何其他完成工作的方法都没问题
答案 0 :(得分:1)
您可以将元数据信息转换为自己的XML表示形式,然后使用XPath找到匹配的条目:
select *
from xmltable('for $i in $x/ROWSET return (element {"ROWSET"} {
for $j in $i/ROW
return (element {"ROW"} {
for $k in $j/*
return (element {$k/name()} {
attribute type { $m/metadata/column[@name=$k/name()]/@type },
$k/text()
} )
} )
} )'
passing generated_xml as "x", metadata_xml as "m"
columns result xmltype path '.');
每个ROWSET(当然只有一个)生成一个新的ROWSET元素;然后每个ROW下生成一个新的ROW元素;然后,每个模式下生成一个具有相同名称和值的新节点,但该名称还用于查找元数据中的匹配条目并提取其类型属性,并将其用作此节点的属性。
一个有效的例子:
create or replace function cursor_to_xml(p_cursor sys_refcursor) return xmltype is
l_cursor sys_refcursor;
l_ctx dbms_xmlgen.ctxhandle;
l_xmltype xmltype;
l_cursor_num pls_integer;
l_col_cnt pls_integer;
l_desc_tab dbms_sql.desc_tab2;
l_metadata varchar2(32767);
l_result xmltype;
begin
-- get generated XMl as shown in the question
l_cursor := p_cursor;
l_ctx := dbms_xmlgen.newcontext(querystring => l_cursor);
l_xmltype := dbms_xmlgen.getxmltype(ctx => l_ctx);
dbms_xmlgen.closecontext(ctx => l_ctx);
-- use DBMS_SQL to get the data types
l_cursor_num := dbms_sql.to_cursor_number(rc => l_cursor);
dbms_sql.describe_columns2(c => l_cursor_num, col_cnt => l_col_cnt,
desc_t => l_desc_tab);
dbms_sql.close_cursor(l_cursor_num);
-- manually create an XML version of the column name/data type mappings
-- which could be extended easily to include length/scale/precision/etc.
l_metadata := '<metadata>';
for i in 1..l_desc_tab.count loop
l_metadata := l_metadata || '<column name="' || l_desc_tab(i).col_name ||
'" type="' || case l_desc_tab(i).col_type
when 1 then 'VARCHAR2'
when 2 then 'NUMBER'
when 12 then 'DATE'
-- ...
end
|| '"/>';
end loop;
l_metadata := l_metadata || '</metadata>';
-- use XMLTable with an XPath that deconstructs and reconstructs the
-- generated XML to add an attribute with the type; this is passed two
-- XML objects, referred to internally as $x and $m
-- xmlserialize() formats the result with indentation; xmltype then just
-- gets it back to that type - you may not need either really
select xmltype(xmlserialize(document result as varchar2(4000) indent))
into l_result
from xmltable('for $i in $x/ROWSET return (element {"ROWSET"} {
for $j in $i/ROW
return (element {"ROW"} {
for $k in $j/*
return (element {$k/name()} {
attribute type { $m/metadata/column[@name=$k/name()]/@type },
$k/text()
} )
} )
} )'
passing l_xmltype as "x", xmltype(l_metadata) as "m"
columns result xmltype path '.');
return l_result;
end cursor_to_xml;
/
然后是一个生成游标的块 - 类似于您的示例,但只有两行才能检查它是否有效 - 然后调用该函数来获取修改后的XML:
set serveroutput on;
declare
l_cursor sys_refcursor;
begin
open l_cursor for
select cast('John' as varchar2(10)) as first_name,
cast('Goodman' as varchar2(10)) as last_name,
date '2011-06-22' as hire_date
from dual
union all
select cast('Rhea' as varchar2(10)) as first_name,
cast('Perlman' as varchar2(10)) as last_name,
date '2012-07-23' as hire_date
from dual;
dbms_output.put_line(cursor_to_xml(l_cursor).getstringval);
end;
/
PL/SQL procedure successfully completed.
<ROWSET>
<ROW>
<FIRST_NAME type="VARCHAR2">John</FIRST_NAME>
<LAST_NAME type="VARCHAR2">Goodman</LAST_NAME>
<HIRE_DATE type="DATE">22-JUN-11</HIRE_DATE>
</ROW>
<ROW>
<FIRST_NAME type="VARCHAR2">Rhea</FIRST_NAME>
<LAST_NAME type="VARCHAR2">Perlman</LAST_NAME>
<HIRE_DATE type="DATE">23-JUL-12</HIRE_DATE>
</ROW>
</ROWSET>
当然,您可能希望在CASE中定义more data types。