oraclesql中的动态透视
。。。枢轴(X中B的总和(A)) 现在B是数据类型varchar2,X是由逗号分隔的varchar2值字符串。oraclesql中的动态透视,sql,oracle,pivot,dynamic-pivot,Sql,Oracle,Pivot,Dynamic Pivot,。。。枢轴(X中B的总和(A)) 现在B是数据类型varchar2,X是由逗号分隔的varchar2值字符串。 X的值是从同一表的列(如CL)中选择不同的值。这样,pivot查询就开始工作了 但问题是,每当CL列中有新值时,我都必须手动将其添加到字符串X中 我尝试用CL中的select distinct值替换X。但查询未运行。 我之所以这样认为,是因为替换X时,我们需要用逗号分隔值。 然后我创建了一个函数来返回与字符串X匹配的精确输出。但查询仍然没有运行。 显示的错误消息如“缺少righr Pa
X的值是从同一表的列(如CL)中选择不同的值。这样,pivot查询就开始工作了 但问题是,每当CL列中有新值时,我都必须手动将其添加到字符串X中 我尝试用CL中的select distinct值替换X。但查询未运行。
我之所以这样认为,是因为替换X时,我们需要用逗号分隔值。
然后我创建了一个函数来返回与字符串X匹配的精确输出。但查询仍然没有运行。
显示的错误消息如“缺少righr Paranthes”、“文件通信通道结束”等
我尝试了pivot xml,而不是pivot,查询运行,但给出了oraxxx等根本没有值的Vlaue 也许我没有正确地使用它。
你能告诉我一些用动态值创建透视图的方法吗?你不能在透视图子句的
中的中放入非常量字符串。
您可以使用PivotXML来实现这一点
发件人:
子查询子查询仅与XML关键字结合使用。
指定子查询时,将使用该子查询找到的所有值
旋转用
应该是这样的:
select xmlserialize(content t.B_XML) from t_aa
pivot xml(
sum(A) for B in(any)
) t;
SELECT
*
FROM
table(
pivot('
SELECT DISTINCT
P.proj_id,
REPLACE(substr(T.UDF_TYPE_LABEL, 1, 30), '''''''','','') as Attribute,
CASE
WHEN V.udf_text is null and V.udf_date is null and V.udf_number is NOT null THEN to_char(V.udf_number)
WHEN V.udf_text is null and V.udf_date is NOT null and V.udf_number is null THEN to_char(V.udf_date)
WHEN V.udf_text is NOT null and V.udf_date is null and V.udf_number is null THEN V.udf_text
ELSE NULL END
AS VALUE
FROM
project P
LEFT JOIN UDFVALUE V ON P.proj_id = V.proj_id
LEFT JOIN UDFTYPE T ON V.UDF_TYPE_ID = T.UDF_TYPE_ID
WHERE
P.delete_session_id IS NULL AND
T.TABLE_NAME = ''PROJECT''
')
)
您还可以使用子查询代替ANY
关键字:
select xmlserialize(content t.B_XML) from t_aa
pivot xml(
sum(A) for B in (select cl from t_bb)
) t;
如果不使用PIVOT XML,则无法将动态语句放入PIVOT的in语句中,因为PIVOT XML会输出一些不理想的输出。但是,您可以创建IN字符串并将其输入到语句中
首先,这是我的样品表
myNumber myValue myLetter
---------- ---------- --------
1 2 A
1 4 B
2 6 C
2 8 A
2 10 B
3 12 C
3 14 A
首先设置要在in语句中使用的字符串。在这里,您将字符串放入“str_in_statement”。我们正在使用和来设置字符串
clear columns
COLUMN temp_in_statement new_value str_in_statement
SELECT DISTINCT
LISTAGG('''' || myLetter || ''' AS ' || myLetter,',')
WITHIN GROUP (ORDER BY myLetter) AS temp_in_statement
FROM (SELECT DISTINCT myLetter FROM myTable);
您的字符串将如下所示:
'A' AS A,'B' AS B,'C' AS C
现在在PIVOT查询中使用String语句
SELECT * FROM
(SELECT myNumber, myLetter, myValue FROM myTable)
PIVOT (Sum(myValue) AS val FOR myLetter IN (&str_in_statement));
以下是输出:
MYNUMBER A_VAL B_VAL C_VAL
---------- ---------- ---------- ----------
1 2 4
2 8 10 6
3 14 12
但也有一些限制。最多只能连接4000字节的字符串 对于以后的读者,这里是另一个解决方案
允许像这样的查询
select * from table( pivot( 'select deptno, job, count(*) c from scott.emp group by deptno,job' ) )
我使用了上面的方法(Anton PL/SQL自定义函数pivot()),它完成了这项工作!由于我不是一名专业的Oracle开发人员,我已经完成了以下简单步骤:
1) 下载zip包以在其中找到pivotFun.sql。
2) 运行pivotFun.sql一次以创建新函数
3) 在普通SQL中使用该函数
只需小心使用动态列名称。在我的环境中,我发现列名限制为30个字符,不能包含一个引号。所以,我的问题是这样的:
select xmlserialize(content t.B_XML) from t_aa
pivot xml(
sum(A) for B in(any)
) t;
SELECT
*
FROM
table(
pivot('
SELECT DISTINCT
P.proj_id,
REPLACE(substr(T.UDF_TYPE_LABEL, 1, 30), '''''''','','') as Attribute,
CASE
WHEN V.udf_text is null and V.udf_date is null and V.udf_number is NOT null THEN to_char(V.udf_number)
WHEN V.udf_text is null and V.udf_date is NOT null and V.udf_number is null THEN to_char(V.udf_date)
WHEN V.udf_text is NOT null and V.udf_date is null and V.udf_number is null THEN V.udf_text
ELSE NULL END
AS VALUE
FROM
project P
LEFT JOIN UDFVALUE V ON P.proj_id = V.proj_id
LEFT JOIN UDFTYPE T ON V.UDF_TYPE_ID = T.UDF_TYPE_ID
WHERE
P.delete_session_id IS NULL AND
T.TABLE_NAME = ''PROJECT''
')
)
可以很好地处理多达100万条记录 对于OP提出的问题,我不打算给出确切的答案,相反,我将只描述如何实现动态轴心
这里我们必须使用动态sql,首先将列值检索到变量中,然后在动态sql中传递变量
示例
我们有一张如下的桌子
如果我们需要将YR
列中的值显示为列名,以及QTY
中这些列中的值,那么我们可以使用以下代码
declare
sqlqry clob;
cols clob;
begin
select listagg('''' || YR || ''' as "' || YR || '"', ',') within group (order by YR)
into cols
from (select distinct YR from EMPLOYEE);
sqlqry :=
'
select * from
(
select *
from EMPLOYEE
)
pivot
(
MIN(QTY) for YR in (' || cols || ')
)';
execute immediate sqlqry;
end;
/
结果
如果需要,还可以创建临时表,并在该临时表中执行select查询以查看结果。很简单,只需在上面的代码中添加创建表TABLENAME作为
sqlqry :=
'
CREATE TABLE TABLENAME AS
select * from
使用动态查询
测试代码如下
如果不使用PIVOT-XML,就不能将动态语句放入PIVOT的in语句中,但是可以使用小技巧在PIVOT中使用动态语句。在PL/SQL中,在字符串值中,两个撇号等于一个撇号
declare
sqlqry clob;
search_ids varchar(256) := '''2016'',''2017'',''2018'',''2019''';
begin
search_ids := concat( search_ids,'''2020''' ); -- you can append new search id dynamically as you wanted
sqlqry :=
'
select * from
(
select *
from EMPLOYEE
)
pivot
(
MIN(QTY) for YR in (' || search_ids || ')
)';
execute immediate sqlqry;
end;
Oracle的SQL中没有直接的动态数据透视方法,除非它返回XML类型的结果。
对于非XML结果,可以通过创建SYS\u REFCURSOR
返回类型的函数来使用PL/SQL
- 使用条件聚合
CREATE OR REPLACE FUNCTION Get_Jobs_ByYear RETURN SYS_REFCURSOR IS
v_recordset SYS_REFCURSOR;
v_sql VARCHAR2(32767);
v_cols VARCHAR2(32767);
BEGIN
SELECT LISTAGG( 'SUM( CASE WHEN job_title = '''||job_title||''' THEN 1 ELSE 0 END ) AS "'||job_title||'"' , ',' )
WITHIN GROUP ( ORDER BY job_title )
INTO v_cols
FROM ( SELECT DISTINCT job_title
FROM jobs j );
v_sql :=
'SELECT "HIRE YEAR",'|| v_cols ||
' FROM
(
SELECT TO_NUMBER(TO_CHAR(hire_date,''YYYY'')) AS "HIRE YEAR", job_title
FROM employees e
JOIN jobs j
ON j.job_id = e.job_id
)
GROUP BY "HIRE YEAR"
ORDER BY "HIRE YEAR"';
OPEN v_recordset FOR v_sql;
DBMS_OUTPUT.PUT_LINE(v_sql);
RETURN v_recordset;
END;
/
带枢轴子句
CREATE OR REPLACE FUNCTION Get_Jobs_ByYear RETURN SYS_REFCURSOR IS
v_recordset SYS_REFCURSOR;
v_sql VARCHAR2(32767);
v_cols VARCHAR2(32767);
BEGIN
SELECT LISTAGG( ''''||job_title||''' AS "'||job_title||'"' , ',' )
WITHIN GROUP ( ORDER BY job_title )
INTO v_cols
FROM ( SELECT DISTINCT job_title
FROM jobs j );
v_sql :=
'SELECT *
FROM
(
SELECT TO_NUMBER(TO_CHAR(hire_date,''YYYY'')) AS "HIRE YEAR", job_title
FROM employees e
JOIN jobs j
ON j.job_id = e.job_id
)
PIVOT
(
COUNT(*) FOR job_title IN ( '|| v_cols ||' )
)
ORDER BY "HIRE YEAR"';
OPEN v_recordset FOR v_sql;
DBMS_OUTPUT.PUT_LINE(v_sql);
RETURN v_recordset;
END;
/
但是编码为ORA-01489的listag()
有一个缺点:只要第一个参数中的连接字符串超过4000个字符的长度,字符串连接的结果就会太长。在这种情况下,返回v_cols
变量值的查询可能会替换为嵌套在XMLAGG()
中的xmlement()
函数,例如
CREATE OR REPLACE FUNCTION Get_Jobs_ByYear RETURN SYS_REFCURSOR IS
v_recordset SYS_REFCURSOR;
v_sql VARCHAR2(32767);
v_cols VARCHAR2(32767);
BEGIN
SELECT RTRIM(DBMS_XMLGEN.CONVERT(
XMLAGG(
XMLELEMENT(e, 'SUM( CASE WHEN job_title = '''||job_title||
''' THEN 1 ELSE 0 END ) AS "'||job_title||'",')
).EXTRACT('//text()').GETCLOBVAL() ,1),',') AS "v_cols"
FROM ( SELECT DISTINCT job_title
FROM jobs j);
v_sql :=
'SELECT "HIRE YEAR",'|| v_cols ||
' FROM
(
SELECT TO_NUMBER(TO_CHAR(hire_date,''YYYY'')) AS "HIRE YEAR", job_title
FROM employees e
JOIN jobs j
ON j.job_id = e.job_id
)
GROUP BY "HIRE YEAR"
ORDER BY "HIRE YEAR"';
DBMS_OUTPUT.put_line(LENGTH(v_sql));
OPEN v_recordset FOR v_sql;
RETURN v_recordset;
END;
/
除非超过VARCHAR2类型的上限32767。最后一种方法也可能适用于Oracle 11g Release 2之前版本为的数据库,因为它们不包含listag()
函数
顺便说一句,在签出v_cols
期间,可以使用LISTAGG()
函数,即使对于生成的非常长的连接字符串,也不会出现ORA-01489错误,而如果数据库的版本为12.2+,则通过使用ON OVERFLOW TRUNCATE子句截断字符串的尾部,如
从SQL开发人员的命令行
或
从PL/SQL开发人员的测试窗口获取结果
设定
您可以使用开源程序在单个SQL语句中动态透视数据
安装包后,调用函数并以字符串形式传入SQL语句。SQL语句的最后一列定义值,倒数第二列定义列名。默认聚合函数是MAX,它适用于以下常见的实体属性值查询:
select * from table(method4.pivot(
q'[
select 'A' name, 1 value from dual union all
select 'B' name, 2 value from dual union all
select 'C' name, 3 value from dual
]'
));
A B C
- - -
1 2 3
该程序还通过参数p_AGGREGATE_函数支持不同的聚合函数,如果添加名为PIVOT_column_ID的列,则允许自定义列名顺序
包裹
BEGIN
:result := Get_Jobs_ByYear;
END;
select * from table(method4.pivot(
q'[
select 'A' name, 1 value from dual union all
select 'B' name, 2 value from dual union all
select 'C' name, 3 value from dual
]'
));
A B C
- - -
1 2 3