格式化SQL输出(透视)

格式化SQL输出(透视),sql,sql-server-2008,tsql,pivot,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 等等。目前,在运行此数据后,我正在重新排列代码中的数据

这是在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
等等。目前,在运行此数据后,我正在重新排列代码中的数据,以便最终输出如下所示:

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
这些问题是:

  • 日期(YYYY/MM)范围是用户输入的
  • 如果用户选择了更多的输出(比如,地址,以及与该客户相关的大量其他可能的字段),则该信息将在每一行中重复。当您在5年以上的时间内为50000多名用户每行处理10-15个项目时,这会导致内存不足的问题,而且效率低下
  • 我考虑过只提取必要的数据(客户id——它们是如何结合在一起的、期间和销售数据),然后在事实发生后运行一个单独的查询来获取额外的数据。虽然这看起来不太有效,但这是可能的

    另一个,我认为这应该是最好的选择,就是重写我的查询,继续执行我当前的代码,并将数据集中在一起,这样客户数据就不会重复,我也不会移动很多不必要的数据

    为了更好地说明我的工作,让我们假设以下表格:

    地址

    id | HouseNum | Street | Unit | City | State
    
    顾客

    id | Name | 
    
    销售

    所以我想在客户id上加入这些表,显示所有地址数据,假设用户输入“2012/01--2012/12”,我可以将其转换为2012/01、2012/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年没有该客户的销售数据——如果任何数据为空,我希望它是一个空字符串“”,而不是空字符串)

    我意识到我可能没有尽可能最好地解释这一点,所以请告诉我,我会补充更多信息。谢谢大家!

    编辑:哦,最后一件事。是否可以在末尾添加一个Min、Max、Avg和Total列,以汇总所有数据透视?在代码背后做这件事并不是什么大不了的事,但在我看来,sql server能为我做的越多越好


    编辑:还有一点,周期在表中是“2013/01”等,但如果不太复杂,我想将它们重命名为“2013年1月”等?

    您可以实现透视功能,将数据从行转换为列。您可以使用以下方法获得结果:

    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;
    
    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)
    
    | 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 |
    

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

    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;
    
    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)
    
    | 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 |
    
    看。结果如下:

    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;
    
    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)
    
    | 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 |
    

    就您上次编辑将数据转换为2013年1月的日期而言,period列的数据类型是什么?它是一个varchar,但我决定保持原样,而不是添加月份名称。谢谢。结果比我想象的要简单得多。我几乎可以结束我原来的查询,现在快多了!谢谢