通过删除NULL值重组表

时间:2018-04-30 09:17:52

标签: sql sql-server database data-structures

我在SQL中有一个如下所示的表:

Customer   Product    1999     2000     2001    2002      2003
Smith      51         NULL     NULL       15      14      NULL
Jones      14           11        7     NULL    NULL      NULL
Jackson    13         NULL     NULL     NULL       3         9

每年列下的数字是金额,以美元计算。每个客户连续两年的金额,其余的年份为零。我想重新构建这个表,以便它只有两列Amount-Year1和Amount-Year2,而不是多年的广泛列表。因此,它选择两个非零年份,并按照正确的顺序将它们放在这些列中。这将大大减少我的桌子的大小。

到目前为止,我已经能够重新构建它,以便有一个数量列和一年列,但我会为每个客户获得多行,不幸的是我无法拥有(由于下游分析)。谁能想到一种方法来获得两个Amount-Year专栏?

我希望决赛桌看起来像这样:

Customer    Product    Amount_Y1    Amount_Y2
Smith       51         15           14
Jones       14         11            7
Jackson     13          3            9

我不介意丢失有关特定年份的信息,因为我可以从其他来源获得该信息。实际表格包含1999年至2018年间所有年份的数据,未来还会有更多年。

谢谢

4 个答案:

答案 0 :(得分:3)

谢天谢地UNPIVOT无论如何都会删除NULL,因此我们可以使用UNPIVOT / ROWNUMBER()PIVOT执行此操作:

declare @t table (Customer varchar(15),Product int,[1999] int,
                  [2000] int,[2001] int,[2002] int,[2003] int)
insert into @T(CUstomer,Product,[1999],[2000],[2001],[2002],[2003]) values
('Smith'  ,51,NULL,NULL,  15,  14,NULL),
('Jones'  ,14,  11,   7,NULL,NULL,NULL),
('Jackson',13,NULL,NULL,NULL,   3,   9)

;With Numbered as (
    select
        Customer,Product,Value,
        ROW_NUMBER() OVER (PARTITION BY Customer,Product
                           ORDER BY Year) rn
    from
        @t t
            unpivot
        (Value for Year in ([1999],[2000],[2001],[2002],[2003])) u
)
select
    *
from
    Numbered n
        pivot
    (SUM(Value) for rn in ([1],[2])) w

结果:

Customer        Product     1           2
--------------- ----------- ----------- -----------
Jackson         13          3           9
Jones           14          11          7
Smith           51          15          14

答案 1 :(得分:1)

尝试使用COALESCE,如下所示:对于一个字段,从开始到结束,第二个字段以相反的方式。

SELECT Customer,Product, COALESCE([1999],[2000],[2001],[2002],[2003]) as Y1, 
       COALESCE([2003],[2002],[2001],[2000],[1999])  as Y2
FROM  #TEMPDATA

答案 2 :(得分:1)

我会使用cross apply执行此操作:

select t.customer, t.product, v.Amount_Y1, v.Amount_Y2
from t cross apply
     (select max(case when which = 1 then val end) as Amount_Y1,
             max(case when which = 2 then val end) as Amount_Y2
      from (select val, yr, row_number() over (order by yr) as which
            from (values (t.[1999], 1999), (t.[2000], 2000), (t.[2001], 2001),
                         (t.[2002], 2002), (t.[2003], 2003)
                 ) v(val, yr)
            where val is not null
           ) v

答案 3 :(得分:1)

使用能为您完成工作的COALESCE。查询是动态的,因此如果明天年份列被更改,即删除或添加,则无需更改任何内容。

示例查询:(假设表格为table1,列名称与年份相同)。

    DECLARE @columnsdesc nvarchar(max), @columnsasc nvarchar(max)
SET @columnsdesc = ''
SELECT @columnsdesc = (select + '[' +  ltrim(c.Name)  +   ']' + ','
FROM     sys.columns c 
         JOIN sys.objects o ON o.object_id = c.object_id 
WHERE    o.type = 'U' and o.Name = 'table1' and c.Name not in ('Customer', 'Product')
ORDER BY c.Name desc for xml path ( '' ))

SET @columnsasc = ''
SELECT @columnsasc = (select + '[' +  ltrim(c.Name)  +   ']' + ','
FROM     sys.columns c 
         JOIN sys.objects o ON o.object_id = c.object_id 
WHERE    o.type = 'U' and o.Name = 'table1' and c.Name not in ('Customer', 'Product')
ORDER BY c.Name asc for xml path ( '' ))
   SELECT @columnsasc = LEFT( @columnsasc,LEN(@columnsasc)-1)
   SELECT @columnsdesc = LEFT( @columnsdesc,LEN(@columnsdesc)-1)
   DECLARE @sql nvarchar(max)

SET @sql = 'SELECT Customer, Product, COALESCE('+ @columnsasc  +') as Amount_Y1,
COALESCE(' + @columnsdesc +' ) as Amount_Y2
FROM Table1'

EXEC(@sql)

如果您正在处理temporary table,则代码会略有变化: 在此测试:http://rextester.com/MRVR48808

DECLARE @columnsdesc nvarchar(max), @columnsasc nvarchar(max)
SET @columnsdesc = ''
SELECT @columnsdesc = (select + '[' +  ltrim(c.Name)  +   ']' + ','
FROM     tempdb.sys.columns c               --Changes here
         JOIN tempdb.sys.objects o ON o.object_id = c.object_id    --Changes here
WHERE    o.type = 'U' and o.Name like '#table1%' and c.Name not in ('Customer', 'Product')   --Changes here
ORDER BY c.Name desc for xml path ( '' ))

SET @columnsasc = ''
SELECT @columnsasc = (select + '[' +  ltrim(c.Name)  +   ']' + ','
FROM     tempdb.sys.columns c                   --Changes here
         JOIN tempdb.sys.objects o ON o.object_id = c.object_id     --Changes here 
WHERE    o.type = 'U' and o.Name like '#table1%' and c.Name not in ('Customer', 'Product')  --Changes here
ORDER BY c.Name asc for xml path ( '' ))
   SELECT @columnsasc = LEFT( @columnsasc,LEN(@columnsasc)-1)
   SELECT @columnsdesc = LEFT( @columnsdesc,LEN(@columnsdesc)-1)
   DECLARE @sql nvarchar(max)

SET @sql = 'SELECT Customer, Product, COALESCE('+ @columnsasc  +') as Amount_Y1,
COALESCE(' + @columnsdesc +' ) as Amount_Y2
FROM #Table1'   --Changes here

EXEC(@sql)