Pivot In Values with dates? - sql

At the end of my sql, I am using the following code. Is there any way of replacing the fixed strings [2011/07/14], [2011/07/16], etc, to GetDate() value?
PIVOT
(
count([AppointmentsBooked])
FOR [date] IN ([2011/07/14], [2011/07/16], [2011/07/17],[2011/07/18],[2011/07/21])
) as pivottable

Can you try to use BETWEEN and DATEADD in following manner:
DECLARE #dates TABLE(value DateTime)
INSERT INTO #dates VALUES
(GETDATE()),
('2011/07/9'),
('2011/07/17'),
('2011/07/18'),
('2011/07/21')
SELECT value FROM #dates
WHERE value BETWEEN GETDATE() AND DATEADD(day, 5, GETDATE())

You can create a string of all dates which is comma seperated with '[' and ']' before and after each date. assign this string to a string variable (#dates) and use the string spit method to split all dates inside the pivot query.

this question was posted about a year ago. i don't care. i have some code that might be exactly what the OP wanted.... i'm sure he'll never come back and chose an answer but still.... all i want to do is count the records by month with a pivot for a few tables and ultimately compare the number of records for each month for each table. however... in this code there is only one table (rt_taco_15m) but that doesn't matter. i just haven't written the rest to completely fit my needs. but i think it fits the needs of the OP or at least gets him on a good start.... if he truly has been waiting a year on this problem. lol.
if object_id('tempdb..#temp') is not null drop table #temp
if object_id('tempdb..#temp2') is not null drop table #temp2
if object_id('tempdb..#temp3') is not null drop table #temp3
declare #start_date as datetime
set #start_date = cast('1-1-2012' as datetime)
declare #end_date as datetime
set #end_date = cast('9-1-2012' as datetime)
;with cte as (
select #start_date as [start],
dateadd(month, 1, #start_date) as [end]
union all
select dateadd(month, 1, [start]) as [start],
dateadd(month, 1, dateadd(month, 1, [start])) as [end]
from cte
where dateadd(month, 1, [start]) <= #end_date
)
(select 'rt_taco_15m' as table_name,
convert(varchar(10), [start], 101) as [start],
convert(varchar(10), [end], 101) as [end],
datename(month, [start]) as month_name,
cast([start] as integer) as orderby,
count(taco.taco_record_id) as [range_count]
into #temp
from cte
left outer join rt_taco_15m as taco
on taco.period >= cte.[start] and
taco.period < cte.[end]
group by cte.[start], cte.[end])
select table_name as table_name,
convert(varchar(10), getdate(), 101) as [start],
convert(varchar(10), getdate(), 101) as [end],
'Total' as month_name,
cast(dateadd(month,2,#end_date) as integer) as orderby,
range_sum as [range_count]
into #temp2
from (select table_name, sum([range_count]) as range_sum
from #temp group by table_name) as summed_up
select *
into #temp3
from (select * from #temp
union all
select * from #temp2) as x
order by orderby
select * from #temp3
declare #cols nvarchar(2000)
select #cols = stuff(
(select '],[' + month_name
from #temp3
order by orderby
for xml path('') )
, 1, 2, '') + ']'
print #cols
if object_id('tempdb..#temp2') is not null drop table #temp2
declare #query varchar(max)
set #query = N'
select table_name, ' + #cols + N'
from (select table_name, month_name, range_count
from #temp3) p
pivot ( sum(range_count) for month_name in ( '+ #cols +' ) ) as pvt'
execute(#query

Related

How to write a loop for such sql query

I need to write such query:
SELECT CAST (date_time_column AS DATE) as 'Date',
AVG(CASE WHEN FORMAT(date_time_column, 'HH:mm') = '00:00' then my_values ELSE NULL end) as '00:00',
........
AVG(CASE WHEN FORMAT(date_time_column, 'HH:mm') = '23:59' then my_values ELSE NULL end) as '23:59'
FROM table
where date_time_column > '2021-08-12'
GROUP BY CAST (date_time_column AS DATE)
What is a way to avoid writing 1440 lines in a query?
Try the below method (Change the variables to match your table and field)
/*Generate all minutes in a day in a string variable*/
Declare #timeRange varchar(max)=null
declare #startdate Datetime='2021-08-12 00:00';
; WITH cte AS
(
SELECT 1 i, #startdate AS resultDate
UNION ALL
SELECT i + 1, DATEADD(minute, i, #startdate )
FROM cte
WHERE DATEADD(minute, i, #startdate ) < DateAdd(day,1,#startdate)
)
SELECT #timeRange=Coalesce(#timeRange +',' + '['+Format(resultDate,'HH:mm')+']','['+Format(resultDate,'HH:mm')+']') FROM cte
OPTION (MAXRECURSION 2000);
/* (Change These variables to match your table & fields */
declare #filterQuery varchar(300)=' where {date_time_column}>''2021-01-01''';
declare #dateTimeColumn varchar(30)='{date_time_column}';
declare #valueColumn varchar(30)='{value_column}';
declare #myTable varchar(20)='{my_table}';
/*Generate Pivot Query */
DECLARE #query AS NVARCHAR(MAX);
set #query= '
SELECT *
FROM (SELECT Cast('+ #dateTimeColumn +' as Date) Resultdate,
       FORMAT(' + #dateTimeColumn + ', ''HH:mm'') DateMinute,
       ' + #valueColumn + ' FROM ' + #myTable + ' ' + #filterQuery +'
     ) FormattedData
PIVOT( AVG('+ #valueColumn + ')  
    FOR DateMinute IN ('+ #timeRange +')
) AS pivotTable
';
/*Execute Generated Query*/
execute(#query)
What you want to do is to return your data in rows, not columns. That is a row for every minute rather than a column for every minute.
Try it something like this:
WITH
cteNums AS
(
Select TOP (1440) ROW_NUMBER() OVER(order by (Select Null)) - 1 as num
From sys.all_columns
Order By num
)
, cteMinutes AS
(
Select FORMAT(CAST(num/cast(1440.0 as real) as DATETIME), N'HH:mm') as Minutes
From cteNums
)
select CAST(t.date_time_column AS DATE) as 'Date',
m.Minutes,
AVG(CASE WHEN FORMAT(t.date_time_column, 'HH:mm') = m.Minute then t.my_values ELSE NULL end) as AvgValues
FROM cteMinutes m
LEFT JOIN [table] t ON m.Minutes = FORMAT(t.date_time_column, 'HH:mm')
where t.date_time_column > '2021-08-12'
GROUP BY CAST(t.date_time_column AS DATE), m.Minutes
ORDER BY CAST(t.date_time_column AS DATE), m.Minutes
;
I cannot, of course, test this as no test data or table definitions were provided.

Keep only Day from Date in column name

i have created a stored procedure by online help to generate a monthly attendance report
USE [Attendace]
GO
/****** Object: StoredProcedure [dbo].[PerDayAttendance] Script Date: 04/11/2018 20:16:05 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[PerDayAttendance]
#STARTDATE DATE,
#ENDDATE DATE
AS BEGIN
WITH DATERANGE AS
(
SELECT DT =DATEADD(DD,0, #STARTDATE)
WHERE DATEADD(DD, 1, #STARTDATE) <= #ENDDATE
UNION ALL
SELECT DATEADD(DD, 1, DT)
FROM DATERANGE
WHERE DATEADD(DD, 1, DT) <= #ENDDATE
) SELECT * INTO cte_DATES
FROM DATERANGE
DECLARE #COLUMN varchar(max)
SELECT #COLUMN=ISNULL(#COLUMN+',','')+ '['+ CAST(CONVERT(DATE , T.DT) AS varchar) + ']' FROM cte_DATES T
DECLARE #Columns2 varchar(max)
SET #Columns2 = SUBSTRING((SELECT DISTINCT ',ISNULL(['+ CAST(CONVERT(DATE , DT)
as varchar )+'],'''') AS ['+ CAST(CONVERT(DATE , DT) as varchar )+']'
FROM cte_DATES GROUP BY dt FOR XML PATH('')),2,8000)
DECLARE #QUERY varchar(MAX)
SET #QUERY = 'SELECT P.EID, ENAME, ' + #Columns2 +', Wdays, Holidays, K.Present, (Wdays-(Holidays + K.Present))as Absent, (cast(((s.Salary/Wdays)*(k.present+Holidays)) as numeric(36,0))) as Salary FROM
(
SELECT A.EID, A.ENAME , B.DT AS DATE, (Case when cast(A.WorkTime as time) >
''00:00:00'' then ''P'' else ''Abs'' end) as worktime FROM TblAttendnce A
RIGHT OUTER JOIN cte_DATES B
ON A.EDATE=B.DT
) X
PIVOT
(
MIN([Worktime])
FOR [DATE] IN (' + #COLUMN + ')
) P
Cross apply (select Wdays, Holidays from dbo.fn_Fn1(''' + CAST(#STARTDATE AS VARCHAR(50)) + ''','''+ CAST(#ENDDATE as Varchar(50))+'''))H
Right Outer Join (select eid, COUNT(present) as present from Attendace.dbo.vwPayroll
where Edate between ''' + CAST(#STARTDATE AS VARCHAR(50)) + '''and'''+ CAST(#ENDDATE as Varchar(50))+'''
group by eid) as K on K.eid=p.EID
Right Outer Join ((select eid, salary from dbo.employeeMast))as s on S.eid=p.eid
WHERE ISNULL(ENAME,'''')<>''''
'
Exec (#QUERY)
DROP TABLE cte_DATES
END
Now the outcome is like this
i need to prepare a crystal report from this procedure, but due to dynamic column header name i am unable to do this.
My Query is how to make column name as 01, 02, 03 instead of 2018-04-01, 2018-04-02, 2018-04-03
i mean i want to rename column name as DD of dd/mm/yyyy
by this way i may able to reflect it in crystal report.
When I am developing a Crystal Report or training a client; I teach them either to rename to the data field in the SQL or stored procedure such date as 3 or Insert a Duplicate Detail line and use the OLD line for Development. Making the NEW Line a Comment Line.

how to use MAX for multiple values in dyamnmic Pivot

I have a query when I'm using static Dynamic pivot it is working fine
Static Script :
Select Name, tableName,MAX([2017-07-10])[2017-07-10],MAX([2017-07-09])[2017-07-09]
from (
SELECT Target_Db_Name,
Target_Tbl_Name,
Cnt
FROM Table1 l
WHERE Name='Employee' AND
dt >= CAST(dateadd(day, -1, getdate()) as date))T
PIVOT (MAX(cnt)FOR dt IN ([2017-07-10],[2017-07-09]) )PVT
GROUP BY Name, tableName
when I'm trying to implement dynamic query in the same query I'm confused to get max values for multiple comma separated data values
Dynamic Script :
DECLARE #cols AS NVARCHAR(MAX)='';
DECLARE #query AS NVARCHAR(MAX)='';
SELECT #cols = #cols + QUOTENAME(dt) + ',' FROM (select distinct CONVERT(DATE,t)Start_dt from Events
WHERE Start_dt >= CAST(dateadd(day, -1, getdate()) as date) ) as tmp ORDER BY Start_dt desc
select #cols = substring(#cols, 0, len(#cols))
Select #query = '
Select Name, tableName,'+#cols+'
from (
SELECT Name,
tableName,
Cnt
FROM Table1 l
WHERE Name=''Employee'' AND
Start_dt >= CAST(dateadd(day, -1, getdate()) as date))T
PIVOT (MAX(Cnt)FOR Start_dt IN ('+#cols+') )PVT
GROUP BY Name, tableName
'
EXEC (#query)
So here date values are coming like this
([2017-07-10]),([2017-07-09]) in dynamic how to apply
MAX([2017-07-10]),MAX([2017-07-09])
how to get max for each date in Dynamic Pivot
Here is one way to get the Max aggregate in column list
SET #cols = stuff((SELECT DISTINCT ','+Quotename(CONVERT(DATE, Start_dt))
FROM Events
WHERE Start_dt >= Cast(Dateadd(day, -1, Getdate()) AS DATE)
ORDER BY Start_dt DESC
FOR xml path('')) ,1,1,'')
--Print #cols
SET #select_cols = stuff((SELECT DISTINCT ',Max('+Quotename(CONVERT(DATE, Start_dt))+') as '+Quotename(CONVERT(DATE, t))
FROM Events
WHERE Start_dt >= Cast(Dateadd(day, -1, Getdate()) AS DATE)
ORDER BY Start_dt DESC
FOR xml path('')) ,1,1,'')
--Print #select_cols
Select #query = '
Select Name, tableName,'+#select_cols+'
from (
SELECT Name,
tableName,
Cnt
FROM Table1 l
WHERE Name=''Employee'' AND
Start_dt >= CAST(dateadd(day, -1, getdate()) as date))T
PIVOT (MAX(Cnt)FOR Start_dt IN ('+#cols+') )PVT
GROUP BY Name, tableName
'
EXEC (#query)
Note : Your current method to convert rows into csv's isn't guaranteed to work all the time. I have used for xml path() method to do the same

A string of quarterly dates between a start and end date

I've got a recursive cte working to generate a list of dates between #startDate and #endDate, incrementing by quarters.
declare #startDate datetime
declare #endDate datetime
set #startDate= '01-jan-2014'
set #endDate= '01-jul-2017'
;With cte
As
( Select #startDate date1
Union All
Select DateAdd(Month,3,date1) From cte where date1 < #endDate
) select cast(cast( Year(date1)*10000 + MONTH(date1)*100 + 1 as
varchar(255)) as date) quarterlyDates From cte
This yields:
quarterlyDates
--------------
2014-01-01
2014-04-01
2014-07-01
2014-10-01 ...
I'd like to concatenate the output of the cte into a single string as follows:
"'01-jan-2014', '01-apr-2014, '01-jul-2014'..."
etc. I'm baffled by this last step - any help would be greatly appreciated!
Not sure why you want to... but just wrap that bottom cte and use stuff.
declare #table table(quarterlyDates date)
insert into #table
values
('2014-01-01'),
('2014-04-01'),
('2014-07-01'),
('2014-10-01')
SELECT stuff((
SELECT ', ' + cast(quarterlyDates as varchar(max))
FROM #table
FOR XML PATH('')
), 1, 2, '')
And in your code... though the second CTE isn't necessary I leave it for clarity.
declare #startDate datetime
declare #endDate datetime
set #startDate= '01-jan-2014'
set #endDate= '01-jul-2017'
;With cte
As
( Select #startDate date1
Union All
Select DateAdd(Month,3,date1) From cte where date1 < #endDate
),
cte2 as(
select cast(cast( Year(date1)*10000 + MONTH(date1)*100 + 1 as
varchar(255)) as date) quarterlyDates From cte)
SELECT stuff((
SELECT ', ' + cast(quarterlyDates as varchar(max))
FROM cte2
FOR XML PATH('')
), 1, 2, '');
use FOR XML Path with type directive to avoid encoding of illegal characters in result
;WITH cte
AS (SELECT #startDate date1
UNION ALL
SELECT Dateadd(month, 3, date1)
FROM cte
WHERE date1 < #endDate)
SELECT Stuff((SELECT ',' + CONVERT(VARCHAR(15), date1, 106) quarterlyDates
FROM cte
FOR xml path, type).value('.[1]', 'nvarchar(max)'), 1, 1, '');
Note : I have altered the unwanted manipulating in the final select. Use style 106 in Convert function to get the required output format
Late answer and just for fun.
Example
declare #startDate datetime = '01-jan-2014'
declare #endDate datetime = '01-jul-2017'
;With cte
As
( Select date1 = #startDate
,dates = ''''+convert(varchar(max),convert(varchar(11),#startDate,113))+''''
Union All
Select DateAdd(Month,3,date1)
,cte.dates+','''+convert(varchar(11),DateAdd(Month,3,date1) ,106)+''''
From cte where date1 < #endDate
)
Select Top 1 with ties
Dates=lower(replace(Dates,' ','-') )
From cte
Order By Date1 Desc
Returns
'01-jan-2014','01-apr-2014','01-jul-2014','01-oct-2014','01-jan-2015','01-apr-2015','01-jul-2015','01-oct-2015','01-jan-2016','01-apr-2016','01-jul-2016','01-oct-2016','01-jan-2017','01-apr-2017','01-jul-2017'

How to create Temp Table with month columns based on date parameters?

I have a stored procedure that accepts two Dates. In my stored procedure, I need to create a temp table with the months in between the two dates as columns.
For example,
If the user passes in
1/1/2016 , 8/1/2016
I need a temp table with the columns:
January February March April May June July August
How could I create this type of temp table with columns created in this manner? With the columns being based on the two dates passed in?
The following script should get you started (and almost there):
declare #start_date DATE = '20160101'
declare #end_date DATE = '20160801'
;WITH CTE AS
(
SELECT #start_date AS cte_start_date, DATENAME(month, #start_date) AS Name,
CAST(' ALTER TABLE #myTemp ADD ' + DATENAME(month, #start_date) + ' INT ' + CHAR(13) + CHAR(10) AS VARCHAR(8000)) AS SqlStr
UNION ALL
SELECT DATEADD(MONTH, 1, cte_start_date), DATENAME(month, DATEADD(MONTH, 1, cte_start_date)) AS Name,
CAST(SqlStr + ' ALTER TABLE #myTemp ADD ' + DATENAME(month, DATEADD(MONTH, 1, cte_start_date)) + ' INT ' + CHAR(13) + CHAR(10) AS VARCHAR(8000))
FROM CTE
WHERE DATEADD(MONTH, 1, cte_start_date) <= #end_date
)
SELECT cte_start_date, Name, SqlStr
FROM CTE
Using a recursive-CTE it generates a loop between start and end date, and for each month it computes its string representation and also creates a alter script to add the columns to a temporary table.
The CTE computes the SQL script gradually, so that the final script is on the last line.
Try This ....
declare #start_date DATE = '20160101'
declare #end_date DATE = '20160801'
;WITH CTE AS
(
SELECT #start_date AS cte_start_date, DATENAME(month, #start_date) AS NAME , 0 AS Coun
UNION ALL
SELECT DATEADD(MONTH, 1, cte_start_date), DATENAME(month, DATEADD(MONTH, 1, cte_start_date)) AS NAME , 0 AS Coun
FROM CTE
WHERE DATEADD(MONTH, 1, cte_start_date) <= #end_date
)
SELECT Coun,Name
INTO #tmp1
FROM CTE
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + Name
from #tmp1
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT ' + #cols + ' from
(
select Coun,Name
from #tmp1
) x
pivot
(
MAX(Coun)
for Name in (' + #cols + ')
) p '
execute(#query);
DROP TABLE #tmp1
It Will Return OutPut Like your expected output .......