0

Hi I have a query that gets the total count of incidents per month and year I wanted a result that shows all distinct years in columns Could you please advise how to make this query dynamic?

Expected Result:
Month      2013 2014 2015
January    8    0   12
February   9    6   10
March      12   1   9
April      10   13  27
May        9    22  15
June       27   4   20
July       15   12  22
August     20   2   2
September  22   5   10
October    10   8   12
November    0   7   0
December    0   15  0

Query

select DATENAME(MONTH,DateOpened) as Month,
   sum(case when year(DateOpened)  = '2015' then 1 else 0 end) as [2015],
   sum(case when year(DateOpened)  = '2014' then 1 else 0 end) as [2014]

from Incidents
group by DATENAME(MONTH,DateOpened), MONTH(DateOpened)
order by MONTH(DateOpened)

Thanks for your help!

2
  • What is the problem with your query? I mean, for three years, just add another column. That is much, much easier than dynamic SQL which is needed for a variable number of columns. Commented Oct 11, 2015 at 12:22
  • @GordonLinoff I need to get the distinct value in years of field:"DateOpened" and put it to columns, I don't want to write the list of years, I need to do it dynamically Commented Oct 11, 2015 at 12:50

2 Answers 2

2

You can use the PIVOT table operator instead, something like this:

SELECT *
FROM
(
  SELECT
    DATENAME(MONTH,DateOpened) as Month,
    DATENAME(Year,DateOpened) AS Year,
    DateOpened
  FROM Incidents
) AS t
PIVOT
(
  COUNT(DateOpened)
  FOR Year IN([2013], [2014], [2015])
) AS p;

If you don't need to write the list of years and do it dynamically for any year, you have to use dynamic SQL to run the query dynamically, like this:

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

SELECT @cols = STUFF((SELECT distinct ',' +
                        QUOTENAME(DATENAME(Year,DateOpened))
                      from Incidents AS t
                      FOR XML PATH(''), TYPE
                     ).value('.', 'NVARCHAR(MAX)') 
                        , 1, 1, '');

SELECT @query = 'SELECT * , '+ @cols + '
FROM
(
  select 
    DATENAME(MONTH,DateOpened) as Month,
    DATENAME(Year,DateOpened) AS Year,
    DateOpened
  FROM Incidents
) AS t
PIVOT
(
  COUNT(DateOpened)
  FOR Year IN(' + @cols + ')' +
                  ') p';

  execute(@query);

This will give you something like this:

|     Month | 2014 | 2015 | 2014 | 2015 |
|-----------|------|------|------|------|
|     April |    0 |    3 |    0 |    3 |
|    August |    1 |    1 |    1 |    1 |
|  December |    1 |    0 |    1 |    0 |
|  February |    0 |    1 |    0 |    1 |
|      July |    1 |    1 |    1 |    1 |
| September |    1 |    0 |    1 |    0 |

Note that: Both the queries won't list any month that has no dates on the original table. If you want to list any month that is not listed on the table with counts 0, you have to modify the anchor query so that it lists all the months even if not listed:

  SELECT
    m.Name as Month,
    i.Year,
    i.DateOpened 
  FROM
  (
   VALUES ('Janurary'), ('February'), ('March'),
          ('April'),    ('May'),      ('June'),
          ('July'),     ('August'),   ('September'),
          ('October'),  ('November'), ('December')
   ) AS m(Name) 
  LEFT JOIN
  (
    SELECT 
      DATENAME(MONTH,DateOpened) as Month,
      DATENAME(Year,DateOpened) AS Year,
      DateOpened 
    FROM Incidents 
  ) AS i ON i.Month = m.Name

and replace it in the dynamic query.

This will give you the missing months with zeros results:

|     Month | 2014 | 2015 | 2014 | 2015 |
|-----------|------|------|------|------|
|  Janurary |    0 |    0 |    0 |    0 | <<
|  February |    0 |    1 |    0 |    1 |
|     March |    0 |    0 |    0 |    0 | <<
|     April |    0 |    3 |    0 |    3 |
|       May |    0 |    0 |    0 |    0 | <<
|      June |    0 |    0 |    0 |    0 | <<
|      July |    1 |    1 |    1 |    1 |
|    August |    1 |    1 |    1 |    1 |
| September |    1 |    0 |    1 |    0 |
|   October |    0 |    0 |    0 |    0 | <<
|  November |    0 |    0 |    0 |    0 | <<
|  December |    1 |    0 |    1 |    0 |
Sign up to request clarification or add additional context in comments.

Comments

0

A simple create table something like this..

  CREATE TABLE #tmpIncidents
  (
    IncidentName NVARCHAR(50)
    , DateOpened DATETIME
  )

  INSERT INTO #tmpIncidents (IncidentName,DateOpened) VALUES 
  ('Test1',GETDATE()),
  ('Test1',DATEADD(YEAR,-1,GETDATE())),
  ('Test1',DATEADD(YEAR,-2,GETDATE())),
  ('Test1',DATEADD(YEAR,2,GETDATE())),
  ('Test1',DATEADD(YEAR,1,GETDATE())),
  ('Test1',DATEADD(YEAR,3,GETDATE()))

Then a build up query for multiple years

  DECLARE @columnVar NVARCHAR(4000)

  SELECT @columnVar =
      (SELECT DISTINCT
            '[' + CONVERT(NVARCHAR(150),DATEPART(YEAR,DateOpened)) + '],' AS [text()]
      FROM #tmpIncidents
      FOR XML PATH('')
      )
  SET @columnVar = (SELECT LEFT(@columnVar,LEN(@columnVar)-1))
  --SELECT @columnVar --so you can see how it looks..

Then execute your query.

  EXEC ('
  SELECT
    pv.*
  FROM 
  (
    SELECT DATENAME(MONTH,DateOpened) AS [DateName], DATEPART(YEAR,DateOpened) AS [YEAR], IncidentName FROM #tmpIncidents
  ) src
  PIVOT
  (
    COUNT(IncidentName)
    FOR [YEAR] IN (' + @columnVar + ')
  ) pv;
  ')

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.