【问题标题】:Handle NULL value in UNPIVOT在 UNPIVOT 中处理 NULL 值
【发布时间】:2014-06-07 22:16:31
【问题描述】:

我能够取消透视表,但结果中不包含空值。

create table pivot_task
(
age int null,
[a] numeric(8,2),
[b] numeric(8,2),
[c] numeric(8,2),
[d] numeric(8,2),
[e] numeric(8,2)
);

select * from pivot_task;

insert into pivot_task values (18, 0.5, null, 0.6, 1.21, 1.52),
(19, 7.51, 6.51, 5.51, null, 3.53),
(20, 4.52, 4.52, 6.52, 3.53, null);


select age, [over], [av]
from pivot_task
unpivot
(
 [av]
 for [over] in ([a], [b], [c], [d], [e])
) a;

您可以在 http://sqlfiddle.com/#!6/2ab59/1 上看到 18 岁 [over] b 的结果,并且它的 null 值丢失了我想在每次遇到 null 时也包含 null。

我发现用不同的值替换 null 然后替换所有那些不变的不同值的方法对我的工作来说是不可行的。我只想包含在 unpivot 中。

【问题讨论】:

  • '18 岁以上 [over]'?这是否包含特别图形或暴力的 SQL?
  • @Westie 你是说在 pivot_task 表中用其他 unpivot 替换空值,但我无法修改源表 pivot_task。
  • 如果您的数字属于指定范围(表示非负数),您可以解决此问题。 +1 格式正确的问题。
  • 不幸的是,UNPIVOT 被记录以消除NULLs - “UNPIVOT 输入中的空值在输出中消失” - 所以对于这个问题没有“仅反透视”解决方案。

标签: sql sql-server sql-server-2008 sql-server-2012 unpivot


【解决方案1】:

这很丑陋,但并不依赖于必须找到 NULL 的带外替换:

declare @pivot_task table
(
age int null,
[a] numeric(8,2),
[b] numeric(8,2),
[c] numeric(8,2),
[d] numeric(8,2),
[e] numeric(8,2)
);

insert into @pivot_task values (18, 0.5, null, 0.6, 1.21, 1.52),
(19, 7.51, 6.51, 5.51, null, 3.53),
(20, 4.52, 4.52, 6.52, 3.53, null);


select a.age, pmu.[over], [av]
from (select 'a' as [over] union all select 'b' union all select 'c'
        union all select 'd' union all select 'e') pmu
cross join (select age from @pivot_task) as a
left join
@pivot_task pt
unpivot
(
 [av]
 for [over] in ([a], [b], [c], [d], [e])
) ex
on pmu.[over] = ex.[over] and
   a.age = ex.age

结果:

age         over av
----------- ---- ---------------------------------------
18          a    0.50
18          b    NULL
18          c    0.60
18          d    1.21
18          e    1.52
19          a    7.51
19          b    6.51
19          c    5.51
19          d    NULL
19          e    3.53
20          a    4.52
20          b    4.52
20          c    6.52
20          d    3.53
20          e    NULL

但是如果你沿着这条路走,你可以完全消除UNPIVOT

select a.age, pmu.[over],
      CASE pmu.[over]
           WHEN 'a' THEN a.a
           WHEN 'b' THEN a.b
           WHEN 'c' THEN a.c
           WHEN 'd' THEN a.d
           WHEN 'e' THEN a.e
        END [av]
from (select 'a' as [over] union all select 'b' union all select 'c'
        union all select 'd' union all select 'e') pmu
cross join @pivot_task as a

【讨论】:

  • 是的,消除 UNPIVOT 的第二个查询完全有效,但您能解释一下这个查询是如何工作的吗?
  • A CROSS JOIN 将左表中的每一行与右表中的每一行配对。在这种情况下,左表只是包含a - e 的五行集合,右表是您原来的pivot_task 表。所以我们最终得到年龄18 行与a - e 中的每一个配对,19 行与a - e 配对,... 然后在CASE 表达式中,我们只需选择与pivot_task 行当前配对的字母匹配的列。
【解决方案2】:

试试这个,它将在 unpivot 之前用 10000000 替换所有空值,这不是 numeric(8,2) 中可接受的数字,因此该值将不存在。那么 unpivot 之后的值会被 null 代替:

;WITH x as
(
select 
age,
coalesce(cast(a as numeric(9,2)), 10000000) a,
coalesce(cast(b as numeric(9,2)), 10000000) b,
coalesce(cast(c as numeric(9,2)), 10000000) c,
coalesce(cast(d as numeric(9,2)), 10000000) d,
coalesce(cast(e as numeric(9,2)), 10000000) e
from pivot_task
)
select age, [over], nullif([av], 10000000) av
from x
unpivot
(
 [av]
 for [over] in ([a], [b], [c], [d], [e])
) a;

【讨论】:

    【解决方案3】:

    在取消透视之前对所有列使用 ISNULL(columnname ,0) http://technet.microsoft.com/en-us/library/ms184325.aspx

    如下所示。

    Select Age, Data, Case When (Value = 0) Then NULL Else Value End Value
    from (
    select age, 
        ISNULL([a],0)[a], ISNULL([b],0)[b], ISNULL([c],0)[c], ISNULL([d],0)[d], ISNULL([e],0)[e]
    From pivot_task) As pvttask
    UnPivot ([Value] for [Data] In ([a], [b], [c], [d], [e])) a
    

    【讨论】:

    • 这不是与@VigneshKumar 发布的答案基本相同吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-07-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-09-25
    • 2015-02-13
    相关资源
    最近更新 更多