Xml 带子查询的Oracle pivot

Xml 带子查询的Oracle pivot,xml,oracle,plsql,oracle11g,pivot,Xml,Oracle,Plsql,Oracle11g,Pivot,我在Oracle PL SQL Developer中使用pivot,如下所示: SELECT * FROM population PIVOT (AVG(Total) for Data_Type IN ('Group1','Group2','Group3')) SELECT * FROM population PIVOT (AVG(Total) for Data_Type IN (SELECT Data_Type FROM population)) 这很好,但我不想每次添加新列或更改一个列(即

我在Oracle PL SQL Developer中使用pivot,如下所示:

SELECT *
FROM population
PIVOT (AVG(Total) for Data_Type IN ('Group1','Group2','Group3'))
SELECT *
FROM population
PIVOT (AVG(Total) for Data_Type IN (SELECT Data_Type FROM population))
这很好,但我不想每次添加新列或更改一个列(即Group4、5、6等)时都进行编辑,因此我尝试了如下子查询:

SELECT *
FROM population
PIVOT (AVG(Total) for Data_Type IN ('Group1','Group2','Group3'))
SELECT *
FROM population
PIVOT (AVG(Total) for Data_Type IN (SELECT Data_Type FROM population))
这将导致以下错误:ORA-00936:缺少表达式

经过一些研究,我似乎可以使用XML生成结果,因此我尝试了以下方法:

SELECT *
FROM population
PIVOT XML(AVG(Total) for Data_Type IN (ANY))

这实际上生成了所需的数据,但是是XML格式的。所以我的问题是,如何在PL SQL Developer中将XML结果转换为标准表格式?或者,如果我想将生成的XML文件放入Crystal Reports之类的工具中,我需要有一个用于这些结果的模式文件。在SQL.

< P>中是否可以很容易自动生成的东西?你会考虑使用流水线函数来实现你的目标吗? 我已经写了一个这样一个函数的例子。该示例基于Tom Kyte的文章中的表、示例数据和PIVOT查询,您可以在他的网站上找到:

该示例的工作原理如下

我们创建两种类型:

t_pivot_test_obj-类型,它保存我们要从XML检索的列 t_pivot_test_obj_选项卡-上述对象的嵌套表类型。 然后,我们创建一个管道函数,其中包含带有PIVOT的查询,该函数生成XML,这样您就不必硬编码要透视的值。此函数从生成的XML中提取数据,并在动态生成时将管道行传递给调用查询-它们不是一次生成的,这对性能很重要

最后,编写一个从该函数中选择记录的查询,最后就是此类查询的一个示例

创建表pivot\u测试 身份证号码 客户识别号, 产品代码VARCHAR25, 数量编号 ; 将测试值1,1,'A',10插入枢轴_; 将测试值2,1,'B',20插入枢轴_; 将测试值3,1,'C',30插入枢轴_; 将测试值4,2,'A',40插入枢轴_; 将测试值5,2,'C',50插入枢轴_; 将测试值6,3,'A',60插入枢轴_; 插入枢轴_测试值7,3,'B',70; 插入枢轴_测试值8,3,'C',80; 插入枢轴_测试值9,3,'D',90; 插入枢轴_测试值10,4,'A',100; 犯罪 创建类型t_pivot_test_obj作为对象 客户识别号, 产品代码VARCHAR25, 数量总数 ; / 创建类型t_pivot_test_obj_选项卡是t_pivot_test_obj的表; / 创建或替换函数从xml提取返回数据透视测试对象选项卡流水线 像 v_xml-XMLTYPE; v_item_xml XMLTYPE; v_指数; v_总和_数量编号; 光标c_客户_项目为 选择客户标识、产品代码和xml 从中选择客户id、产品代码、数量 从枢轴试验 在选择不同的产品代码中,将XML SUMquantity透视为产品代码的总和数量 来自pivot_测试; 开始 -使用PIVOT循环查询返回的所有记录 对于c_客户_项目中的v_rec 环 v_xml:=v_rec.product_code_xml; v_指数:=1; -循环检查每个客户的所有项目元素 环 v_item_xml:=v_xml.EXTRACT'/PivotSet/item['| | | | |']; 当v_item_xml为空时退出; v_指数:=v_指数+1; 如果v_item_xml.EXTRACT“/item/column[@name=SUM_QUANTITY]/text”不为空,则 v_sum_数量:=v_item_xml.EXTRACT'/item/column[@name=sum_数量]/text'.getNumberVal; 其他的 v_总和_数量:=0; 如果结束; -最后,对于每个客户和项目,通过管道将行发送到调用查询 管排、枢轴、测试、objv、记录客户id、, v_item_xml.EXTRACT'/item/column[@name=PRODUCT_CODE]/text'.getStringVal, v_总和_数量; 端环; 端环; 终止 / 选择客户id、产品代码、合计数量 从表格从xml中提取 ; 输出:

CUSTOMER_ID            PRODUCT_CODE SUM_QUANTITY           
---------------------- ------------ ---------------------- 
1                      A            10                     
1                      B            20                     
1                      C            30                     
1                      D            0                      
2                      A            40                     
2                      B            0                      
2                      C            50                     
2                      D            0                      
3                      A            60                     
3                      B            70                     
3                      C            80                     
3                      D            90                     
4                      A            100                    
4                      B            0                      
4                      C            0                      
4                      D            0                      

16 rows selected

您可以通过迭代生成第一条SQL语句的文本,然后单独执行该语句

如果您不介意使用准动态解决方案,您可以使用动态SQL(即EXECUTE IMMEDIATE)以这种方式安排视图的创建

据我所知,Crystal Report需要提前知道列名

编辑以添加代码。我没有测试这个。还要注意的是,当SQL语句超过32KB时,无论实际的多字节字符数是多少,这都会中断

DECLARE
   sql_statement_ VARCHAR2(32767);
BEGIN
   sql_statement_ := 'CREATE OR REPLACE VIEW population_view AS ' ||
                     'SELECT * FROM population ' ||
                     'PIVOT (AVG(total) FOR data_type IN (';
   FOR rec_ IN (SELECT DISTINCT data_type FROM population) LOOP
      sql_statement_ := sql_statement_ ||
                        '''' || REPLACE(rec_.data_type, '''', '''''') || ''', ';
   END LOOP;
   /* trim last comma and space */
   sql_statement_ = SUBSTR(1, sql_statement_, LENGTH(sql_statement_) - 2);
   /* close statement */
   sql_statement_ = sql_statement_ || ')) WITH READ ONLY';
   /* Rub your rabbit's foot, scatter garlic, and grab your four leaf clover.
      This could hurt if we didn't properly handle injection above. */
   EXECUTE IMMEDIATE sql_statement_;
END;
/