... pivot((X)中B的总和(A))
现在B的数据类型为varchar2,X是由逗号分隔的varchar2值的字符串 X的值是来自同一表的列(比如CL)的不同值。这种方式使用数据透视查询。
但问题是,只要列CL中有新值,我就必须手动将其添加到字符串X中。
我尝试用CL中的select distinct值替换X.但是查询没有运行 我之所以感觉到的原因是,为了替换X,我们需要用逗号分隔的值 然后我创建了一个函数来返回精确的输出以匹配字符串X.但查询仍然没有运行 显示的错误消息类似于“丢失较大的parantheses”,“文件结束通信通道”等 我尝试使用pivot xml而不仅仅是pivot,查询运行但是提供了类似oraxxx等的值,这些值根本没有值。
也许我没有正确使用它 你能告诉我一些用动态值创建枢轴的方法吗?
答案 0 :(得分:21)
如果不使用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”。我们正在使用COLUMN NEW_VALUE和LISTAGG来设置字符串。
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个字节的字符串。
答案 1 :(得分:9)
您不能在pivot子句的IN
子句中放置非常量字符串
您可以使用Pivot XML。
子查询子查询仅与XML关键字一起使用。 指定子查询时,将使用子查询找到的所有值 用于旋转
它应该是这样的:
select xmlserialize(content t.B_XML) from t_aa
pivot xml(
sum(A) for B in(any)
) t;
您还可以使用子查询而不是ANY
关键字:
select xmlserialize(content t.B_XML) from t_aa
pivot xml(
sum(A) for B in (select cl from t_bb)
) t;
答案 2 :(得分:4)
对于后来的读者,这是另一种解决方案 https://technology.amis.nl/2006/05/24/dynamic-sql-pivoting-stealing-antons-thunder/
允许像
这样的查询select * from table( pivot( 'select deptno, job, count(*) c from scott.emp group by deptno,job' ) )
答案 3 :(得分:2)
使用DYNAMIC QUERY
测试代码在
下面-- DDL for Table TMP_TEST
--------------------------------------------------------
CREATE TABLE "TMP_TEST"
( "NAME" VARCHAR2(20),
"APP" VARCHAR2(20)
);
/
SET DEFINE OFF;
Insert into TMP_TEST (NAME,APP) values ('suhaib','2');
Insert into TMP_TEST (NAME,APP) values ('suhaib','1');
Insert into TMP_TEST (NAME,APP) values ('shahzad','3');
Insert into TMP_TEST (NAME,APP) values ('shahzad','2');
Insert into TMP_TEST (NAME,APP) values ('shahzad','5');
Insert into TMP_TEST (NAME,APP) values ('tariq','1');
Insert into TMP_TEST (NAME,APP) values ('tariq','2');
Insert into TMP_TEST (NAME,APP) values ('tariq','6');
Insert into TMP_TEST (NAME,APP) values ('tariq','4');
/
CREATE TABLE "TMP_TESTAPP"
( "APP" VARCHAR2(20)
);
SET DEFINE OFF;
Insert into TMP_TESTAPP (APP) values ('1');
Insert into TMP_TESTAPP (APP) values ('2');
Insert into TMP_TESTAPP (APP) values ('3');
Insert into TMP_TESTAPP (APP) values ('4');
Insert into TMP_TESTAPP (APP) values ('5');
Insert into TMP_TESTAPP (APP) values ('6');
/
create or replace PROCEDURE temp_test(
pcursor out sys_refcursor,
PRESULT OUT VARCHAR2
)
AS
V_VALUES VARCHAR2(4000);
V_QUERY VARCHAR2(4000);
BEGIN
PRESULT := 'Nothing';
-- concating activities name using comma, replace "'" with "''" because we will use it in dynamic query so "'" can effect query.
SELECT DISTINCT
LISTAGG('''' || REPLACE(APP,'''','''''') || '''',',')
WITHIN GROUP (ORDER BY APP) AS temp_in_statement
INTO V_VALUES
FROM (SELECT DISTINCT APP
FROM TMP_TESTAPP);
-- designing dynamic query
V_QUERY := 'select *
from ( select NAME,APP
from TMP_TEST )
pivot (count(*) for APP in
(' ||V_VALUES|| '))
order by NAME' ;
OPEN PCURSOR
FOR V_QUERY;
PRESULT := 'Success';
Exception
WHEN OTHERS THEN
PRESULT := SQLcode || ' - ' || SQLERRM;
END temp_test;
答案 4 :(得分:1)
我使用了上面的方法(Anton PL / SQL自定义函数pivot())并完成了这项工作!由于我不是专业的Oracle开发人员,所以这些都是我已经完成的简单步骤:
1)下载zip包以在那里找到pivotFun.sql。 2)运行pivotFun.sql以创建一个新函数 3)在普通SQL中使用该函数。
请注意动态列名称。在我的环境中,我发现列名限制为30个字符,并且不能包含单个引号。所以,我的查询现在是这样的:
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万条记录。
答案 5 :(得分:0)
我不会完全回答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;
/
结果
如果需要,您还可以创建临时表并在该临时表中执行选择查询以查看结果。它很简单,只需在上面的代码中添加CREATE TABLE TABLENAME AS
。
sqlqry :=
'
CREATE TABLE TABLENAME AS
select * from
答案 6 :(得分:0)
如果不使用PIVOT XML,则不能在PIVOT的IN语句中放置动态语句,但是 您可以使用小型Technic 来使用动态语句在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;
答案 7 :(得分:0)
除非在SQL中返回XML类型结果,否则没有直接的方法可用于动态进行Oracle SQL数据透视。
对于非XML结果,可以通过创建SYS_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;
/
带有 PIVOT 条款
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;
/
但是LISTAGG()
的缺点编码为 ORA-01489: 字符串连接的结果太长第一个参数中的并置字符串超过了 4000 个字符的长度。在这种情况下,返回v_cols
变量值的查询可能被嵌套在XMLELEMENT()
中的XMLAGG()
函数替换,例如
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第2版之前的数据库,因为它们不包含LISTAGG()
函数。
顺便说一句,LISTAGG()
函数可以在v_cols
的结帐期间使用,即使生成的很长的串联字符串也不会在结尾部分出现 ORA-01489 错误的情况下使用如果数据库的版本是 12.2 + ,例如
LISTAGG( <concatenated string>,',' ON OVERFLOW TRUNCATE 'THE REST IS TRUNCATED' WITHOUT COUNT )
该函数可以调用为
VAR rc REFCURSOR EXEC :rc := Get_Jobs_ByYear; PRINT rc
从 SQL Developer 的命令行
或
从 PL / SQL Developer 的 Test 窗口中BEGIN :result := Get_Jobs_ByYear; END;
以获得结果 设置。
答案 8 :(得分:0)
您可以使用开源程序 Method4.Pivot 在单个 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_FUNCTION 支持不同的聚合函数,如果添加名为 PIVOT_COLUMN_ID 的列,则允许自定义列名顺序。
该包使用类似于 Anton 的数据透视表的 Oracle Data Cartridge 方法,但 Method4.Pivot 有几个重要优点:
但大多数用户仍然最好在应用程序层或使用数据透视 XML 选项创建动态数据透视。