Arrange a single column data in multiple columns in SQL Server - sql

I have the following table(raw data). I would like to see the data in following format(Formatted Data).
I tried the following query. It produces a rather weird result. Could someone guide me here how to achieve this.
declare #monthnames varchar(100)
select distinct [Month], MonthNumber
into #Months
from Table
order by MonthNumber
SELECT #monthnames = Stuff((SELECT ', ' + [Month]
FROM #Months
order by MonthNumber
FOR XML PATH('')
), 1, 2, '')
declare #query varchar(500)
set #query = 'select CUR,' + #monthnames +
' from ' +
' Pivot ( min(DATE) for [Month] in (' + #monthnames + ') ) as Pivottable ORDER BY CUR'
EXEC (#query)

You don't really need to use Dynamic SQL as you have a fixed number of pivoting columns.
What you are missing is the rn, so that each date appear under different row
SELECT CUR,
[1] as Jan,
[2] as Feb,
[3] as Mar,
[4] as Apr,
[5] as May
FROM (
SELECT CUR, MonthNumber, DATE,
rn = ROW_NUMBER() OVER (PARTITION BY CUR, MonthNumber ORDER BY DATE)
FROM #Table
) AS d
PIVOT
(
MIN(DATE)
FOR MonthNumber IN ([1], [2], [3], [4], [5])
) AS p

declare #t table
(
CUR varchar(10),
[Month] varchar(20),
MonthNumber tinyint,
[Date] date
);
insert into #t(Cur, [Month], MonthNumber, [Date])
values
('AED', 'January', 1, '20200110'),
('AED', 'February', 2, '20200212'),('AED', 'February', 2, '20200215'),
('AED', 'March', 3, '20200305'),('AED', 'March', 3, '20200305'),('AED', 'March', 3, '20200305'),
('AED', 'April', 4, '20200402'),('AED', 'April', 4, '20200412'),('AED', 'April', 4, '20200415'),
('AED', 'June', 6, '20200619'),
('AED', 'August', 8, '20200801'),('AED', 'August', 8, '20200805'),('AED', 'August', 8, '20200810'), ('AED', 'August', 8, '20200824'),
----
('ARS', 'January', 1, '20200118'),
('ARS', 'April', 4, '20200416'),
('ARS', 'May', 5, '20200512'), ('ARS', 'May', 5, '20200513'), ('ARS', 'May', 5, '20200514'),
('ARS', 'September', 9, '20200902'),('ARS', 'September', 9, '20200922');
select CUR, [January],[February],[March],[April],[May],[June],[July],[August],[September],[October],[November],[December]
from
(
select CUR, [Date], dense_rank() over(partition by CUR, [Month] /*or MonthNumber*/ order by [Date]) as ranking,
[Month]
from #t
) as t
pivot
(
min([Date]) for [Month] in ([January],[February],[March],[April],[May],[June],[July],[August],[September],[October],[November],[December])
) as pvt
order by CUR;

Related

How to use non-aggregate columns used in the SELECT clause without using it in GROUP BY clause?

I am running this query and I get correct result
SELECT
CONCAT("year", ' - ', TRIM('20' FROM "year") + 1) as 'Years',
SUM("svalue") as 'Value',
'Sale' as 'Type',
"year" as 'Year',
"code" as 'FACode',
FROM "FCJOIN"
WHERE "code" IN
(
SELECT "fccode"
FROM "fcdetails"
)
AND "month" between '04' and '12'
AND "year" IN ( '2016' , '2017' , '2018' , '2019' )
GROUP BY "code", "year"
what i need is , if I add a new column formonth it will gives an error Improper usage of GROUP BY clause ? Please ensure that all non-aggregate columns used in the SELECT clause are also used in GROUP BY clause.
that's my modified query is like this
SELECT
CONCAT("year", ' - ', TRIM('20' FROM "year") + 1) as 'Years',
SUM("svalue") as 'Value',
'Sale' as 'Type',
"year" as 'Year',
"code" as 'FACode',
**"formonth" as 'Period'**
FROM "FCJOIN"
WHERE "code" IN
(
SELECT "fccode"
FROM "fcdetails"
)
AND "month" between '04' and '12'
AND "year" IN ( '2016' , '2017' , '2018' , '2019' )
GROUP BY "code", "year"
is there any way to make this correct group by or or there any way to rewrite ? I dont want the newly added column in group by clause. i am trying this in zoho reports. Any help ?
if you want to show the months of each year for which the code was applicable, then you could aggregate their descriptions/strings with string_agg(). For multiple levels of aggregation i.e sum(svalue) group by (code, year) or group by (year, month description/period) GROUPING SETS could be used.
declare #fcdetails table (fccode int)
insert into #fcdetails(fccode) values(1), (2), (3);
declare #fcjoin table
(
[year] char(4),
[month] tinyint,
svalue int,
code int
) ;
insert into #fcjoin([year], [month], svalue, code)
values
('2016', 5, 10, 1), ('2016', 6, 10, 1), ('2016', 7, 10, 1),
('2017', 5, 4, 2), ('2017', 6, 4, 2),
('2018', 7, 10, 3);
declare #othertable table
(
[themonth] tinyint,
period varchar(20)
);
insert into #othertable([themonth], period)
select distinct [month], {fn MONTHNAME(datefromparts('2020', [month], 1))}
from #fcjoin;
SELECT
CONCAT("year", ' - ', TRIM('20' FROM "year") + 1),
SUM("svalue") as 'Value',
'Sale' as 'Type',
"year" as 'Year',
"code" as 'FACode',
string_agg(o.period, ',') as forperiod
FROM #FCJOIN as f
join #othertable as o on f.month = o.themonth --careful with joins & aggregations
WHERE "code" IN
(
SELECT "fccode"
FROM #fcdetails
)
AND "month" between '04' and '12'
AND "year" IN ( '2016' , '2017' , '2018' , '2019' )
GROUP BY "code", "year";
SELECT
CONCAT("year", ' - ', TRIM('20' FROM "year") + 1),
SUM("svalue") as 'Value',
'Sale' as 'Type',
"year" as 'Year',
"code" as 'FACode',
(select string_agg(o.period, ',')
from #othertable as o
where o.themonth in
(select b.month from #FCJOIN as b where b.year = f.year and b.code = f.code)
) as forperiod
FROM #FCJOIN as f
WHERE "code" IN
(
SELECT "fccode"
FROM #fcdetails
)
AND "month" between '04' and '12'
AND "year" IN ( '2016' , '2017' , '2018' , '2019' )
GROUP BY "code", "year";
SELECT
CONCAT("year", ' - ', TRIM('20' FROM "year") + 1),
SUM("svalue") as 'Value',
'Sale' as 'Type',
"year" as 'Year',
"code" as 'FACode',
o.period as forperiod
FROM #FCJOIN as f
join #othertable as o on f.month = o.themonth --careful with joins & aggregations
WHERE "code" IN
(
SELECT "fccode"
FROM #fcdetails
)
AND "month" between '04' and '12'
AND "year" IN ( '2016' , '2017' , '2018' , '2019' )
GROUP BY GROUPING SETS(("code", "year"),("year", o.period) /*,(code, o.period)*/);

Calculate percentage of results in week ranges starting from date

I have a table of data that shows attendance with the following columns.
PupilID, AttendanceDate, AttendanceSession, IsInAttendance(1 or 0)
I need to be able to define a start and an end date (term dates) to run a query on that will show each pupils attendance percentage in each week.
I am able to find each pupils total percentage % with the following code:
SELECT
PupilID,
((SUM(CASE WHEN IsInAttendance = 1 THEN 1 ELSE 0 END) * 100) / COUNT(IsInAttendance)) AS TotalAttPct
FROM #CombinedAttClSe
GROUP BY PupilID
I have the weeks of the year with the following code:
DECLARE #StartDate DATETIME = '2011-09-01 00:00:00.000';
DECLARE #EndDate DATETIME = '2011-12-31 00:00:00.000';
;WITH GetDates AS
(
SELECT
DATEADD(DAY, 0, #startdate) AS TheDate,
DATEPART(WK, DATEADD(DAY, 1, #startdate)) WkNo,
DATEPART(YEAR, DATEADD(DAY, 1, #startdate)) DateYear
UNION ALL
SELECT
DATEADD(DAY, 1, TheDate),
DATEPART(WK, DATEADD(DAY, 1, TheDate)),
DATEPART(YEAR, DATEADD(DAY, 1, TheDate))
FROM GetDates
WHERE TheDate < #enddate
)
SELECT
WkNo,
MIN(TheDate) AS SoW,
MAX(TheDate) AS EoW
FROM GETDATES
GROUP BY WKNO
OPTION (MAXRECURSION 0);
I'm not sure where to start to output this data as the following:
PupilID | Week1 | Week2 | Week3 | Week 4| etc.
With each 'Week' heading containing a number to show the percentage of attendance.
EDIT:
From user suggestions I have tried to use PIVOT tables and dynamic SQL to solve this but am running into some issues. Below is the code I have so far.
CREATE TABLE #yt
(
[PupilID] int,
[WeekNo] int,
[IsInAttendance] int
);
INSERT INTO #yt
(
[PupilID],
[WeekNo],
[IsInAttendance]
)
VALUES
(102, 1, 1),
(102, 1, 1),
(102, 1, 1),
(102, 1, 1),
(102, 1, 0),
(102, 1, 1),
(102, 1, 0),
(102, 1, 1),
(102, 2, 1),
(102, 2, 1),
(102, 2, 1),
(102, 2, 1),
(102, 2, 1),
(102, 2, 1),
(102, 2, 0),
(102, 2, 1),
(101, 1, 1),
(101, 1, 1),
(101, 1, 1),
(101, 1, 1),
(101, 1, 1),
(101, 1, 1),
(101, 1, 1),
(101, 2, 1),
(101, 2, 1),
(101, 2, 1),
(101, 2, 1),
(101, 2, 0)
DECLARE #WkCols AS NVARCHAR(MAX),
#WkQuery AS NVARCHAR(MAX)
select #WkCols = STUFF((SELECT ',' + QUOTENAME(WeekNo)
FROM #yt
GROUP BY WeekNo
ORDER BY WeekNo
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #WkQuery = 'SELECT PupilID,' + #WkCols + ' FROM
(
SELECT PupilID, WeekNo, IsInAttendance
FROM #yt
) x
PIVOT
(
SUM(CASE WHEN IsInAttendance = 1 THEN 1 ELSE 0 END) * 100 / COUNT(IsInAttendance)
FOR WeekNo IN (' + #WkCols + ')
) p '
execute(#WkQuery);
At the moment I am getting the error 'Incorrect syntax near the keyword 'CASE'.'
Any pointers or help would be appreciated.
You can't apply complex aggregates on a PIVOT aggregate function unfortunately. So you will have to make do with a simple SUM or MAX. Try calculating the attendance percentage before pivoting:
SELECT
T.PupilID,
T.WeekNo,
AttendancePercentage = SUM(T.IsInAttendance) * 100.0 / COUNT(1)
FROM
#yt AS T
GROUP BY
T.PupilID,
T.WeekNo
And the pivot:
DECLARE #WkCols AS NVARCHAR(MAX),
#WkQuery AS NVARCHAR(MAX)
select #WkCols = STUFF((SELECT ',' + QUOTENAME(WeekNo)
FROM #yt
GROUP BY WeekNo
ORDER BY WeekNo
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #WkQuery = 'SELECT PupilID,' + #WkCols + ' FROM
(
SELECT
T.PupilID,
T.WeekNo,
AttendancePercentage = SUM(T.IsInAttendance) * 100.0 / COUNT(1)
FROM
#yt AS T
GROUP BY
T.PupilID,
T.WeekNo
) x
PIVOT
(
MAX(AttendancePercentage)
FOR WeekNo IN (' + #WkCols + ')
) p '
execute(#WkQuery);

Calculating closing (sums of two views, including the past)

I am trying to get the remaining number of working units for each month, of a sum between a bought number of working unit, and a consumed number of working unit.
I tried two possibilities, but both have flaws :
In the first test, I created a "Months" table that contains every month and every year, in order to show all months in the final matrix I wish to create with these data. With this one, I get the closing whenever there is a consumed working unit, but when there is not, the column is "empty", because it does not get the last closing.
USE OTRS_Revised
SELECT [Customer], CASE WHEN [Year] < 2016 THEN 1 ELSE [Year] END AS [Year], CASE WHEN [Year] < 2016 THEN 0 ELSE [Month] END AS [Month], [Closing] AS Total, SUM([Closing])
OVER (PARTITION BY [Customer] ORDER BY [Year], [Month] ROWS UNBOUNDED PRECEDING) AS Closing
FROM [dbo].[WU_Closing_View]
WHERE [Customer] IN ('CustomerList')
GROUP BY [Customer], [Year], [Month], [Closing]
UNION ALL
SELECT '' AS Customer, CASE WHEN [Year] < 2016 THEN 1 ELSE [Year] END AS [Year], CASE WHEN [Year] < 2016 THEN 0 ELSE [Month] END AS [Month], '' AS Total, '' AS Sum_bought
FROM [dbo].Months
WHERE [Year] <= 2016
GROUP BY Year, Month
ORDER BY Customer, Year, Month
I also tried to do it "month by month", with the below query. It works for one month, but I can't find any way to use this to get the results for each month of the year 2016.
SELECT
(SELECT SUM(Closing) AS Expr1
FROM OTRS_Revised.dbo.WU_Bought_View
WHERE (Customer LIKE 'SomeCustomer') AND (DATEADD(Year, Year - 1900, DATEADD(Month, Month - 1, DATEADD(day, 0, 0))) <= DATEADD(Year, 2016 - 1900, DATEADD(Month, 5 - 1, DATEADD(day, 0, 0))))
GROUP BY Customer)
+
(SELECT SUM(Closing) AS Expr1
FROM OTRS_Revised.dbo.WU_Consumed_View
WHERE (Customer LIKE 'SomeCustomer') AND (DATEADD(Year, Year - 1900, DATEADD(Month, Month - 1, DATEADD(day, 0, 0))) <= DATEADD(Year, 2016 - 1900, DATEADD(Month, 5 - 1, DATEADD(day, 0, 0))))
GROUP BY Customer) AS Expr1,
[Month]
FROM OTRS_Revised.dbo.Months
GROUP BY [Month]
SELECT b.*
FROM
( SELECT CASE WHEN [Year] < 2016 THEN 1 ELSE [Year] END AS [Year], CASE WHEN [Year] < 2016 THEN 0 ELSE [Month] END AS [Month]
FROM [dbo].Months
WHERE [Year] <= 2016
GROUP BY Year, Month
ORDER BY Customer, Year, Month ) AS a
LEFT OUTER JOIN
(SELECT [Customer], CASE WHEN [Year] < 2016 THEN 1 ELSE [Year] END AS [Year], CASE WHEN [Year] < 2016 THEN 0 ELSE [Month] END AS [Month], [Closing] AS Total, SUM([Closing])
OVER (PARTITION BY [Customer] ORDER BY [Year], [Month] ROWS UNBOUNDED PRECEDING) AS Closing
FROM [dbo].[WU_Closing_View]
WHERE [Customer] IN ('CustomerList')
GROUP BY [Customer], [Year], [Month], [Closing]) AS b
ON a.Month = b.Month )
In your approach when you do union the rows that don't have matching months is getting removed. Since you want the months that does not have closing match as well, you need to use the left outer join
Something like this perhaps
DECLARE #T TABLE (ID INT, ProductID INT, TrDate DATE,InOut VARCHAR(10),Amount INT)
INSERT INTO #T VALUES
(1 ,1, '2016-01-01', 'I', 100),
(2 ,2, '2016-01-01', 'I', 100),
(3 ,3, '2016-02-01', 'I', 100),
(4 ,4, '2016-03-01', 'I', 100),
(5 ,1, '2016-03-01', 'I', 100),
(6 ,2, '2016-04-01', 'O', 10),
(7 ,3, '2016-05-01', 'I', 100),
(8 ,5, '2016-05-01', 'I', 100),
(9 ,5, '2016-05-01', 'O', 100),
(10 ,6, '2016-05-01', 'I', 100)
declare #m table (id int, menddate date)
insert #m values
(1,'2015-12-31'),(2,'2016-01-31'),(3,'2016-02-29'),(4,'2016-03-31'),
(5,'2016-04-30'),(6,'2016-05-31'),(7,'2016-06-30'),(4,'2016-07-31')
Select *
from
(
select -- t.*
x.xproductid , x.xyyyymm,
SUM(t.total) OVER (partition by x.xproductid
ORDER BY x.xyyyymm
ROWS UNBOUNDED PRECEDING) AS CumulativeTotal
from
(
SELECT t.ProductID tproductid, year(t.trdate) * 100 + month(t.trdate) tyyyymm,
sum(case when t.Inout = 'I' then t.Amount else t.amount * -1 end) as total
FROM #T t
group by ProductID, year(t.trdate) * 100 + month(t.trdate)
) t
right outer join
(select distinct productid as xproductid,year(m.menddate) * 100 + month(m.menddate) xyyyymm from #t t, #m m) x on x.xproductid = t.tproductid and x.xyyyymm = t.tyyyymm
) z
where z.xyyyymm >= 201601
order by z.xProductID,z.xyyyymm
Note the use of a right outer join to get all the month ends for all products

Date functions in Stored Procedure

Overview : I want to show the weekly result by input parameters startdate and enddate. I am getting this result quite well. But the problem here is, When i want start date from 28/08/2015 from end date 04/09/2015 am getting 28, 29, 30, 31, 01, 02, 03, 04 from same month(august). Expected result should be 28, 29, 30, 31 from august and 01, 02, 03, 04 from september.
Help me to overcome this problem. Below is my code
ALTER PROCEDURE [dbo].[usp_Get_TimesheetDetails]
#UserID int, #startdate datetime, #enddate datetime
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
declare #intStartDate int
declare #intEndDate int, #diff int
declare #strMonth varchar(50)
Select #intStartDate = DATEPART(day, #startDate)
Select #intEndDate = DATEPART(day, #endDate)
select #strMonth = DATENAME(MONTH, GETDATE())
Declare #temptable table (num date )
Declare #columns varchar(max)
DECLARE #sqlText nvarchar(1000);
DECLARE #startnum INT = #intStartDate-1
DECLARE #endnum INT = #intEndDate
select #diff = DATEDIFF(MONTH, #startdate, #enddate)
;WITH gen AS (
SELECT #startdate AS num
UNION ALL
SELECT DATEADD(DAY,1,num) FROM gen
WHERE DATEADD(DAY,1,num) <= #enddate
)
insert into #temptable SELECT num FROM gen
option (maxrecursion 10000)
set #columns=
(SELECT distinct
STUFF((SELECT ',' + CAST( DATEPART(DAY, num) as varchar(100)) [text()]
FROM #temptable
FOR XML PATH(''), TYPE)
.value('.','NVARCHAR(MAX)'),1,2,' ') List_Output
FROM #temptable t)
if(#startnum < 10)
BEGIN
SET #sqlText = N'SELECT ' + STUFF(REPLACE(#columns,',','],['),1,3,'') + ']' + ' FROM dbo.timesheet where month ='''+ #strMonth+''' and [Task ID] in(select TaskID from ManageTasks where TeamMemberUserID ='+ Cast(#UserID AS VARCHAR(max)) +')'
print #sqlText
END
else if(#startnum >= 10)
BEGIN
SET #sqlText = N'SELECT ' + STUFF(REPLACE(#columns,',','],['),1,4,'') + ']' + ' FROM dbo.timesheet where month ='''+ #strMonth+''' and [Task ID] in(select TaskID from ManageTasks where TeamMemberUserID ='+ Cast(#UserID AS VARCHAR(max)) +')'
END
print #sqlText
Exec (#sqlText)
end
end
Edited : I tried with if else condition like, if(monthdifference is equal to 0)
else(monthdifference is greater than 0). But not getting expected result.
try this
Declare
#StartDate datetime='2015/08/28',
#EndDate datetime='2015/09/04'
;WITH sample AS (
SELECT CAST(#StartDate AS DATETIME) AS dt
UNION ALL
SELECT DATEADD(dd, 1, dt)
FROM sample s
WHERE DATEADD(dd, 1, dt) <= CAST(#EndDate AS DATETIME))
SELECT *
FROM sample
output is :
2015-08-28 00:00:00.000
2015-08-29 00:00:00.000
2015-08-30 00:00:00.000
2015-08-31 00:00:00.000
2015-09-01 00:00:00.000
2015-09-02 00:00:00.000
2015-09-03 00:00:00.000
2015-09-04 00:00:00.000
Original Link : https://stackoverflow.com/a/3946151/3465753
Make sure you declared startdate as well as enddate in dynamic query
The main Ideas are:
You don't need to use STUFF. Just select dates from DATEADD(DAY,1,#startdate)
You should get dates twise if DATENAME(MONTH, #startdate)!=DATENAME(MONTH, #enddate). First time from startdate to end of months. Second time from start of second month to enddate.
My (checked) script.
ALTER PROCEDURE [dbo].[usp_Get_TimesheetDetails]
#UserID int, #startdate datetime, #enddate datetime
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
Declare #columns varchar(max);
DECLARE #sqlText nvarchar(1000);
Declare #temptable table (num date );
WITH gen AS (
SELECT DATEADD(DAY,1,#startdate) AS num
UNION ALL
SELECT DATEADD(DAY,1,num) FROM gen
WHERE DATEADD(DAY,1,num) <= #enddate
and DATEADD(DAY,1,num) < dateadd(month,datediff(month,0,#enddate),0)
)
insert into #temptable SELECT num FROM gen
option (maxrecursion 10000)
set #columns=
(SELECT distinct
STUFF((SELECT ',' + CAST( DATEPART(DAY, num) as varchar(100)) [text()]
FROM #temptable
FOR XML PATH(''), TYPE)
.value('.','NVARCHAR(MAX)'),1,1,'') List_Output
FROM #temptable t)
SET #sqlText = N'SELECT [' + REPLACE(#columns,',','],[') + ']' + ' FROM dbo.timesheet where month ='''+ DATENAME(MONTH, #startdate)+''' and [Task ID] in(select TaskID from ManageTasks where TeamMemberUserID ='+ Cast(#UserID AS VARCHAR(max)) +')';
print #sqlText;
IF DATENAME(MONTH, #startdate)!=DATENAME(MONTH, #enddate)
BEGIN
delete from #temptable;
WITH gen AS (
SELECT dateadd(month,datediff(month,0,#enddate),0) AS num
UNION ALL
SELECT DATEADD(DAY,1,num) FROM gen
WHERE DATEADD(DAY,1,num) <= #enddate
)
insert into #temptable SELECT num FROM gen
option (maxrecursion 10000)
set #columns=
(SELECT distinct
STUFF((SELECT ',' + CAST( DATEPART(DAY, num) as varchar(100)) [text()]
FROM #temptable
FOR XML PATH(''), TYPE)
.value('.','NVARCHAR(MAX)'),1,1,'') List_Output
FROM #temptable t)
SET #sqlText = N'SELECT [' + REPLACE(#columns,',','],[') + ']' + ' FROM dbo.timesheet where month ='''+ DATENAME(MONTH, #enddate)+''' and [Task ID] in(select TaskID from ManageTasks where TeamMemberUserID ='+ Cast(#UserID AS VARCHAR(max)) +')';
print #sqlText
end
end
You might find pivot/unpivot to be more robust. Certainly your table design is not ideal. (I pared down the number of columns for demonstration purposes.)
create table dbo.timesheet (
[month] varchar(12) not null,
[1] int null, [2] int null, [3] int null,
[28] int null, [29] int null, [30] int null, [31] int null
);
declare #startDate date = '20160628';
declare #endDate date = '20160703';
insert into dbo.timesheet ([month], [1], [2], [3], [28], [29], [30], [31])
values ('June', 1, 2, 3, 4, 5, 6, null), ('July', 8, 9, 10, 11, 12, 13, 14);
with hrs as (
select
hrs,
dy,
dateadd(
month,
case [month]
when 'January' then 1 when 'February' then 2 when 'March' then 3
when 'April' then 4 when 'May' then 5 when 'June' then 6
when 'July' then 7 when 'August' then 8 when 'September' then 9
when 'October' then 10 when 'November' then 11 when 'December' then 12
end,
dateadd(year, year(getdate()) - 2000, dateadd(day, dy - 1, '19991201'))
) as dt
from
(select [month], [1], [2], [3], [28], [29], [30], [31] from dbo.timesheet) t
unpivot (hrs for dy in ([1], [2], [3], [28], [29], [30], [31])) as upvt
)
select datename(month, dt), [1], [2], [3], [28], [29], [30], [31]
from hrs pivot (min(hrs) for dy in ([1], [2], [3], [28], [29], [30], [31])) as pvt
where dt between #startDate and #endDate;

How to create dates from date components in SQL (T-SQL)?

How can I construct native date data type values in SQL (T-SQL)?
I've added some examples, but please provide your own. My examples assume that the month and year are being stored (or are readily available) as integer values, but maybe your example will assume that the day and the month (or whatever) are stored as text. I can't see the future; surprise me.
SELECT DATEFROMPARTS(#Year, #Month, #Day)
(From SQL Server 2012)
Why, with input data as strings one of the most obvious (and therefore hardly surprising, sorry) solutions would be:
SELECT
mydate = CAST([year] + RIGHT('0' + [month], 2) + '01' AS datetime)
/* or 'AS date' in SQL Server 2008+ */
FROM (
SELECT [month] = '2', [year] = '2011' UNION ALL
SELECT [month] = '03', [year] = '2011' UNION ALL
SELECT [month] = '5', [year] = '2011' UNION ALL
SELECT [month] = '12', [year] = '2011' UNION ALL
SELECT [month] = '8', [year] = '2084' UNION ALL
SELECT [month] = '1', [year] = '1940'
) x;
The following code shows how to create date values from year and month (integer) values:
SELECT DATEADD(
month,
DATEDIFF( month, 0, GETDATE() )
+ x.[month]
- MONTH( GETDATE() ),
DATEADD(
year,
DATEDIFF( year, 0, GETDATE() )
+ x.[year]
- YEAR( GETDATE() ),
0 ) )
FROM ( SELECT [month] = 2, [year] = 2011
UNION ALL
SELECT [month] = 3, [year] = 2011
) x;
Date values from year, month, AND day (integer) values, though maybe the inputs should be sanitized first:
SELECT DATEADD(
day,
x.[day] - DAY(0),
DATEADD(
month,
x.[month] - MONTH(0),
DATEADD(
year,
x.[year] - YEAR(0),
0 ) ) )
FROM ( SELECT [month] = 2, [year] = 2011, [day] = 14
UNION ALL
SELECT [month] = 3, [year] = 2011, [day] = 2
UNION ALL
SELECT [month] = 5, [year] = 2011, [day] = 1
UNION ALL
SELECT [month] = 7, [year] = 2011, [day] = 0
UNION ALL
SELECT [month] = 8, [year] = 2084, [day] = 40
UNION ALL
SELECT [month] = 1, [year] = 1940, [day] = -6
) x;
More example code to create date values from year and month (integer) values, but even simpler than some other example code:
SELECT DATEADD(
month,
x.[month] - MONTH(0),
DATEADD(
year,
x.[year] - YEAR(0),
0 ) )
FROM ( SELECT [month] = 2, [year] = 2011
UNION ALL
SELECT [month] = 3, [year] = 2011
UNION ALL
SELECT [month] = 5, [year] = 2011
UNION ALL
SELECT [month] = 7, [year] = 2011
UNION ALL
SELECT [month] = 8, [year] = 2084
UNION ALL
SELECT [month] = 1, [year] = 1940
) x;