最近在写一个销售报表的SQL,对此类型的二维SQL 进行一个总结。
表结构 Test
ProductName SaleMonth SaleNumber
A m1 1
B m1 1
A m2 2
B m2 3
要求出查询结果:
ProductName M1 M2
A 1 2
B 1 3
方法一:
创建临时表,通过游标将数据添加到临时表中。
但是此方法只适合列固定,如果列不确定,编写起来比较麻烦。所以此方法忽略。
方法二:
思路:通过Case依次创建列。
如下: (Case SaleMonth When 'M1' then SaleNumber ELSE 0 END) [M1],
(Case SaleMonth When 'M2' then SaleNumber ELSE 0 END) [M2]
带来的问题是:如何获取不确定的列名。
declare @sql varchar(8000)
SET @sql='SELECT ProductName'
-- 这步关键需要初始化,否则会无效。因为varchar类型相加,如果有个内容为空,则相加之后的内容也是为空
SELECT @sql = @sql + ',(Case SaleMonth WHEN ''' + SaleMonth
+ ''' THEN SaleNumber ELSE 0 END) ['
+ SaleMonth + ']' FROM (SELECT DISTINCT SaleMonth FROM Test) A
SET @sql = @sql + ' FROM Test'
PRINT @sql
EXEC(@sql)
运行结果如下:
p m1 m2 m3 m4
A 1 0 0 0
A 0 2 0 0
A 0 0 3 0
A 0 0 0 4
B 1 0 0 0
B 0 2 0 0
B 0 0 3 0
B 0 0 0 4
这并不是所预期的。经分析,在进行Case处理的时候加上MAX可避免。
如下:
declare @sql varchar(8000)
SET @sql='SELECT ProductName'
-- 这步关键需要初始化,否则会无效。因为varchar类型相加,如果有个内容为空,则相加之后的内容也是为空
SELECT @sql = @sql + ',MAX(Case SaleMonth WHEN ''' + SaleMonth
+ ''' THEN SaleNumber ELSE 0 END) [' + SaleMonth + ']' FROM
(SELECT DISTINCT SaleMonth FROM Test) A
SET @sql = @sql + ' FROM Test GROUP BY ProductName'
PRINT @sql
EXEC(@sql)
运行结果如下:
P M1 M2 M3 M4
A 1 2 3 4
B 1 2 3 4
效果达到预期。
但是带来了另外一个问题,如果SaleNumber中有负数的时候则会出现数据错误(此列数据会被0代替)。此时需要根据实际情况进行相应的更改。
方法三:
SQL2005中有 PIVOT函数可实现行转列
PIVOT具体介绍可以参考:http://technet.microsoft.com/zh-cn/library/ms177410.aspx
SQL如下:
SELECT * FROM (SELECT ProductName, SaleNumber, SaleMonth FROM Test) A
PIVOT(MAX(SaleNumber) FOR SaleMonth IN(m1,m2,m3,m4)) B
带来的问题是:如何动态生成M1,M2。。。
declare @sql varchar(8000)
SELECT @sql = ISNULL(@sql + ',', '') + SaleMonth FROM Test GROUP BY SaleMonth
SET @sql = 'SELECT * FROM (SELECT ProductName, SaleNumber, SaleMonth FROM Test) A
PIVOT(MAX(SaleNumber) FOR SaleMonth IN(' + @sql + ')) B'
PRINT @sql
EXEC(@sql)
总结:如果你的表中有主键或者某一列具备唯一值时,在进行创建源的时候记得将此列排除。