在 Oracle 中动态地将行转为列
Posted
技术标签:
【中文标题】在 Oracle 中动态地将行转为列【英文标题】:Pivoting rows into columns dynamically in Oracle 【发布时间】:2011-12-05 12:26:14 【问题描述】:我有以下名为 _kv 的 Oracle 10g 表:
select * from _kv
ID K V
---- ----- -----
1 name Bob
1 age 30
1 gender male
2 name Susan
2 status married
我想使用普通 SQL(不是 PL/SQL)将我的键转换为列,以便生成的表看起来像这样:
ID NAME AGE GENDER STATUS
---- ----- ----- ------ --------
1 Bob 30 male
2 Susan married
查询的列数应与表中存在的唯一 K
s 一样多(没有那么多)
在运行查询之前无法知道可能存在哪些列。
我试图避免运行初始查询来以编程方式构建最终查询。
空白单元格可能是 null 或空字符串,这并不重要。
我使用的是 Oracle 10g,但 11g 解决方案也可以。
当你知道你的旋转列可能被称为什么时,有很多例子,但我就是找不到适用于 Oracle 的通用旋转解决方案。
谢谢!
【问题讨论】:
亲爱的上帝,那是...元数据库吗?? 你应该阅读这个:***.com/questions/7340422/… 在阅读有关“实体属性值”表的文章时,我总是觉得很幽默:asktom.oracle.com/pls/apex/… 不幸的是,这不是我的设计(_kv 表就是一个例子)...我需要从该表中生成一些实时报告,任何过滤或排序的尝试都会让我发疯。 哇,如果您想要一个键值对数据库,那么使用 Oracle 的代价太大了。查看 Berkeley db(仍然免费,但具有讽刺意味的是由 Oracle 控制;)。更好的是,重新设计那个吸盘……不惜一切代价。祝你好运 【参考方案1】:首先,再次使用pivot xml
动态旋转需要被解析。我们还有另一种方法,将列名存储在一个变量中,并将它们传递到动态 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;
/
结果
【讨论】:
对listagg('''' || YR || ''' as "' || YR || '"', ',')
有疑问。为什么不listagg(YR, ',')
? '''' ||
在那里做什么?【参考方案2】:
为了处理可能存在多个值的情况(在您的示例中为 v),我使用 PIVOT
和 LISTAGG
:
SELECT * FROM
(
SELECT id, k, v
FROM _kv
)
PIVOT
(
LISTAGG(v ,',')
WITHIN GROUP (ORDER BY k)
FOR k IN ('name', 'age','gender','status')
)
ORDER BY id;
由于您需要动态值,因此请使用动态 SQL 并传入通过在调用数据透视语句之前对表数据运行 select 确定的值。
【讨论】:
【参考方案3】:恰好有一个枢轴任务。以下对我有用,正如刚才在 11g 上测试的那样:
select * from
(
select ID, COUNTRY_NAME, TOTAL_COUNT from ONE_TABLE
)
pivot(
SUM(TOTAL_COUNT) for COUNTRY_NAME in (
'Canada', 'USA', 'Mexico'
)
);
【讨论】:
【参考方案4】:Oracle 11g 提供了一个PIVOT
操作,可以满足您的需求。
Oracle 11g 解决方案
select * from
(select id, k, v from _kv)
pivot(max(v) for k in ('name', 'age', 'gender', 'status')
(注意:我没有 11g 的副本来测试它,所以我没有验证它的功能)
我从:http://orafaq.com/wiki/PIVOT获得此解决方案
编辑——pivot xml 选项(也是 Oracle 11g)
显然,当您不知道您可能需要的所有可能的列标题时,还有一个 pivot xml
选项。 (请参阅位于页面底部附近的 XML TYPE 部分,位于http://www.oracle.com/technetwork/articles/sql/11g-pivot-097235.html)
select * from
(select id, k, v from _kv)
pivot xml (max(v)
for k in (any) )
(注意:和以前一样,我没有 11g 的副本来测试它,所以我没有验证它的功能)
Edit2: 将 pivot
和 pivot xml
语句中的 v
更改为 max(v)
,因为它应该按照其中一个 cmets 中所述进行聚合。我还添加了in
子句,这对于pivot
来说不是可选的。当然,必须在in
子句中指定值会破坏拥有完全动态的枢轴/交叉表查询的目标,而这正是该问题发布者的愿望。
【讨论】:
pivot(v for k)
部分在 11g 中不起作用,for
需要一个 in
,我不知道在 in
子句中添加什么
@dave - PIVOT 操作仅适用于一组固定的列。您的查询无法突然返回更多列,因为向表中添加了额外的行。
@JustinCave , @ojosilva 我认为in
子句可能是可选的,因为如果您想将列限制为特定值,并且如果需要所有值,则可以省略它。显然有一个pivot xml
选项可以处理这个问题(请参阅更新的答案)。
xml 枢轴在 v:(max(v) for k in (any))
上添加了一个聚合,但结果是一个名为 K_XML
的 clob 列中的 xml - 这不是我所需要的。以上是关于在 Oracle 中动态地将行转为列的主要内容,如果未能解决你的问题,请参考以下文章
根据 Oracle 10g PL/SQL 中的内容将行转为列