sql-server - 透视查询以返回多个重复组?

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

我正在尝试获取具有多个重复组的结果集(将插入到表中)。这是一个脚本,它显示了我开始使用的数据的一个非常简化的版本:

CREATE TABLE #Aggregate(
    StoreKey int ,
    NumberOfDaysBack int ,
    ThisYearGrossTransactions int ,
    ThisYearGrossPrice money ,
    LastYearGrossTransactions int ,
    LastYearGrossPrice money 
) 
GO
INSERT #Aggregate VALUES (10134, 7, 198, 71324.3600, 248, 95889.6089)
INSERT #Aggregate VALUES (10131, 7, 9, 1299.8300, 3, 662.5700)
INSERT #Aggregate VALUES (10132, 7, 57, 11029.5300, 56, 6848.3800)
INSERT #Aggregate VALUES (10130, 7, 6, 429.3100, 15, 1606.1100)
INSERT #Aggregate VALUES (10134, 28, 815, 339315.9265, 822, 342834.2365)
INSERT #Aggregate VALUES (10131, 28, 29, 5725.4900, 8, 1938.4100)
INSERT #Aggregate VALUES (10132, 28, 262, 42892.5476, 269, 37229.2600)
INSERT #Aggregate VALUES (10130, 28, 62, 6427.7072, 93, 13428.0000)

然后我想为每组 NumberOfDaysBack 显示单独的数据集,如下所示:

StoreKey    ThisYearLast7GrossTransactions ThisYearLast7GrossPrice LastYearLast7GrossTransactions LastYearLast7GrossPrice ThisYearLast28GrossTransactions ThisYearLast28GrossPrice LastYearLast28GrossTransactions LastYearLast28GrossPrice
----------- ------------------------------ ----------------------- ------------------------------ ----------------------- ------------------------------- ------------------------ ------------------------------- ------------------------
10130       6                              429.31                  15                             1606.11                 62                              6427.7072                93                              13428.00
10131       9                              1299.83                 3                              662.57                  29                              5725.49                  8                               1938.41
10132       57                             11029.53                56                             6848.38                 262                             42892.5476               269                             37229.26
10134       198                            71324.36                248                            95889.6089              815                             339315.9265              822                             342834.2365

我能够通过此查询获得上述结果集。

-- (using this Common Table expression as a shortcut, there's actually a dimention table
;with Store as (select distinct StoreKey from #Aggregate)
Select
    Store.StoreKey
    ,ThisYearLast7GrossTransactions = DaysBack7.ThisYearGrossTransactions
    ,ThisYearLast7GrossPrice = DaysBack7.ThisYearGrossPrice
    ,LastYearLast7GrossTransactions = DaysBack7.LastYearGrossTransactions
    ,LastYearLast7GrossPrice = DaysBack7.LastYearGrossPrice
    ,ThisYearLast28GrossTransactions = DaysBack28.ThisYearGrossTransactions
    ,ThisYearLast28GrossPrice = DaysBack28.ThisYearGrossPrice
    ,LastYearLast28GrossTransactions = DaysBack28.LastYearGrossTransactions
    ,LastYearLast28GrossPrice = DaysBack28.LastYearGrossPrice    
from Store 
    join #Aggregate DaysBack7
        on Store .StoreKey = DaysBack7.StoreKey
        and DaysBack7 .NumberOfDaysBack = 7
    join #Aggregate DaysBack28
        on Store .StoreKey = DaysBack28.StoreKey
        and DaysBack28 .NumberOfDaysBack = 28
order by Store.StoreKey

但是,由于我的实际数据集要复杂得多,有更多的 NumberOfDaysBack 和更多可能会更改的指标,我希望能够使用数据透视语句来完成此操作,而无需明确命名每个字段。

这可能吗?感谢您的任何想法!

最佳答案

您可以使用 UNPIVOTPIVOT 获得您想要的结果:

select *
from
(
  select storekey, 
    value, col +'Last'+ cast(numberofdaysback as varchar(20)) + 'Days' new_col
  from
  (
    select storekey,
      numberofdaysback,
      cast(ThisYearGrossTransactions as decimal(20,5)) ThisYearGrossTransactions,
      cast(ThisYearGrossPrice as decimal(20,5)) ThisYearGrossPrice,
      cast(LastYearGrossTransactions as decimal(20,5)) LastYearGrossTransactions,
      cast(LastYearGrossPrice as decimal(20,5)) LastYearGrossPrice    
    from aggregate
  ) un
  unpivot
  (
    value
    for col in (ThisYearGrossTransactions, ThisYearGrossPrice,
                LastYearGrossTransactions, LastYearGrossPrice)
  ) unpiv
) src
pivot
(
  sum(value)
  for new_col in ([ThisYearGrossTransactionsLast7Days], [ThisYearGrossPriceLast7Days],
                  [LastYearGrossTransactionsLast7Days], [LastYearGrossPriceLast7Days],
                  [ThisYearGrossTransactionsLast28Days], [ThisYearGrossPriceLast28Days],
                  [LastYearGrossTransactionsLast28Days], [LastYearGrossPriceLast28Days])
) piv;

参见 SQL Fiddle with Demo

UNPIVOT 获取 ThisYearGrossTransactionsThisYearGrossPrice 中的列值, LastYearGrossTransactionsLastYearGrossPrice 并将它们转换为包含多行的单列。

select storekey, 
  value, col +'Last'+ cast(numberofdaysback as varchar(20)) + 'Days' new_col
from
(
  select storekey,
    numberofdaysback,
    cast(ThisYearGrossTransactions as decimal(20,5)) ThisYearGrossTransactions,
    cast(ThisYearGrossPrice as decimal(20,5)) ThisYearGrossPrice,
    cast(LastYearGrossTransactions as decimal(20,5)) LastYearGrossTransactions,
    cast(LastYearGrossPrice as decimal(20,5)) LastYearGrossPrice    
  from aggregate
) un
unpivot
(
  value
  for col in (ThisYearGrossTransactions, ThisYearGrossPrice,
              LastYearGrossTransactions, LastYearGrossPrice)
) unpiv

参见 SQL Fiddle with Demo

UNPIVOT 的要求是所有数据类型必须相同,因此您需要将 castconvert 应用于任何值。然后为了 PIVOT 数据,我通过将 numberofdaysback 添加到每条记录来创建新的列名称。这些是在查询的 PIVOT 部分中使用的值。

最后的结果是:

| STOREKEY | THISYEARGROSSTRANSACTIONSLAST7DAYS | THISYEARGROSSPRICELAST7DAYS | LASTYEARGROSSTRANSACTIONSLAST7DAYS | LASTYEARGROSSPRICELAST7DAYS | THISYEARGROSSTRANSACTIONSLAST28DAYS | THISYEARGROSSPRICELAST28DAYS | LASTYEARGROSSTRANSACTIONSLAST28DAYS | LASTYEARGROSSPRICELAST28DAYS |
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|    10130 |                                  6 |                      429.31 |                                 15 |                     1606.11 |                                  62 |                    6427.7072 |                                  93 |                        13428 |
|    10131 |                                  9 |                     1299.83 |                                  3 |                      662.57 |                                  29 |                      5725.49 |                                   8 |                      1938.41 |
|    10132 |                                 57 |                    11029.53 |                                 56 |                     6848.38 |                                 262 |                   42892.5476 |                                 269 |                     37229.26 |
|    10134 |                                198 |                    71324.36 |                                248 |                  95889.6089 |                                 815 |                  339315.9265 |                                 822 |                  342834.2365 |

如果您有已知数量的 NumberOfDaysBack 值,上面的静态版本效果很好,但如果您有未知数量的值,那么您可以使用这个的动态版本:

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

select @colsUnpivot = stuff((select ','+quotename(C.name)
         from sys.columns as C
         where C.object_id = object_id('Aggregate') and
               C.name not in ('StoreKey', 'NumberOfDaysBack')
         for xml path('')), 1, 1, '')

select @colsPivot = STUFF((SELECT  ',' 
                      + quotename(c.name +'Last'
                         + cast(a.NumberOfDaysBack as varchar(10)) +'Days')
                    from Aggregate a
                    cross apply sys.columns  C
                   where C.object_id = object_id('Aggregate') and
                         C.name not in ('StoreKey', 'NumberOfDaysBack')
                   group by c.name, a.NumberOfDaysBack
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')

set @query 
  = 'select *
      from
      (
        select storekey, 
            value, col +''Last''+ cast(numberofdaysback as varchar(20)) + ''Days'' new_col
        from 
        (
          select storekey,
            numberofdaysback,
            cast(ThisYearGrossTransactions as decimal(20,5)) ThisYearGrossTransactions,
            cast(ThisYearGrossPrice as decimal(20,5)) ThisYearGrossPrice,
            cast(LastYearGrossTransactions as decimal(20,5)) LastYearGrossTransactions,
            cast(LastYearGrossPrice as decimal(20,5)) LastYearGrossPrice    
          from aggregate
        ) x
        unpivot
        (
          value
          for col in ('+ @colsunpivot +')
        ) u
      ) x1
      pivot
      (
        sum(value)
        for new_col in ('+ @colspivot +')
      ) p'

exec(@query)

参见 SQL Fiddle with Demo

两个查询的结果相同。

关于sql-server - 透视查询以返回多个重复组?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13454132/

相关文章:

c# - 非常冗长的存储过程状态

sql-server - 是否可以使用 SSIS 包 dtsConfig 文件的相对路径?

sql - 在日期时间间隔内展开表格

sql-server - SQL Server 代理作业 - 将结果导出到制表符分隔文件?

sql-server-2008 - 获取返回错误的存储过程的模式

sql-server - SQL Server 作业输出到 Azure Blob 存储

php - 如何通过 PHP 将 Unicode 数据更新到 SQL Server?

sql-server - 动态列名称和值到 Where 子句

SQL Server 只返回单行或 null

sql - 遍历sql结果集并删除[n]个重复项