使用动态 PIVOT 时出现 PL/SQL 错误
使用动态 PIVOT 时出现 PL/SQL 错误
我正在尝试创建一个动态的PIVOT命令,因为我不知道在"in"子句中需要多少列。这种方法的问题是,尽管预期的值匹配,但在执行完execute immediate之后,我收到一个数据类型不一致的错误。我还尝试使用sys_refcursor,但是发生了同样的错误。可能是什么原因呢?
代码如下:
set serveroutput on; declare storage_var clob; storage_query clob; type table_model is table of varchar2(100) index by pls_integer; tabl table_model; begin SELECT DISTINCT LISTAGG('''' || scd_local.descricao || '''',',') WITHIN GROUP (ORDER BY scd_local.descricao) INTO storage_var FROM scd_local; --创建一个用于在pivot命令中使用的值列表 storage_query := 'select * from (select doc.nome, loc.descricao from scd_documento doc, scd_local_doc doc_loc, scd_local loc where doc.nome = doc_loc.id_doc and loc.id = doc_loc.id_local order by 1, 2) pivot (max(descricao) for descricao in ( ' || storage_var || ' ))'; dbms_output.put_line(storage_query); execute immediate storage_query bulk collect into tabl; --出现错误:"数据类型不一致:预期%s,实际%s" for i in 1.. tabl.count loop dbms_output.put_line(tabl(i)); end loop; end; /
模型如下:

在使用动态PIVOT时,PL/SQL出现错误的原因是无法获取游标的未知列结构。然而,如果你只是想通过DBMS_OUTPUT
将结果发送回客户端,你实际上可以通过使用DBMS_OUTPUT.RETURN_RESULTS
来实现。
下面是一个修改过的PL/SQL块的示例,它打印了来自DBA_OBJECTS
的用户和对象类型的交叉表,每个单元格显示给定类型的对象由给定用户拥有的数量。
declare storage_var clob; storage_query clob; l_ref_cur SYS_REFCURSOR; begin SELECT LISTAGG('''' || o.object_type || '''',',') WITHIN GROUP ( ORDER BY o.object_type) INTO storage_var FROM ( SELECT DISTINCT OBJECT_TYPE FROM dba_objects ) o; storage_query := 'select * from ( SELECT owner, object_type FROM dba_objects ) o pivot (count(*) for object_type in (' || storage_var || ')) order by 1'; dbms_output.put_line(storage_query); OPEN l_ref_cur FOR storage_query; dbms_sql.return_result(l_ref_cur); end; /
在Oracle 11g中,似乎不存在dbms_sql.return_result函数。
不,它是12c的一个功能。
可以参考stackoverflow.com/a/34075462/5174436上的链接,了解如何使用DBMS_SQL处理类似于您的动态游标。它在11g中也可以工作。
问题的原因是动态查询的结果具有可变数量的列,所以无法提前知道结果将具有多少列。因此,无法将结果获取到固定数量的结构中,因为在编译时无法知道需要使用多少个变量来获取结果。
解决方法是使用动态SQL来执行查询,并将结果存储在一个游标中。然后,可以使用游标的FETCH语句来逐行获取结果,并将每一行的数据存储在一个结构中。这样,就可以处理动态结果集中的可变数量的列。
以下是解决方法的示例代码:
DECLARE -- 定义游标和结果集的变量 TYPE ResultCursor IS REF CURSOR; result_set ResultCursor; -- 定义用于存储结果的结构 TYPE ResultRecord IS RECORD ( column1 NUMBER, column2 VARCHAR2(50), -- 添加更多的列定义... ); result_row ResultRecord; BEGIN -- 执行动态查询并将结果存储在游标中 OPEN result_set FOR 'SELECT column1, column2 FROM your_table'; -- 循环获取结果集中的每一行数据 LOOP -- 从游标中获取一行数据并存储在结构中 FETCH result_set INTO result_row; -- 判断是否还有更多的行数据 EXIT WHEN result_set%NOTFOUND; -- 处理获取到的行数据,可以根据需要执行其他操作 -- ... -- 输出获取到的行数据 DBMS_OUTPUT.PUT_LINE('Column 1: ' || result_row.column1); DBMS_OUTPUT.PUT_LINE('Column 2: ' || result_row.column2); END LOOP; -- 关闭游标 CLOSE result_set; END;
通过使用动态SQL和游标,可以解决在使用动态PIVOT时出现的PL/SQL错误。这种方法可以处理动态结果集中的可变数量的列,并且可以根据需要执行其他操作。