SQL sum by year report, looking for an elegant sol

2020-02-26 13:24发布

I have a table with 3 columns: ItemCode, Quantity and DocDate. I would like to create the following report in a more "elegant" way:

SELECT T0.ItemCode, 
       (SELECT SUM(QUANTITY) FROM MyTable T1 WHERE YEAR(T0.DocDate) = 2011 AND T0.ItemCode = T1.ItemCode) AS '2011',
       (SELECT SUM(QUANTITY) FROM MyTable T1 WHERE YEAR(T0.DocDate) = 2012 AND T0.ItemCode = T1.ItemCode) AS '2012'
FROM MyTable T0
GROUP BY T0.ItemCode, YEAR(T0.DocDate)

I'm pretty sure there's a better, more efficient way to write this but I can't come up with the right syntax. Any ideas?

2条回答
forever°为你锁心
2楼-- · 2020-02-26 13:40

This type of data transformation is known as a PIVOT. There are several ways that you can perform this operation. You can use the PIVOT function or you can use an aggregate function with a CASE statement:

Static Pivot Version: This is where you hard-code all of the values into the query

select ItemCode, [2011], [2012]
from
(
  SELECT ItemCode,
    QUANTITY,
    YEAR(DocDate) Year
  FROM MyTable 
) src
pivot
(
  sum(quantity)
  for year in ([2011], [2012])
) piv

See SQL Fiddle with Demo

Case with Aggregate:

SELECT ItemCode, 
  SUM(CASE WHEN YEAR(DocDate) = 2011 THEN QUANTITY ELSE 0 END) AS '2011',
  SUM(CASE WHEN YEAR(DocDate) = 2012 THEN QUANTITY ELSE 0 END) AS '2012'
FROM    MyTable 
GROUP BY ItemCode;

See SQL Fiddle with Demo

Dynamic Pivot: The previous two versions will work great is you have a known number of year values to transform, but it you have an unknown number then you can use dynamic sql:

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

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

set @query = 'SELECT itemcode, ' + @cols + ' from 
             (
                select itemcode, quantity, year(docdate) year
                from mytable
            ) x
            pivot 
            (
                sum(quantity)
                for year in (' + @cols + ')
            ) p '

execute(@query)

See SQL Fiddle with Demo

All three versions will produce the same result:

| ITEMCODE | 2011 | 2012 |
--------------------------
|        1 |  200 |   45 |
|        2 |   89 |    0 |
|        3 |    0 |    7 |
查看更多
小情绪 Triste *
3楼-- · 2020-02-26 13:52

You can try this:

SELECT  T0.ItemCode, 
        SUM(CASE WHEN YEAR(T0.DocDate) = 2011 THEN QUANTITY ELSE 0 END) AS '2011',
        SUM(CASE WHEN YEAR(T0.DocDate) = 2012 THEN QUANTITY ELSE 0 END) AS '2012'
FROM    MyTable T0
GROUP BY 
        T0.ItemCode
查看更多
登录 后发表回答