扩展当前查询,计算列

时间:2014-09-11 09:57:31

标签: sql sql-server-2008-r2

我的表格如下所示:

Name       date      result
 A      2012-01-01     1
 A      2012-02-01     2
 B      2013-01-01     1
         ...

完整示例:http://sqlfiddle.com/#!3/0226b/1

目前我有一个工作查询,按人和年计算行数:http://sqlfiddle.com/#!3/0226b/3

这很完美,但我想要的是2014年的一些额外信息。我需要计算每个结果我有多少行。 像这样的东西:

NAME       1   2   3   2014    2013    2012    TOTAL
Person B   4   0   2     6       2       2      10
Person A   2   1   1     4       3       4      11
Person C   1   1   1     3       1       0       4

更好的是我给结果列一个好名字(1 =丢失,2 =抽奖,3 =赢):

NAME       lost   draw   won   2014    2013    2012    TOTAL
Person B    4       0     2     6       2       2      10
Person A    2       1     1     4       3       4      11
Person C    1       1     1     3       1       0       4

我尝试添加一些额外的代码,例如:

select @colsResult 
  = STUFF((SELECT  ',' + QUOTENAME(result) 
           from list
           group by result
           order by result
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')

我的结果是:

,[1]
,[2]
,[3]

但如果我运行整个代码,我会收到一个错误,即invallid列名......

1 个答案:

答案 0 :(得分:7)

由于您有两列现在想要PIVOT,因此您首先必须取消这些列的转换,然后将这些值转换为新列。

从SQL Server 2005开始,您可以使用CROSS APPLY来取消列。基本语法类似于:

select 
  name,
  new_col,
  total  
from
(
  select name, 
    dt = year(date),
    result,
    total = count(*) over(partition by name)
  from list
) d
cross apply
(
  select 'dt', dt union all
  select 'result', result
) c (old_col_name, new_col)

SQL Fiddle with Demo。此查询会为您提供一个名称列表,其中包含"新列"然后是每个名称的总条目数。

|     NAME | NEW_COL | TOTAL |
|----------|---------|-------|
| Person A |    2012 |    11 |
| Person A |       1 |    11 |
| Person A |    2012 |    11 |
| Person A |       2 |    11 |

您会看到日期和结果现在都存储在" new_col"中。这些值现在将用作新列名。如果您的列数有限,那么您只需对查询进行硬编码:

select name, lost = [1], 
  draw=[2], won = [3], 
  [2014], [2013], [2012], Total
from
(
  select 
    name,
    new_col,
    total  
  from
  (
    select name, 
      dt = year(date),
      result,
      total = count(*) over(partition by name)
    from list
  ) d
  cross apply
  (
    select 'dt', dt union all
    select 'result', result
  ) c (old_col_name, new_col)
) src
pivot
(
  count(new_col)
  for new_col in([1], [2], [3], [2014], [2013], [2012])
) piv
order by [2014];

请参阅SQL Fiddle with Demo

既然你的岁月是动态的,那么你需要使用动态的sql。但是看起来你有3个结果并且可能有多年 - 所以我使用静态/动态sql的组合来使这更容易:

DECLARE @cols AS NVARCHAR(MAX),
    @query  AS NVARCHAR(MAX),
    @orderby nvarchar(max)

select @cols 
  = STUFF((SELECT  ',' + QUOTENAME(year(date)) 
           from list
           group by year(date)
           order by year(date) desc
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')

select @orderby = 'ORDER BY ['+cast(year(getdate()) as varchar(4)) + '] desc'

set @query = 'SELECT name, lost = [1], 
                draw=[2], won = [3],' + @cols + ', Total
            from 
            (
              select 
                name,
                new_col,
                total  
              from
              (
                select name, 
                  dt = year(date),
                  result,
                  total = count(*) over(partition by name)
                from list
              ) d
              cross apply
              (
                select ''dt'', dt union all
                select ''result'', result
              ) c (old_col_name, new_col)
            ) x
            pivot 
            (
                count(new_col)
                for new_col in ([1], [2], [3],' + @cols + ')
            ) p '+ @orderby

exec sp_executesql @query;

SQL Fiddle with Demo。这给出了一个结果:

|     NAME | LOST | DRAW | WON | 2014 | 2013 | 2012 | TOTAL |
|----------|------|------|-----|------|------|------|-------|
| Person B |    7 |    1 |   2 |    6 |    2 |    2 |    10 |
| Person A |    5 |    3 |   3 |    4 |    3 |    4 |    11 |
| Person C |    2 |    1 |   1 |    3 |    1 |    0 |     4 |

如果您只想过滤当前年份的结果列,那么您可以通过多种方式执行此过滤,但最简单的方法是在unpivot中包含过滤器。硬编码版本将是:

select name, lost = [1], 
  draw=[2], won = [3], 
  [2014], [2013], [2012], Total
from
(
  select 
    name,
    new_col,
    total  
  from
  (
    select name, 
      dt = year(date),
      result,
      total = count(*) over(partition by name)
    from list
  ) d
  cross apply
  (
    select 'dt', dt union all
    select 'result', case when dt = 2014 then result end  
  ) c (old_col_name, new_col)
) src
pivot
(
  count(new_col)
  for new_col in([1], [2], [3], [2014], [2013], [2012])
) piv
order by [2014] desc;

SQL Fiddle with Demo。那么动态的sql版本将是:

DECLARE @cols AS NVARCHAR(MAX),
    @query  AS NVARCHAR(MAX),
    @orderby nvarchar(max),
    @currentYear varchar(4)

select @currentYear = cast(year(getdate()) as varchar(4))

select @cols 
  = STUFF((SELECT  ',' + QUOTENAME(year(date)) 
           from list
           group by year(date)
           order by year(date) desc
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')

select @orderby = 'ORDER BY ['+ @currentYear + '] desc'

set @query = 'SELECT name, lost = [1], 
                draw=[2], won = [3],' + @cols + ', Total
            from 
            (
              select 
                name,
                new_col,
                total  
              from
              (
                select name, 
                  dt = year(date),
                  result,
                  total = count(*) over(partition by name)
                from list
              ) d
              cross apply
              (
                select ''dt'', dt union all
                select ''result'', case when dt = '+@currentYear+' then result end
              ) c (old_col_name, new_col)
            ) x
            pivot 
            (
                count(new_col)
                for new_col in ([1], [2], [3],' + @cols + ')
            ) p '+ @orderby

exec sp_executesql @query;

SQL Fiddle with Demo。这个版本将给出一个结果:

|     NAME | LOST | DRAW | WON | 2014 | 2013 | 2012 | TOTAL |
|----------|------|------|-----|------|------|------|-------|
| Person B |    4 |    0 |   2 |    6 |    2 |    2 |    10 |
| Person A |    2 |    1 |   1 |    4 |    3 |    4 |    11 |
| Person C |    1 |    1 |   1 |    3 |    1 |    0 |     4 |