根据SQL中的数据将行转换为列

时间:2015-01-23 15:52:13

标签: sql sql-server pivot

下图是我的表enter image description here

以下excel是输出设计。enter image description here

我的表包含每月12列和年份列。一个项目,它可以在多年内提供,每个月都有数据。

year- itemcode- jan- feb

2014-  pqr-      12-  11

2015-  pqr-      4-    8

我需要生成以下输出。对于可用多年的项目 输出需要按以下方式列出。

ItemCode- Jan14- Feb14- Mar14-... Dec14- Jan15- Feb15-... Dec15

pqr-        12-   11-                     4-     8-

我怎样才能实现这一目标。 我在google搜索后尝试了不同的方法。但是我无法得到适当的输入来解决这个问题。 目前我正在尝试在SO中找到一些解决方案。有人可以提供一些意见非常有帮助。提前谢谢。

2 个答案:

答案 0 :(得分:1)

假设您有下表:

select 'AA' as ItemCode,2014 as year, 23 as Jan, 55 as Feb, 55 as Mar,565 as Apr,656 as May,
343 as Jun,54 as Jul,23 as Aug,66 as Sep,645 as Oct,32 as Nov,66 as Dec
into dbo.test ;
insert into dbo.test select 'AA',2015,554,456,3,54,756,98,2,765,24,876,34,66
union select 'BB',2014,45,56,3,54,756,98,2,765,24,876,34,66
union select 'BB',2015,45,56,3,54,756,98,2,765,24,876,34,66;

使用动态sql,执行

declare @sql nvarchar(1000);
declare @sql2 nvarchar(1000);
declare @year int;
declare @first_year int;
declare c cursor for select distinct year from dbo.test;
open c;
FETCH NEXT FROM c into @year
if @@FETCH_STATUS = 0
begin
  select @sql='select test'+convert(varchar,@year)+'.ItemCode';
  select @sql = @sql+',test'+CONVERT(varchar,@year)+'.Jan as Jan'+CONVERT(varchar,@year)+',test'
    +CONVERT(varchar,@year)+'.Feb as Feb'+CONVERT(varchar,@year)+',test'
    +CONVERT(varchar,@year)+'.Mar as Mar'+CONVERT(varchar,@year);
  select @sql2='test test'+CONVERT(varchar,@year);
  select @first_year=@year;
end;
FETCH NEXT FROM c into @year
WHILE @@FETCH_STATUS = 0
begin
  select @sql = @sql+',test'+CONVERT(varchar,@year)+'.Jan as Jan'+CONVERT(varchar,@year)+',test'
    +CONVERT(varchar,@year)+'.Feb as Feb'+CONVERT(varchar,@year)+',test'
    +CONVERT(varchar,@year)+'.Mar as Mar'+CONVERT(varchar,@year);
  select @sql2=@sql2+' inner join test test'+CONVERT(varchar,@year)+' on test'+CONVERT(varchar,@year)+'.ItemCode=test'+CONVERT(varchar,@first_year)+'.ItemCode and test'+CONVERT(varchar,@year)+'.year='+CONVERT(varchar,@year);
  FETCH NEXT FROM c into @year
end;
close c;
deallocate c;
select @sql=@sql+' FROM '+@sql2 + ' AND test'+convert(varchar,@first_year)+'.year='+CONVERT(varchar,@year);
print @sql
EXECUTE sp_executesql @sql;

或者,使用标准SQL,类似这样的

select test2014.ItemCode,test2014.Jan as Jan2014,test2014.Feb as Feb2014,test2015.Jan as Jan2015,test2015.Feb as Feb2015
from test test2014 inner join test test2015 on test2014.ItemCode=test2015.ItemCode
where test2014.year=2014 and test2015.year=2015;

答案 1 :(得分:1)

您需要使用动态SQL ...

基本上假设一个名为#tbl的表(带有一点样本数据 - 我只做了3个月但延伸到12个!)

CREATE TABLE #tbl ([ItemCode] NVARCHAR(20), [Year] INT, Jan INT, Feb INT, Mar INT)
INSERT #tbl ( ItemCode, Year, Jan, Feb, Mar )
VALUES  ( 'pqr', 2014, 12, 11, 7  ), ( 'pqr', 2015, 4, 8, 0  ), 
  ( 'xyz', 2015, 7, 1, 0  ), ( 'abc', 2013, 63, 23, 12  ), ( 'abc', 2015, 63, 23, 12  )

我们希望生成一个类似于

的查询
SELECT tbase.ItemCode
    , ISNULL(t13.Jan,0) AS 'Jan-13', ISNULL(t13.Feb,0) AS 'Feb-13', ISNULL(t13.Mar,0) AS 'Mar-13'
    , ISNULL(t14.Jan,0) AS 'Jan-14', ISNULL(t14.Feb,0) AS 'Feb-14', ISNULL(t14.Mar,0) AS 'Mar-14'
    , ISNULL(t15.Jan,0) AS 'Jan-15', ISNULL(t15.Feb,0) AS 'Feb-15', ISNULL(t15.Mar,0) AS 'Mar-15'  
FROM
    (SELECT DISTINCT(ItemCode) AS ItemCode FROM #tbl) AS tbase 
    LEFT JOIN (SELECT * FROM #tbl AS t13 WHERE YEAR = 2013) AS t13 ON t13.ItemCode = tbase.ItemCode
    LEFT JOIN (SELECT * FROM #tbl AS t14 WHERE YEAR = 2014) AS t14 ON t14.ItemCode = tbase.ItemCode
    LEFT JOIN (SELECT * FROM #tbl AS t15 WHERE YEAR = 2015) AS t15 ON t15.ItemCode = tbase.ItemCode

结果如:

ItemCode    Jan-13  Feb-13  Mar-13  Jan-14  Feb-14  Mar-14  Jan-15  Feb-15  Mar-15
abc         63      23      12      0       0       0       63      23      12
pqr         0       0       0       12      11      7       4       8       0
xyz         0       0       0       0       0       0       7       1       0

正如您从查询中看到的那样,要构建的两件事是行, ISNULL(t13.Jan,0)...LEFT JOIN (SELECT ...

我们可以通过声明2个NVARCHAR(MAX)变量(一个用于select,一个用于from)并在while循环中构建它们,同时迭代可用年份来完成此操作。

即... ...

DECLARE @select NVARCHAR(MAX);
DECLARE @from NVARCHAR(MAX);
DECLARE @years TABLE(yr INT);
DECLARE @year INT;
DECLARE @yearName NVARCHAR(2)
INSERT @years
SELECT DISTINCT [Year] FROM #tbl

SELECT @year = MIN(yr) FROM @years
SELECT @yearName = RIGHT(CAST(@year AS NVARCHAR(4)),2)

SELECT @select = 'SELECT tbase.ItemCode'
SELECT @from = 'FROM (SELECT DISTINCT(ItemCode) AS ItemCode FROM #tbl) AS tbase '

WHILE EXISTS (SELECT NULL FROM @years WHERE yr = @year)
BEGIN
    SELECT @yearName = RIGHT(CAST(@year AS NVARCHAR(4)),2)
    SELECT @select = @select + CHAR(13) + CHAR(10) 
        + ', ISNULL(t' + @yearName + '.Jan,0) AS [Jan-' + @yearName + '],' 
        + ' ISNULL(t' + @yearName + '.Feb,0) AS [Feb-' + @yearName + '],'-- +9 more
        + ' ISNULL(t' + @yearName + '.Mar,0) AS [Mar-' + @yearName + '] '  
    SELECT @from = @from + CHAR(13) + CHAR(10) 
        + 'LEFT JOIN (SELECT * FROM #tbl AS t' + @yearName 
        + ' WHERE [Year] = ' + CAST(@year AS NVARCHAR(4)) + ') AS t' + @yearName 
        + ' ON t' + @yearName + '.ItemCode = tbase.ItemCode '

    SELECT @year = @year + 1
END

DECLARE @sql NVARCHAR(MAX)
SELECT @sql = @select + CHAR(13) + CHAR(10) + @from
EXEC (@sql)

您需要做的就是将其延长至整整12个月,然后您就完成了!

注意 - 我认为每年至少有一个条目。如果您的范围中间有一年没有任何条目,您需要对WHILE循环进行微小修改 - 即WHILE @year <= (SELECT MAX(Year) FROM @years)