sql - 根据月数将列返回为行

标签 sql sql-server sql-server-2008 tsql pivot

这似乎是一件很容易完成的事情,但我不确定我是否正确地考虑它以获得所需的结果。我正在使用枢轴,但我认为我需要其他东西与之搭配。

我有一个发票表,其中包含每个客户的每月发票。客户每年最多有 12 张发票,每月 1 张。

+----------+-------+-------+--------------+--------------+--------------+
| ClientID | Month | Year  | ColumnValue1 | ColumnValue2 | ColumnValue3 |
+----------+-------+-------+--------------+--------------+--------------+
|        1 |     1 |  2012 |           20 |           30 |           50 |
|        1 |     2 |  2012 |           25 |           35 |           40 |
|        2 |     1 |  2012 |           28 |           38 |           48 |
+----------+-------+-------+--------------+--------------+--------------+

现在,我想根据每个客户创建一个如下所示的列表。每个月都会有一个专栏。所以客户端 1 看起来像:

+--------------+----+----+---+---+---+---+---+---+---+----+----+----+-------+
|  ColumnName  | 1  | 2  | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | Total |
+--------------+----+----+---+---+---+---+---+---+---+----+----+----+-------+
| ColumnValue1 | 20 | 25 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |  0 |  0 |  0 |   45  |
| ColumnValue2 | 30 | 35 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |  0 |  0 |  0 |   65  |
| ColumnValue3 | 50 | 40 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |  0 |  0 |  0 |   90  |
+--------------+----+----+---+---+---+---+---+---+---+----+----+----+-------+

最佳答案

这可以使用 SQL Server 中的 UNPIVOTPIVOT 函数来完成。如果您的列数已知,则可以使用静态版本:

select clientid,
  col, year,
  isnull([1], 0) [1], 
  isnull([2], 0) [2], 
  isnull([3], 0) [3], 
  isnull([4], 0) [4], 
  isnull([5], 0) [5], 
  isnull([6], 0) [6], 
  isnull([7], 0) [7], 
  isnull([8], 0) [8], 
  isnull([9], 0) [9], 
  isnull([10], 0) [10], 
  isnull([11], 0) [11], 
  isnull([12], 0) [12],
  (isnull([1], 0) + isnull([2], 0) + isnull([3], 0) 
   + isnull([4], 0) + isnull([5], 0) + isnull([6], 0) 
   + isnull([7], 0) + isnull([8], 0) + isnull([9], 0) 
   + isnull([10], 0) + isnull([11], 0) + isnull([12], 0) ) Total
from
(
  select clientid, col, month, year, value
  from yourtable
  unpivot
  (
    value for col in (ColumnValue1, ColumnValue2, ColumnValue3)
  ) u
) x
pivot
(
  sum(value)
  for month in ([1], [2], [3], [4], [5], [6], [7], 
                [8], [9], [10], [11], [12])
) p

参见SQL Fiddle with Demo

但是使用动态 sql 来执行此操作可能会容易得多,这样需要编写的代码就会更少,并且这将根据您的数据样本中的内容调整月数:

DECLARE @colsUnpivot AS NVARCHAR(MAX),
    @query  AS NVARCHAR(MAX),
    @colsPivot as  NVARCHAR(MAX),
    @colsTotal as  NVARCHAR(MAX),
    @colsNull as  NVARCHAR(MAX)

select @colsUnpivot = stuff((select ','+ quotename(C.name)
         from sys.columns as C
         where C.object_id = object_id('yourtable') and
               C.name like 'ColumnValue%'
         for xml path('')), 1, 1, '')

select @colsPivot = STUFF((SELECT distinct ', '  + quotename(Month)
                    from yourtable t
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')

select @colsNull = STUFF((SELECT distinct ', IsNull(' 
                           + quotename(Month) + ', 0) as '+quotename(Month)
                    from yourtable t
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')

select @colsTotal = STUFF((SELECT distinct '+ IsNull(' 
                           + quotename(Month) + ', 0)'
                    from yourtable t
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')


set @query 
  = 'select clientid,
          year,
          '+@colsNull+', '+@colsTotal+' as Total
      from
      (
        select clientid, col, month, year, value
        from yourtable
        unpivot
        (
          value for col in ('+@colsUnpivot+')
        ) u
      ) x
      pivot
      (
        sum(value)
        for month in('+ @colspivot +')
      ) p'

exec(@query)

参见SQL Fiddle with Demo

两者都会产生相同的结果,不同之处在于第二个将根据表中的数据进行调整:

| CLIENTID | YEAR |  1 |  2 | TOTAL |
-------------------------------------
|        1 | 2012 | 20 | 25 |    45 |
|        1 | 2012 | 30 | 35 |    65 |
|        1 | 2012 | 50 | 40 |    90 |
|        2 | 2012 | 28 |  0 |    28 |
|        2 | 2012 | 38 |  0 |    38 |
|        2 | 2012 | 48 |  0 |    48 |

关于sql - 根据月数将列返回为行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13022718/

相关文章:

sql - 如何聚合sql(HANA)中不同行的值?

sql - 删除 SQL 查询中的重复行

sql - 使用自连接将行转为列

MySQL:查询应返回计数为 0 的行

php - 通过连接从 2 个以上 mysql 表中获取数据。

sql-server - 使用Map Reduce将大型CSV文件中的数据加载到SQL Server

java - 无法从 Spring 应用程序连接到 mssql 服务器

sql - 在 SQL Server 表中插入多个值

sql-server - 从源表中删除,如果不存在则插入到目标表中

sql - SQL Management Studio 中的虚线/点线关系线代表什么 - 数据库图