How do I PIVOT this SQL to achieve the desired result? - sql

I have a stored proc that produces a table like this:
PeriodStart PeriodEnd TotalActive TotalAdded TotalRemoved
2011-03-01 2011-03-07 123 456 789
2011-03-08 2011-03-14 567 789 123
2011-03-15 2011-03-21 444 555 666
etc...
I need to display the results in a table that looks like this:
03-01-2011 03-08-2011 03-15-2011
Active 123 567 444
Added 456 789 555
Removed 789 123 666
I've never had to use PIVOT before so I'm a little unsure of how to modify the below SQL in order to achieve the desired result. Here's the sproc:
ALTER PROCEDURE [dbo].[TrendReport]
#DateStart datetime,
#DateEnd datetime,
#Frequency varchar(5)
AS
BEGIN
SELECT
PeriodStart,
PeriodEnd,
SUM(TotalActive) As TotalActive,
SUM(TotalAdded) As TotalAdded,
SUM(TotalRemoved) As TotalRemoved
FROM (
SELECT
PeriodStart = CASE #Frequency
WHEN 'day' THEN DATEADD(hour, 0, DATEDIFF(DAY, 0, [Date]))
WHEN 'week' THEN DATEADD(hour, 0, DATEDIFF(DAY, 0, DATEADD(DAY, 1 - DATEPART(WEEKDAY, [Date]), [Date])))
WHEN 'month' THEN DATEADD(hour, 0, DATEDIFF(DAY, 0, DATEADD(MONTH, DATEDIFF(MONTH, 0, [Date]), 0)))
WHEN 'quarter' THEN DATEADD(hour, 0, DATEDIFF(DAY, 0, DATEADD(QUARTER, DATEDIFF(QUARTER, 0, [Date]), 0)))
WHEN 'year' THEN DATEADD(hour, 0, DATEDIFF(DAY, 0, DATEADD(YEAR, DATEDIFF(YEAR, 0, [Date]), 0)))
END,
PeriodEnd = CASE #Frequency
WHEN 'day' THEN DATEADD(s, -1, DATEADD(day, 1, DATEDIFF(DAY, 0, [Date])))
WHEN 'week' THEN DATEADD(s, -1, DATEADD(hour, 0, DATEDIFF(DAY, -1, DATEADD(DAY, 7 - DATEPART(WEEKDAY, [Date]), [Date]))))
WHEN 'month' THEN DATEADD(s, -1, DATEADD(hour, 0, DATEDIFF(DAY, -1, DATEADD(DAY, -1, DATEADD(MONTH, DATEDIFF(MONTH, 0, [Date]) + 1, 0)))))
WHEN 'quarter' THEN DATEADD(s, -1, DATEADD(hour, 0, DATEDIFF(DAY, -1, DATEADD(DAY, -1, DATEADD(QUARTER, DATEDIFF(QUARTER, 0, [Date]) + 1, 0)))))
WHEN 'year' THEN DATEADD(s, -1, DATEADD(hour, 0, DATEDIFF(DAY, -1, DATEADD(DAY, -1, DATEADD(YEAR, DATEDIFF(YEAR, 0, [Date]) + 1, 0)))))
END,
TotalActive,
TotalAdded,
TotalRemoved
FROM TrendReport P
WHERE [Date] BETWEEN #DateStart AND #DateEnd
) s
GROUP BY
PeriodStart,
PeriodEnd
ORDER BY PeriodStart
END

You actually need to first apply UNPIVOT to rotate the three quantity types from columns to rows and then apply PIVOT to rotate the period start date to columns and aggregate the quantities.
Here's the thing with using the PIVOT operator though, you need to know what columns you will have ahead of time (03-01-2011, 03-08-2011, 03-15-2011), it will need hard coded into the query (unless you want to get down and dirty with dynamic sql, which i would read up on before using in production)
Here is the solution:
WITH CTE AS (
SELECT
PeriodStart,
PeriodEnd,
SUM(TotalActive) As TotalActive,
SUM(TotalAdded) As TotalAdded,
SUM(TotalRemoved) As TotalRemoved
FROM (
SELECT
PeriodStart = CASE #Frequency
WHEN 'day' THEN DATEADD(hour, 0, DATEDIFF(DAY, 0, [Date]))
WHEN 'week' THEN DATEADD(hour, 0, DATEDIFF(DAY, 0, DATEADD(DAY, 1 - DATEPART(WEEKDAY, [Date]), [Date])))
WHEN 'month' THEN DATEADD(hour, 0, DATEDIFF(DAY, 0, DATEADD(MONTH, DATEDIFF(MONTH, 0, [Date]), 0)))
WHEN 'quarter' THEN DATEADD(hour, 0, DATEDIFF(DAY, 0, DATEADD(QUARTER, DATEDIFF(QUARTER, 0, [Date]), 0)))
WHEN 'year' THEN DATEADD(hour, 0, DATEDIFF(DAY, 0, DATEADD(YEAR, DATEDIFF(YEAR, 0, [Date]), 0)))
END,
PeriodEnd = CASE #Frequency
WHEN 'day' THEN DATEADD(s, -1, DATEADD(day, 1, DATEDIFF(DAY, 0, [Date])))
WHEN 'week' THEN DATEADD(s, -1, DATEADD(hour, 0, DATEDIFF(DAY, -1, DATEADD(DAY, 7 - DATEPART(WEEKDAY, [Date]), [Date]))))
WHEN 'month' THEN DATEADD(s, -1, DATEADD(hour, 0, DATEDIFF(DAY, -1, DATEADD(DAY, -1, DATEADD(MONTH, DATEDIFF(MONTH, 0, [Date]) + 1, 0)))))
WHEN 'quarter' THEN DATEADD(s, -1, DATEADD(hour, 0, DATEDIFF(DAY, -1, DATEADD(DAY, -1, DATEADD(QUARTER, DATEDIFF(QUARTER, 0, [Date]) + 1, 0)))))
WHEN 'year' THEN DATEADD(s, -1, DATEADD(hour, 0, DATEDIFF(DAY, -1, DATEADD(DAY, -1, DATEADD(YEAR, DATEDIFF(YEAR, 0, [Date]) + 1, 0)))))
END,
TotalActive,
TotalAdded,
TotalRemoved
FROM TrendReport P
WHERE [Date] BETWEEN #DateStart AND #DateEnd
) s
GROUP BY
PeriodStart,
PeriodEnd
ORDER BY PeriodStart
), U AS (
SELECT qty_type, PeriodStart, qty
FROM CTE
UNPIVOT(qty FOR qty_type IN([TotalActive], [TotalAdded], [TotalRemoved]))Q
)
SELECT * FROM U
PIVOT (SUM(Qty) FOR PeriodStart IN([03/01/2011], [03/08/2011], [03/15/2011])) AS P

I do not have an instance of SQL on my machine at the moment, so this may have some problems with syntax. However, give this a try...it is my attempt at the craftiness via dynamic SQL that J Cooper referred to:
CREATE TABLE #Temp1 (ID INT IDENTITY(1,1), PeriodStart DATETIME, PeriodEnd DATETIME, TotalActive INT, TotalAdded INT, TotalRemoved INT)
INSERT INTO #Temp1 (PeriodStart, PeriodEnd, TotalActive, TotalAdded, TotalRemoved)
SELECT
PeriodStart,
PeriodEnd,
TotalActive,
TotalAdded,
TotalRemoved
FROM (
SELECT
PeriodStart = CASE #Frequency
WHEN 'day' THEN DATEADD(hour, 0, DATEDIFF(DAY, 0, [Date]))
WHEN 'week' THEN DATEADD(hour, 0, DATEDIFF(DAY, 0, DATEADD(DAY, 1 - DATEPART(WEEKDAY, [Date]), [Date])))
WHEN 'month' THEN DATEADD(hour, 0, DATEDIFF(DAY, 0, DATEADD(MONTH, DATEDIFF(MONTH, 0, [Date]), 0)))
WHEN 'quarter' THEN DATEADD(hour, 0, DATEDIFF(DAY, 0, DATEADD(QUARTER, DATEDIFF(QUARTER, 0, [Date]), 0)))
WHEN 'year' THEN DATEADD(hour, 0, DATEDIFF(DAY, 0, DATEADD(YEAR, DATEDIFF(YEAR, 0, [Date]), 0)))
END,
PeriodEnd = CASE #Frequency
WHEN 'day' THEN DATEADD(s, -1, DATEADD(day, 1, DATEDIFF(DAY, 0, [Date])))
WHEN 'week' THEN DATEADD(s, -1, DATEADD(hour, 0, DATEDIFF(DAY, -1, DATEADD(DAY, 7 - DATEPART(WEEKDAY, [Date]), [Date]))))
WHEN 'month' THEN DATEADD(s, -1, DATEADD(hour, 0, DATEDIFF(DAY, -1, DATEADD(DAY, -1, DATEADD(MONTH, DATEDIFF(MONTH, 0, [Date]) + 1, 0)))))
WHEN 'quarter' THEN DATEADD(s, -1, DATEADD(hour, 0, DATEDIFF(DAY, -1, DATEADD(DAY, -1, DATEADD(QUARTER, DATEDIFF(QUARTER, 0, [Date]) + 1, 0)))))
WHEN 'year' THEN DATEADD(s, -1, DATEADD(hour, 0, DATEDIFF(DAY, -1, DATEADD(DAY, -1, DATEADD(YEAR, DATEDIFF(YEAR, 0, [Date]) + 1, 0)))))
END,
TotalActive,
TotalAdded,
TotalRemoved
FROM TrendReport P
WHERE [Date] BETWEEN #DateStart AND #DateEnd
) s
GROUP BY
PeriodStart,
PeriodEnd
ORDER BY PeriodStart
DECLARE #CreatePivot VARCHAR(MAX), #PivotColumns VARCHAR(MAX)
SET #CreatePivot = 'CREATE TABLE #Temp2 (BaseColumn VARCHAR(25)'
DECLARE #Count INT, #CurrentDateBeingChecked DATETIME
SET #Count = 1
WHILE(SELECT COUNT(*) FROM #Temp1 WHERE ID >= #Count) > 0
BEGIN
SELECT #CurrentDateBeingChecked = PeriodStart FROM #Temp1 WHERE ID = #Count
IF #Count > 1
SET #PivotColumns = #PivotColumns + ','
SET #PivotColumns = #PivotColumns + '[' + CAST(#CurrentDateBeingChecked AS VARCHAR(15) + ']'
SET #CreatePivot = #CreatePivot + ', [' + CAST(#CurrentDateBeingChecked AS VARCHAR(15) + '] INT'
SET #Count = #Count + 1
END
SET #CreatePivot = #CreatePivot + ')'
sp_executesql(#CreatePivot)
DECLARE #PivotStatementToRun VARCHAR(MAX)
SET #PivotStatementToRun =
'
INSERT INTO #Temp2
SELECT 'Active' AS BaseColumn,
' + #PivotColumns + '
FROM
(SELECT PeriodStart, TotalActive
FROM #Temp1) AS SourceTable
PIVOT
(
SUM(TotalActive)
FOR PeriodStart IN (
' + #PivotColumns + ')
) AS PivotTable;
'
sp_executesql(#PivotStatementToRun)
SET #PivotStatementToRun =
'
INSERT INTO #Temp2
SELECT 'Added' AS BaseColumn,
' + #PivotColumns + '
FROM
(SELECT PeriodStart, TotalAdded
FROM #Temp1) AS SourceTable
PIVOT
(
SUM(TotalAdded)
FOR PeriodStart IN (
' + #PivotColumns + ')
) AS PivotTable;
'
sp_executesql(#PivotStatementToRun)
SET #PivotStatementToRun =
'
INSERT INTO #Temp2
SELECT 'Removed' AS BaseColumn,
' + #PivotColumns + '
FROM
(SELECT PeriodStart, TotalRemoved
FROM #Temp1) AS SourceTable
PIVOT
(
SUM(TotalRemoved)
FOR PeriodStart IN (
' + #PivotColumns + ')
) AS PivotTable;
'
sp_executesql(#PivotStatementToRun)
SELECT * FROM #Temp2

Related

Rows to one column

i have sql query:
DECLARE #StartDate DATETIME,
#EndDate DATETIME;
SELECT #StartDate = DATEADD(yy, DATEDIFF(yy, 0, GETDATE()), 0)
,#EndDate = getdate();
SELECT LEFT(CONVERT(varchar, DATEADD(MM, DATEDIFF(MM, 0, DATEADD(MONTH, x.number, #StartDate)), 0),112),6) AS MonthName --LEFT(CONVERT(varchar, DATEADD(MONTH, x.number, #StartDate)), 0),112),6)
FROM master.dbo.spt_values x
WHERE x.type = 'P'
AND x.number <= DATEDIFF(MONTH, #StartDate, #EndDate)`
i need to do it in this format:
201901, 201902
Thanks
For the older version of SQL Server, you can try like following.
DECLARE #StartDate DATETIME,
#EndDate DATETIME;
SELECT #StartDate = DATEADD(yy, DATEDIFF(yy, 0, GETDATE()), 0)
,#EndDate = getdate();
;with cte as
(
SELECT LEFT(CONVERT(varchar, DATEADD(MM, DATEDIFF(MM, 0, DATEADD(MONTH, x.number, #StartDate)), 0),112),6) AS MonthName
FROM master.dbo.spt_values x
WHERE x.type = 'P'
AND x.number <= DATEDIFF(MONTH, #StartDate, #EndDate)
)
SELECT DISTINCT
STUFF ((
SELECT ', ' + [MonthName]
FROM
cte
ORDER BY [MonthName]
FOR XML PATH('')
),1,2,'' )
AS [MonthName]
For SQL Server 2017+, you can use STRING_AGG like following.
select STRING_AGG (MonthName, ',') as MonthName
FROM CTE
Try this below query:
Select (Stuff((Select ', ' + cast(eid as varchar(10)) From employee FOR XML PATH('')),1,2,''))

How to get count record for every month for year from SQL Server?

I tried this code, it's returning all records with month name, but without total count
SELECT
DATENAME(month, DATEADD(month, MONTH([R_datetime]), -1 )) MonName,
COUNT(*) count
FROM
[Tbl_TechRequest]
WHERE
([R_datetime]) BETWEEN DATEADD(yy, DATEDIFF(yy, 0, GETDATE()), 0)
AND DATEADD(yy, DATEDIFF(yy, 0, GETDATE()) + 1, -1)
GROUP BY
[R_datetime]
SELECT
DATEPART(year, R_datetme),
DATEPART(month, R_datetime),
count (*)
from myTable
where
[R_datetime]) > = DATEADD(yy, DATEDIFF(yy, 0, GETDATE()) + 1, -1)
group by
DATEPART(year, R_datetme),
DATEPART(month, R_datetime)
OR
SELECT
DATEPART(year, R_datetme)*100 + DATEPART(month, R_datetime),
count(*)
from myTable
where
[R_datetime]) > = DATEADD(yy, DATEDIFF(yy, 0, GETDATE()) + 1, -1)
group by
DATEPART(year, R_datetme)*100 + DATEPART(month, R_datetime)

How to get list of 2nd and 4th Saturday dates in SQL Server?

I am almost a newbie to writing SQL queries.
In the context of SQL Server, how to get list of 2nd and 4th Saturday dates
in the year 2016?
Done as a derived table simply to show the logic but you can reduce if you prefer:
select *
from (
select d2016,
datename( weekday, d2016 ) as wkdy,
row_number( ) over ( partition by datepart( month, d2016 ), datename( weekday, d2016 ) order by d2016 ) as rn_dy_mth
from (
select dateadd( day, rn, cast( '2016-01-01' as date ) ) as d2016
from (
select row_number() over( order by object_id ) - 1 as rn
from sys.columns
) as rn
) as dy
) as dy_mth
where rn_dy_mth in ( 2, 4 )
and wkdy = 'Saturday'
order by d2016
--DEFINE LIMITS FOR DAY
DECLARE #TODATE DATETIME, #FROMDATE DATETIME
SET #FROMDATE ='2010-01-01'
SET #TODATE = '2017-12-31'
;WITH DATESEQUENCE( [DATE] ) AS
(
SELECT #FROMDATE AS [DATE]
UNION ALL
SELECT DATEADD(DAY, 1, [DATE])
FROM DATESEQUENCE
WHERE DATE < #TODATE
)
, DATESATURDAY AS
(SELECT CAST(CAST(YEAR([DATE]) AS VARCHAR)+
(CASE WHEN DATEPART(M,[DATE])<=9 THEN '0'+CAST(DATEPART(M,[DATE]) AS VARCHAR)
ELSE CAST(DATEPART(M,[DATE]) AS VARCHAR) END ) AS NUMERIC) AS MONTH_ID
,CONVERT(VARCHAR,[DATE],106) AS DAY_DESC
,UPPER(DATENAME(DW,[DATE]))AS DAY_NAME
FROM DATESEQUENCE )
,SECOND_FOURTH_SATURDAY AS
(SELECT *
,ROW_NUMBER() OVER (PARTITION BY MONTH_ID ORDER BY DAY_NAME) FALL_IN
FROM DATESATURDAY
WHERE DAY_NAME='SATURDAY')
SELECT * FROM SECOND_FOURTH_SATURDAY
WHERE FALL_IN IN(2,4)
OPTION (MAXRECURSION 10000)
You can get any Saturday of a month using the Following Query in SQL.
Here I'm Getting on Current Date, You can set your own selected date to get a Specific month Saturday
select DATEADD(dd, (14 - ##DATEFIRST - DATEPART(dw, DATEADD(MONTH, DATEDIFF(mm, 0,getdate()), 0))) % 7, DATEADD(MONTH, DATEDIFF(mm, 0, getdate()), 0)) as FirstSaturday,
DATEADD(dd,7,DATEADD(dd, (14 - ##DATEFIRST - DATEPART(dw, DATEADD(MONTH, DATEDIFF(mm, 0, getdate()), 0))) % 7, DATEADD(MONTH, DATEDIFF(mm, 0, getdate()), 0))) as SecondSaturday,
DATEADD(dd,14,DATEADD(dd, (14 - ##DATEFIRST - DATEPART(dw, DATEADD(MONTH, DATEDIFF(mm, 0, getdate()), 0))) % 7, DATEADD(MONTH, DATEDIFF(mm, 0, getdate()), 0))) as ThirdSaturday,
DATEADD(dd,21,DATEADD(dd, (14 - ##DATEFIRST - DATEPART(dw, DATEADD(MONTH, DATEDIFF(mm, 0, getdate()), 0))) % 7, DATEADD(MONTH, DATEDIFF(mm, 0, getdate()), 0))) as LastSaturday

Calculate month number,month name, first day of month(date) and last day of month(date) using common table expression in sql server?

declare #start_date as datetime = '12/31/2014'
declare #end_date as datetime = '02/15/2015'
;with cte as (
select
datename(month,#start_date) as [mnth_nm],
month(#start_date) as [mnth_no],
#start_date as dat,
DATEADD(DAY, -1 * DAY(#start_date) + 1, #start_date) as [first_day],
DATEADD(dd, -DAY(DATEADD(mm, 1, #start_date)), DATEADD(mm, 1, #start_date)) as [last_day]
union all
select
datename(month, DateAdd(Month, 1, dat)),
month(dat) + 1 as [mnth_no],
DateAdd(Month, 1, dat),
DATEADD(MONTH, 1, [first_day]),
DATEADD(dd, -DAY(DATEADD(mm, 1, dat)),
DATEADD(mm, 1, dat)) + 1
from
cte
where
DateAdd(Month,1,dat) < #end_date
)
select
[mnth_nm], [mnth_no], #start_date, [first_day], [last_day]
from
CTE
In above CTE the required output should give december, january and february month output based on my start and end date.
Change the where query only:-
DateAdd(Month,0,dat)< #end_date
<b>Got the solution ... </b>
<p>
declare #start_date as datetime = '12/31/2014'
declare #end_date as datetime = '03/15/2015'
;with cte as (
select
datename(month,#start_date) as [mnth_nm],
month(#start_date) as [mnth_no],
#start_date as dat,
DATEADD(DAY, -1 * DAY(#start_date) + 1, #start_date) as [first_day],
DATEADD(dd, -DAY(DATEADD(mm, 1, #start_date)), DATEADD(mm, 1, #start_date)) as [last_day]
union All
select
datename(month, DateAdd(Month, 1, dat)),
month(DateAdd(m,1,dat)) as [mnth_no],
DateAdd(Month, 1, dat),
DATEADD(MONTH, 1, [first_day]),
DATEADD(d, -1, DATEADD(m, DATEDIFF(m, 0, DATEADD(MONTH, 1, [first_day])) + 1, 0))
from
cte
where
DateAdd(Month,0,dat)< #end_date
)
select
[mnth_nm], [mnth_no], #start_date, [first_day], [last_day]
from
CTE</p>

SQL Server correct way to account for the weekend

I have a few sql statements where I manually enter the date everyday then execute.
I would like to automate the date part. But weekends are giving me trouble. I use excel to run the reports as it is then saved and another member of staff does a mailshot.
This is my test example:
if (datename(dw, getdate()) = 'Friday') BEGIN
select convert(varchar, dateadd(day, 7, DATEADD(dd, 0, DATEDIFF(dd, 0,getdate()))), 103)
+ ',' + convert(varchar, dateadd(day, 9, DATEADD(dd, 0, DATEDIFF(dd, 0,getdate()))), 103) AS [Date] END
ELSE BEGIN select convert(varchar, dateadd(day, 7, DATEADD(dd, 0, DATEDIFF(dd, 0,getdate()))), 103) as [Date] END;
But how would I put this into a sql where statement?
for example
select * from table where (date = )
where Date equals 7 days in advance unless It's Friday when we have to account for the weekend so we want 7,8 and 9 days in advance?
select * from table where date = (case
when datename(dw, getdate()) = 'Friday' then
convert(varchar, dateadd(day, 7, DATEADD(dd, 0, DATEDIFF(dd, 0,getdate()))), 103)
+ ',' + convert(varchar, dateadd(day, 9, DATEADD(dd, 0, DATEDIFF(dd, 0,getdate()))), 103)
else
convert(varchar, dateadd(day, 7, DATEADD(dd, 0, DATEDIFF(dd, 0,getdate()))), 103)
end)