Oracle 必须知道 PARSING 阶段选择列表中的所有列。
这有几个后果
Oracle 无法在不重新解析的情况下更改查询的列列表。不管应该有什么影响——无论是某些列中的不同值列表还是其他东西。换句话说,如果您在示例中向 CCL 列添加了新值,您不能期望 Oracle 会在输出中添加新列。
在每个查询中,您必须明确指定选择列表中的所有列,除非您将"*" 与表别名一起使用。如果您使用"*",那么 Oracle 从元数据中获取列列表,如果您修改元数据(即在表上运行 DDL),那么 Oracle 会重新解析查询。
因此,处理“动态旋转”的最佳选择是在 UI 中旋转和格式化结果。但是,您可能需要考虑数据库中的一些选项。
生成带有旋转结果的 XML 并对其进行解析。
对 XML 进行透视,然后解析结果。在这种情况下,最终,您必须以一种或另一种方式指定透视列。
create table tablea(id, ccl, flag) as
(
select 1, 'john', 'x' from dual
union all select 1, 'adam', 'x' from dual
union all select 1, 'terry', null from dual
union all select 1, 'rob', 'x' from dual
union all select 2, 'john', 'x' from dual
);
在下面的示例中,您不必提供 CCL 的值列表,您指定的唯一文字是:
旋转表达式 (FLAG) 和用于旋转 (CCL) 的列。
SQL> select id, x.*
2 from tablea t
3 pivot xml (max(flag) flag for ccl in(any))
4 -- parsing output
5 , xmltable('/PivotSet' passing ccl_xml
6 columns
7 name1 varchar2(30) path '/PivotSet/item[1]/column[@name="CCL"]/text()',
8 value1 varchar2(30) path '/PivotSet/item[1]/column[@name="FLAG"]/text()',
9 name2 varchar2(30) path '/PivotSet/item[2]/column[@name="CCL"]/text()',
10 value2 varchar2(30) path '/PivotSet/item[2]/column[@name="FLAG"]/text()',
11 name3 varchar2(30) path '/PivotSet/item[3]/column[@name="CCL"]/text()',
12 value3 varchar2(30) path '/PivotSet/item[3]/column[@name="FLAG"]/text()',
13 name4 varchar2(30) path '/PivotSet/item[4]/column[@name="CCL"]/text()',
14 value4 varchar2(30) path '/PivotSet/item[4]/column[@name="FLAG"]/text()') x;
ID NAME1 VALUE NAME2 VALUE NAME3 VALUE NAME4 VALUE
---------- ----- ----- ----- ----- ----- ----- ----- -----
1 adam x john x rob x terry
2 john x
您可能已经注意到 2 个重要细节
可以只使用索引来获得相同的输出。
select id, x.*
from tablea
pivot xml (max(flag) flag for ccl in(any))
-- parsing output
, xmltable('/PivotSet' passing ccl_xml
columns
name1 varchar2(30) path '/PivotSet/item[1]/column[1]',
value1 varchar2(30) path '/PivotSet/item[1]/column[2]',
name2 varchar2(30) path '/PivotSet/item[2]/column[1]',
value2 varchar2(30) path '/PivotSet/item[2]/column[2]',
name3 varchar2(30) path '/PivotSet/item[3]/column[1]',
value3 varchar2(30) path '/PivotSet/item[3]/column[2]',
name4 varchar2(30) path '/PivotSet/item[4]/column[1]',
value4 varchar2(30) path '/PivotSet/item[4]/column[2]') x;
但输出中的每个透视列仍然有两列。
以下查询返回与您的示例完全相同的数据
SQL> select id, x.*
2 from tablea
3 pivot xml (max(flag) flag for ccl in(any))
4 -- parsing output
5 , xmltable('/PivotSet' passing ccl_xml
6 columns
7 john varchar2(30) path '/PivotSet/item[column="john"]/column[2]',
8 adam varchar2(30) path '/PivotSet/item[column="adam"]/column[2]',
9 terry varchar2(30) path '/PivotSet/item[column="terry"]/column[2]',
10 rob varchar2(30) path '/PivotSet/item[column="rob"]/column[2]') x;
ID JOHN ADAM TERRY ROB
---------- ----- ----- ----- -----
1 x x x
2 x
但是等等... CCL 的所有值都在查询中指定。这是因为列标题不能依赖表中的数据。那么,如果您可以硬编码 for 子句中的所有值并获得同样的成功,那么转向 XML 有什么意义呢?其中一个想法是Oracle SQL引擎转置查询结果,显示输出的工具只需要正确解析XML。因此,您将旋转逻辑分为两层。 XML 解析可以在 SQL 之外完成,例如在您的应用程序中。
ODCI 表接口
Anton's solution 的另一个答案中已经有一个链接。
您还可以查看示例here。
当然,Oracle 文档中对此进行了详细说明。
多态表函数
Oracle 18 中引入了一项更先进的技术 - Polymorphic Table Functions。
但同样,您不应期望在向 CCL 添加新值后查询的列列表会发生变化。只有重新解析后才能改变。有一种方法可以在每次执行之前强制进行硬解析,但这是另一个话题。
动态 SQL
最后,正如 cmets 中已经指出的那样,您可以使用良好的旧 DSQL。
第一步 - 根据表内容生成 SQL 语句。第二步 - 执行它。
SQL> var rc refcursor
SQL> declare
2 tmp clob;
3 sql_str clob := 'select * from tablea pivot (max(flag) for ccl in ([dynamic_list]))';
4 begin
5 select listagg('''' || ccl || ''' as ' || ccl, ',') within group(order by max(ccl))
6 into tmp
7 from tablea
8 group by ccl;
9 open :rc for replace(sql_str, '[dynamic_list]', tmp);
10 end;
11 /
PL/SQL procedure successfully completed.
SQL> print rc
ID ADAM JOHN ROB TERRY
---------- ----- ----- ----- -----
1 x x x
2 x