【发布时间】:2021-02-18 10:17:35
【问题描述】:
当没有要透视的行时,透视查询返回NULL。
在以下示例中,id=2 缺少 COLOR 属性。
with src_data (id, attr_name, attr_id, attr_type) as (
select 1, 'ITALY', 'IT', 'COUNTRY' FROM DUAL UNION ALL --
select 1, 'GREEN', 'G', 'COLOR' FROM DUAL UNION ALL --
select 1, 'BIG', 'B', 'SIZE' FROM DUAL UNION ALL --
select 2, 'FRANCE', 'FR', 'COUNTRY' FROM DUAL UNION ALL --
select 2, 'SMALL', 'S', 'SIZE' FROM DUAL --
)
select * from src_data
PIVOT (MAX(ATTR_NAME) AS NAME, MAX(ATTR_ID) AS ID --
FOR attr_type IN ('COUNTRY' AS "COUNTRY", 'COLOR' AS "COLOR", 'SIZE' AS "SIZE"));
结果是
| ID | COUNTRY_NAME | COUNTRY_ID | COLOR_NAME | COLOR_ID | SIZE_NAME | SIZE_ID |
|---|---|---|---|---|---|---|
| 1 | ITALY | IT | GREEN | G | BIG | B |
| 2 | FRANCE | FR | NULL | NULL | SMALL | S |
我想用特定值替换那些空值(例如,使用 N/D 作为名称,使用 -1 作为 ID)。
天真的尝试不起作用
-
PIVOT (NVL(MAX(ATTR_NAME), 'N/D') AS NAME ...给ORA-56902: expect aggregate function inside pivot operation -
PIVOT (MAX(NVL(ATTR_NAME, 'N/D')) AS NAME ...仍然给出空结果。我的解释是,甚至从未调用过 NVL,因为根本没有要调用的行(ATTR_TYPE = 'COLOR' AND ID = 2)
我看到的非常丑陋的解决方案是
- 在
PIVOT生成的所有列上添加特定的NVL逻辑。我的真实案例有 14 个这样的列。 - 向
PIVOT输入添加虚假行以涵盖此类情况
有更好的想法吗?
--- 编辑---
看起来本机 pivot 无法做到这一点。我能做的最好的就是用外连接添加缺失的行,并在外连接返回的真实NULLs 上添加nvl
with src_data (id, attr_name, attr_id, attr_type) as (
select 1, 'ITALY', 'IT', 'COUNTRY' FROM DUAL UNION ALL
select 1, 'GREEN', 'G', 'COLOR' FROM DUAL UNION ALL
select 1, 'BIG', 'B', 'SIZE' FROM DUAL UNION ALL
select 2, 'FRANCE', 'FR', 'COUNTRY' FROM DUAL UNION ALL
select 2, 'SMALL', 'S', 'SIZE' FROM DUAL
),
src_ids_types as (
select src_ids.id, src_types.attr_type
from (select distinct id from src_data) src_ids
cross join (select distinct attr_type from src_data) src_types
),
full_data as (
select sit.id, sit.attr_type, d.attr_name, d.attr_id
from src_ids_types sit
left outer join src_data d on d.id = sit.id and d.attr_type = sit.attr_type
)
select *
from full_data d
PIVOT (MAX(NVL(ATTR_NAME, 'N/D')) AS NAME, MAX(NVL(ATTR_ID, -1)) AS ID --
FOR attr_type IN ('COUNTRY' AS "COUNTRY", 'COLOR' AS "COLOR", 'SIZE' AS "SIZE"))
【问题讨论】:
-
在外部
SELECT子句中使用NVL()有什么问题?无论如何,SELECT *通常是一种不好的做法。明确命名列,并根据需要使用NVL()。 -
@mathguy,这很难看,因为您必须为枢轴返回的所有列重复 NVL 特定逻辑。如果你添加一个新的属性类型,你需要记住在最终选择中再添加一次。
-
抱歉,您的理由没有道理。你说你想对
name列使用N/D,对id列使用-1,所以“占位符”是与列相关的。如果您添加一个新的“属性类型”(无论这意味着什么),您将必须为该列说明您想要的null的“占位符” - 您将在哪里执行此操作?甚至在知道您是否、何时以及添加什么“属性类型”之前?另一方面,如果您只想在 all 列中为null显示N/D,那么在客户端程序中执行此操作要容易得多( SQL Developer、SQL*Plus 等) -
@mathguy 我不确定我是否理解您的评论。每个属性类型(例如国家/地区)都有一个 ID 和一个 NAME,它们是具有不同语义的不同列。当我旋转它们时,我希望将生成的 NULL 值转换为名称的
N/D或 ID 的-1(数据仓库设计有要求)。当给定 ID 的属性不存在时,我们如何应用此逻辑?在ID=2的示例中,我们没有COLOR属性。我天真地认为PIVOT (NVL(MAX(ATTR_NAME), 'N/D') AS NAME会给出正确的结果,但这不起作用。 -
您的尝试很容易得到纠正。您所旋转的必须始终是一个聚合函数,而
nvl不是。诀窍是将nvl移动到max中(效率有点低,但它会起作用):pivot( max(nvl(attr_name, 'N/D'/)) for ...)但这会将'N/D'而不是null放在every 列中输出。我的评论是,你似乎不想要那样。您希望null的替换因列而异。将来某个时候,您将添加一个新属性(它是旋转后的新列),并带有一个全新的null替换。您现在如何编写代码?