格式化SQL输出(Pivot)

时间:2013-04-19 19:52:15

标签: sql sql-server-2008 tsql pivot

这是在SQL Server 2008上运行。

无论如何,我有销售数据,我可以编写一个查询来使输出看起来像这样:

id | Name       | Period  | Sales
1  | Customer X | 2013/01 | 50
1  | Customer X | 2013/02 | 45

等。目前,运行此数据后,我正在重新排列后面代码中的数据,以便最终输出如下所示:

id  | Name       | 2013/01 | 2013/02
1   | Customer X |   50    |   40

问题是:

  1. 日期(YYYY / MM)范围是来自用户的输入。
  2. 如果用户选择更多输出(例如,地址以及与该客户相关的大量其他可能字段),则该信息将在每个行中重复。如果你每行做10-15个项目,在5年以上的时间内,对于50000多个用户来说,这会导致内存不足的问题,而且效率也很低。
  3. 我考虑过只提取必要的数据(客户ID - 它们如何连接在一起,期间和销售数字),然后运行单独的查询以获取其他数据。这看起来似乎并不高效,但这是一种可能性。

    另一个,我正在考虑的应该是最好的选择,就是重写我的查询以继续执行我当前的代码所做的事情,并将数据转移到一起,这样客户数据就是永远不会重复,我不会移动很多不必要的数据。

    为了更好地说明我正在使用的内容,让我们假设这些表:

    地址

    id | HouseNum | Street | Unit | City | State
    

    客户

    id | Name | 
    

    销售

    id | Period | Sales
    

    所以我想在客户ID上加入这些表,显示所有地址数据,假设用户输入“2012/01 - 2012/12”,我可以将其转换为2012 / 01,02012 / 02 ... 2012/12在我的代码背后,在执行之前输入查询,所以我可以使用它。

    我希望它看起来像是:

    id | Name | HouseNum | Street   | City | State | 2012/01 | 2012/02 | ... | 2012/12
    1  | X    | 100      | Main St. | ABC  | DEF   |   30    |         | ... |   20
    

    (2012/02年度没有该客户的销售数据 - 如果任何数据为空白我希望它是一个空字符串“”,而不是NULL)

    我意识到我可能不会以最好的方式解释这个,所以请告诉我,我会添加更多信息。谢谢!

    编辑:哦,最后一件事。是否可以添加Min,Max,Avg和&到目前为止的总列数,它总结了所有数据?在后面的代码上做这件事并不是什么大不了的事,但是更多的sql server可以为我做得更好,imo!

    编辑:另外,期间在表格中作为“2013/01”等,但我想将它们重命名为“2013年1月”等,如果它不是太复杂?

1 个答案:

答案 0 :(得分:2)

您可以实现PIVOT函数将数据从行转换为列。您可以使用以下方法获得结果:

select id,
  name,
  HouseNum,
  Street,
  City,
  State,
  isnull([2013/01], 0) [2013/01], 
  isnull([2013/02], 0) [2013/02], 
  isnull([2012/02], 0) [2012/02], 
  isnull([2012/12], 0) [2012/12],
  MinSales,
  MaxSales,
  AvgSales,
  TotalSales
from
(
  select c.id,
    c.name,
    a.HouseNum,
    a.Street,
    a.city,
    a.state,
    s.period,
    s.sales,
    min(s.sales) over(partition by c.id) MinSales,
    max(s.sales) over(partition by c.id) MaxSales,
    avg(s.sales) over(partition by c.id) AvgSales,
    sum(s.sales) over(partition by c.id) TotalSales
  from customer c
  inner join address a
    on c.id = a.id
  inner join sales s
    on c.id = s.id
) src
pivot
(
  sum(sales)
  for period in ([2013/01], [2013/02], [2012/02], [2012/12])
) piv;

请参阅SQL Fiddle with Demo

如果您要将未知数量的period值转换为列,则必须使用动态SQL来获取结果:

DECLARE @cols AS NVARCHAR(MAX),
    @colsNull AS NVARCHAR(MAX),
    @query  AS NVARCHAR(MAX)

select @cols = STUFF((SELECT distinct ',' + QUOTENAME(period) 
                    from Sales
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')

select @colsNull = STUFF((SELECT distinct ', IsNull(' + QUOTENAME(period) + ', 0) as '+ QUOTENAME(period) 
                    from Sales
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')


set @query = 'SELECT id,
                name,
                HouseNum,
                Street,
                City,
                State,' + @colsNull + ' ,
                MinSales,
                MaxSales,
                AvgSales,
                TotalSales
             from 
             (
               select c.id,
                c.name,
                a.HouseNum,
                a.Street,
                a.city,
                a.state,
                s.period,
                s.sales,
                min(s.sales) over(partition by c.id) MinSales,
                max(s.sales) over(partition by c.id) MaxSales,
                avg(s.sales) over(partition by c.id) AvgSales,
                sum(s.sales) over(partition by c.id) TotalSales
              from customer c
              inner join address a
                on c.id = a.id
              inner join sales s
                on c.id = s.id
            ) x
            pivot 
            (
                sum(sales)
                for period in (' + @cols + ')
            ) p '

execute(@query)

SQL Fiddle with Demo。结果如下:

| ID |       NAME | HOUSENUM |    STREET |    CITY |  STATE | 2012/02 | 2012/12 | 2013/01 | 2013/02 | MINSALES | MAXSALES | AVGSALES | TOTALSALES |
---------------------------------------------------------------------------------------------------------------------------------------------------
|  1 | Customer X |      100 | Maint St. |     ABC |    DEF |       0 |      20 |      50 |      45 |       20 |       50 |       38 |        115 |
|  2 | Customer Y |      108 |   Lost Rd | Unknown | Island |      10 |       0 |       0 |       0 |       10 |       10 |       10 |         10 |