user3583912
user3583912

Reputation: 1322

how can I get data from every month from past one year in t-sql and each month should be in different column

I want data from past 1 year, for example today is 02-05-2014, i need data from May 2013, June 2013..till April 2014 as separate columns. If anybody can help with this code in t-sql that will be very helpful for me. Thanks

ok here i want the data. below is my column

created date
------------
02-05-2013
16-05-2013
05-06-2013
22-07-2013
01-08-2013
09-08-2013
02-09-2013
03-10-2013
19-11-2013
11-12-2013
03-01-2014
29-02-2014
15-03-2014
19-04-2014

I want the result as

May 2013    June 2013    July 2013    August 2013 till   April 2014
--------    ---------    ---------    -----------        ----------
02-05-2013 05-06-2013   22-07-2013   01-08-2013          19-04-2014
16-05-2013                           09-08-2013

and also I want to make Columns dynamically which is very important for my query

Upvotes: 0

Views: 1457

Answers (3)

cyan
cyan

Reputation: 747

OOOPS this already have an answer T_T,
but try this if you have a free time. :)

use a lot of things in this case:

  1. CTE
  2. STUFF
  3. CONVERT
  4. SUBSTRING

declare @xCol nvarchar(max);

--Begin create column
with cte as
(
    select 1 as id,
        aa.month as mm, aa.Year
        --,aa.xmonth as mmm
        , aa.ord
    from
    (
    select 
        xdate
        ,Year(xdate) as Year
        --,month(xdate) as xmonth
        , substring(convert(nvarchar(max),xdate,106),3,len(convert(nvarchar(max),xdate,106))) as month
        ,convert(nvarchar(6), xdate,112) as ord
    from tempData vv
    ) aa
    group by aa.ord, aa.month,aa.Year
    --order by aa.Year
)
select 

    distinct 
    --c.id,
    @xCol = stuff(
        (
            select ',' + c2.mm
            from cte c2
            where c.id = c2.id
            for xml path ('')
        ),1,0,''
    )
from cte c

;

set @xCol= SUBSTRING(@xCol,2, len(@xCol))
select @xCol = '[' + replace(@xCol,',','],[') + ']'
--select @xCol as '@columns', len(@xCol)

--END CREATE COLUMNS

--CREATE INPUT STRING
Declare @tbl_inputstr table
(
  id int,
  xstr nvarchar(max)
)
;

    with cte as
    (
        select 
            a.xdate, a.month
            ,row_number() over(partition by a.month order by a.xdate) as xrow
        from
        (
        select 
                xdate
                ,Year(xdate) as Year
                ,month(xdate) as xmonth
                ,convert(nvarchar(6),xdate,112) as month2
                , substring(convert(nvarchar(max),xdate,106),3,len(convert(nvarchar(max),xdate,106))) as month
        from tempData 
        ) a
    )
    insert into @tbl_inputstr(id,xstr)
    select  distinct c.xrow as id,
        ' Insert into @tempData (' + substring(stuff(
            (
                select ',[' + cast(c2.month as nvarchar(max)) + ']'
                from cte c2
                where c.xrow = c2.xrow
                for xml path ('')
            ),1,0,''
        ),2,len(stuff(
            (
                select ',[' + cast(c2.month as nvarchar(max)) + ']'
                from cte c2
                where c.xrow = c2.xrow
                for xml path ('')
            ),1,0,''
        ))) + ')'
        +' Values(' + Substring(stuff(
            (
                select ',''' + cast(c2.xdate as nvarchar(max)) + ''''
                from cte c2
                where c.xrow = c2.xrow
                for xml path ('')
            ),1,0,''
        ),2,len(stuff(
            (
                select ',''' + cast(c2.xdate as nvarchar(max)) + ''''
                from cte c2
                where c.xrow = c2.xrow
                for xml path ('')
            ),1,0,''
        ))) + ')'
    from cte c
    order by c.xrow;

select * from @tbl_inputstr

Declare @inputStr nvarchar(max)
select @inputStr = 
    substring(stuff
    (
        (
            select ';' + xstr
            from @tbl_inputstr
            for xml path('')
        ),1,0,''
    ),2, len(stuff
    (
        (
            select ';' + xstr
            from @tbl_inputstr
            for xml path('')
        ),1,0,''
    ))
    ) 


select @inputStr= 'Declare @tempData Table (' +replace(@xCol,']', '] nvarchar(max)') + ');' + @inputStr 
    + '; select ' + @xCol 
    + ' from @tempData'



exec(@inputStr)
--END INPUT STRING

Upvotes: 0

mdisibio
mdisibio

Reputation: 3540

Although everyone was quick to suggest using PIVOT, that really won't work here because PIVOT would need to aggregate each column into one (max, min, whatever) date for that month and year.

I gave this question some attention because it was actually an interesting challenge. The reality is, this is best done by some reporting tool, such as SSRS, because your output is really a timeline report.

Because of the requirement for dynamic columns and such, I really don't see how this can be done without variables and some dynamic sql, so assuming that is acceptable, the following is tested and will output exactly what you described. It essentially creates 12 CTE tables, each containing the dates for a month and year, (counting backwards from whatever month and year you run the sql). It then creates the report simply by using a FULL OUTER JOIN of all the tables. However, if you full joined just the 12 tables, each column would have its values randomly over several rows with many nulls in betweeen. To line the dates in each column up at the top, it was necessary to add a base table with sequential numbers that each mo/yr table can join on. The sequential numbers are generated up to the max number of dates for any given mo/yr. (Note: with the base number table, a LEFT OUTER JOIN would have sufficed as well...)

This assumes the table name is dbo.MyTable and the date column is CreatedDate:

DECLARE @cteSql nvarchar(MAX) = '';
DECLARE @tblSql nvarchar(MAX) = '';
DECLARE @frmSql nvarchar(MAX) = '';
DECLARE @colNm  varchar(10);
DECLARE @tblNm  varchar(3);
DECLARE @i int = 0;

/* today's date */
DECLARE @td date = GETDATE();
/* max number of dates per yr/mo */
DECLARE @maxItems int = (SELECT MAX(CNT) FROM (SELECT COUNT(*) AS CNT FROM dbo.MyTable GROUP BY YEAR(CreatedDate), MONTH(CreatedDate)) T)

/* a table of sequential numbers up to the max per yr/mo; this is so the full outer join is laid out neatly */
SET @cteSql = 'WITH T(id) AS( SELECT id = 1 UNION ALL SELECT id + 1 FROM T WHERE id + 1 <= ' + CAST(@maxItems AS varchar(16)) + ')';

/* count down from current date to past 12 months */
WHILE @i > -12
BEGIN
  /* a simple name for each CTE: T0, T1, T2 etc */
  SET @tblNm = 'T' + CAST((@i*-1) AS varchar(2));
  /* rpt column names; [Jan 2014], [Feb 2014] etc */
  SET @colNm = '[' + RIGHT(CONVERT(varchar(11), DATEADD(m, @i, @td), 106),8) + ']';

  /* each CTE contains a sequential id and the dates belonging to that month and yr */
  SET @cteSql += ', ' + @tblNm + '(id, ' + @colNm + ')'
               + ' AS (SELECT ROW_NUMBER() OVER(ORDER BY CreatedDate) AS id, CreatedDate FROM dbo.MyTable WHERE YEAR(CreatedDate) = ' + CAST(YEAR(DATEADD(m, @i, @td)) AS varchar(4))
               + ' AND MONTH(CreatedDate) = ' + CAST(MONTH(DATEADD(m, @i, @td)) AS varchar(2)) + ')';

  /* this will eventually be the SELECT statement for the report...just the month columns, not the id */
  SET @tblSql = ', ' + @colNm + @tblSql;
  /* concatenate all the columns using FULL OUTER JOIN with the first table of simple sequential numbers as the driver */
  SET @frmSql += ' FULL OUTER JOIN ' + @tblNm + ' ON T.id = ' + @tblNm + '.id ';

  SET @i -= 1;
END

/* put all the sql together */
SET @tblSql = @cteSql + ' SELECT' + STUFF(@tblSql, 1, 1, '') + ' FROM T ' + @frmSql

/*  view the generated sql */
-- SELECT @tblSql AS X

/*  this should generate the report you described above, showing the last 12 months from whatever date you run it */
EXECUTE (@tblSql)

Output:

Jun 2013   Jul 2013   Aug 2013   Sep 2013   Oct 2013   Nov 2013   Dec 2013   Jan 2014   Feb 2014   Mar 2014   Apr 2014   May 2014
---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ----------
2013-06-05 2013-07-22 2013-08-01 2013-09-02 2013-10-03 2013-11-19 2013-12-11 2014-01-03 2014-02-28 2014-03-15 2014-04-19 NULL
2013-06-07 NULL       2013-08-09 NULL       NULL       NULL       NULL       NULL       NULL       NULL       NULL       NULL
NULL       NULL       2013-08-10 NULL       NULL       NULL       NULL       NULL       NULL       NULL       NULL       NULL

As it turns out, the sql generated is conceptually similar to what @Hogan suggested, although I did not realize it at first. It really just adds the dynamic naming plus the segregation by yr/mo and not just month.

Upvotes: 1

Hogan
Hogan

Reputation: 70528

Here is a way to do it without a dynamic pivot. I only did it for 2013, you can see what is needed to add more columns:

(working fiddle: http://sqlfiddle.com/#!6/d9797/1)

with nums as
(
  select [create date],
         MONTH([create date]) as M,
         ROW_NUMBER() OVER (ORDER BY [create date] ASC) as RN
  from table1       
  where  MONTH([create date]) =1
  union all      
  select [create date],
         MONTH([create date]) as M,
         ROW_NUMBER() OVER (ORDER BY [create date] ASC) as RN
  from table1       
  where  MONTH([create date]) =2
  union all      
  select [create date],
         MONTH([create date]) as M,
         ROW_NUMBER() OVER (ORDER BY [create date] ASC) as RN
  from table1       
  where  MONTH([create date]) =3
  union all      
  select [create date],
         MONTH([create date]) as M,
         ROW_NUMBER() OVER (ORDER BY [create date] ASC) as RN
  from table1       
  where  MONTH([create date]) =4
  union all      
  select [create date],
         MONTH([create date]) as M,
         ROW_NUMBER() OVER (ORDER BY [create date] ASC) as RN
  from table1       
  where  MONTH([create date]) =5
  union all      
  select [create date],
         MONTH([create date]) as M,
         ROW_NUMBER() OVER (ORDER BY [create date] ASC) as RN
  from table1       
  where  MONTH([create date]) =6
  union all      
  select [create date],
         MONTH([create date]) as M,
         ROW_NUMBER() OVER (ORDER BY [create date] ASC) as RN
  from table1       
  where  MONTH([create date]) =7
  union all      
  select [create date],
         MONTH([create date]) as M,
         ROW_NUMBER() OVER (ORDER BY [create date] ASC) as RN
  from table1       
  where  MONTH([create date]) =8
  union all      
  select [create date],
         MONTH([create date]) as M,
         ROW_NUMBER() OVER (ORDER BY [create date] ASC) as RN
  from table1       
  where  MONTH([create date]) =9
  union all      
    select [create date],
         MONTH([create date]) as M,
         ROW_NUMBER() OVER (ORDER BY [create date] ASC) as RN
  from table1       
  where  MONTH([create date]) =10
  union all      
  select [create date],
         MONTH([create date]) as M,
         ROW_NUMBER() OVER (ORDER BY [create date] ASC) as RN
  from table1       
  where  MONTH([create date]) =11
  union all      
  select [create date],
         MONTH([create date]) as M,
         ROW_NUMBER() OVER (ORDER BY [create date] ASC) as RN
  from table1       
  where  MONTH([create date]) =12
),maxrn as
(
  select MAX(RN) as maxnum from nums
), rowNumbers as
(
   select 1 as RN
   union all
   select RN+1 AS RN
   from rowNumbers
   where RN < (select maxnum from maxrn)
)
SELECT 
   nJan.[create date] as [Jan 2013],
   nFeb.[create date] as [Feb 2013],
   nMar.[create date] as [Mar 2013],
   nApr.[create date] as [Apr 2013],
   nMay.[create date] as [May 2013],
   nJun.[create date] as [Jun 2013],
   nJul.[create date] as [Jul 2013],
   nAug.[create date] as [Aug 2013],
   nSep.[create date] as [Sep 2013],
   nOct.[create date] as [Oct 2013],
   nNov.[create date] as [Nov 2013],
   nDec.[create date] as [Dec 2013]
FROM rowNumbers n
LEFT JOIN nums nJan ON n.RN = nJan.RN and nJan.M = 1
LEFT JOIN nums nFeb ON n.RN = nFeb.RN and nFeb.M = 2
LEFT JOIN nums nMar ON n.RN = nMar.RN and nMar.M = 3
LEFT JOIN nums nApr ON n.RN = nApr.RN and nApr.M = 4
LEFT JOIN nums nMay ON n.RN = nMay.RN and nMay.M = 5
LEFT JOIN nums nJun ON n.RN = nJun.RN and nJun.M = 6
LEFT JOIN nums nJul ON n.RN = nJul.RN and nJul.M = 7
LEFT JOIN nums nAug ON n.RN = nAug.RN and nAug.M = 8
LEFT JOIN nums nSep ON n.RN = nSep.RN and nSep.M = 9
LEFT JOIN nums nOct ON n.RN = nOct.RN and nOct.M = 10
LEFT JOIN nums nNov ON n.RN = nNov.RN and nNov.M = 11
LEFT JOIN nums nDec ON n.RN = nDec.RN and nDec.M = 12
ORDER BY n.RN ASC

Upvotes: 0

Related Questions