我在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年间所有年份的数据,未来还会有更多年。
谢谢
答案 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)