A string of quarterly dates between a start and end date - sql

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'

Related

Use dynamic value to a variable which is being used in the subsequent dynamic query in my code

This code works when set #ToDate = '20200311' i.e. when I have hardcoded the specific date, but does not work when I want this to always execute till the current date
The line set #ToDate = select .....getdate.. throws error, can you please help me out and tell me what I should do to rectify?
While ##Fetchstatus =0,
begin
set #Fromdate = '20180102';
set #ToDate = select convert (varchar, getdate(), 112);
while (#Fromdate < #ToDate) begin
set #StrQuery = 'INSERT into dbo.Tmp_M_Rates (Rate_Date, Rate_D, Entity, cur) values ('
+''''+ CAST (#FromDate AS nvarchar) + ''', ''' + Substring (CONVERT (VARCHAR
(10), #Fromdate, 112),3,8) + ''', ''' + #EntityName + ''', ''' + #Cur +
''')'
EXEC (#StrQuery)
set #Fromdate = dateadd(DAY,1,#Fromdate)
end
If Gordon's answer has the correct idea, a far better approach would be to not use any type of recursion. SQL Server is designed for set based methods, and so you should really be using one.
The method you want here is a Tally, which isn't recursive, and isn't limited to 100 loops by default (like an rCTE) because it doesn't loop:
DECLARE #FromDate date = '20180102';
WITH N AS(
SELECT N
FROM (VALUES(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL))N(N)),
Tally AS(
SELECT TOP (DATEDIFF(DAY,#FromDate, GETDATE())+1)
ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS I
FROM N N1, N N2, N N3), --1000 rows, add more cross joins to N for more rows
Dates AS(
SELECT DATEADD(DAY, T.I, #FromDate) AS D
FROM Tally T)
INSERT INTO dbo.Tmp_M_Rates (Rate_Date, Rate_D, Entity, cur)
SELECT D,
SUBSTRING(CONVERT(VARCHAR(10), #Fromdate, 112), 3, 8),
#EntityName,
#Cur
FROM Dates;
Just another option with an ad-hoc tally/numbers table
Example
Declare #Date1 date = '2018-01-02'
Declare #Date2 date = getdate()
Declare #Entity varchar(50) ='Entity Name'
Declare #Cur varchar(50) ='USD'
--INSERT into dbo.Tmp_M_Rates (Rate_Date, Rate_D, Entity, cur)
Select Rate_Date = D
,Rate_D = dateadd(DAY,1,D)
,Entity = #Entity
,Cur = #Cur
From (
Select Top (DateDiff(DAY,#Date1,#Date2)+1)
D=DateAdd(DAY,-1+Row_Number() Over (Order By (Select Null)),#Date1)
From master..spt_values n1,master..spt_values n2
) A
Generates the Following for INSERT
You can phrase this as a single query with no dynamic SQL:
WITH dates as (
SELECT CONVERT(date, '20180102') as dte
UNION ALL
SELECT dateadd(day, 1, dte)
FROM CTE
WHERE dte <= GETDATE()
)
INSERT into dbo.Tmp_M_Rates (Rate_Date, Rate_D, Entity, cur)
SELECT dte, SUBSTRING(CONVERT(VARCHAR(10), #Fromdate, 112), 3, 8),
#EntityName, #Cur
FROM dates
OPTION (MAXRECURSION 0);
I would strongly recommend that you take this or a similar approach.

How can I find how many Week Numbers are in a given interval of time in T-SQL

I am a bit stuck here, I want to find all the week numbers in a given interval of time and I can't really figure it out
For example , instead of
- > datepart(week,dateadd(day,-1,#oneSingleDate)) (monday is the first day of the week) ,
I need something like
- > datepart(week,#startDate,#endDate)
Given the interval '2019-01-04'-'2019-01-28' the output needs to be :
WeekNo : 1,2,3,4,5 .
i've used a recursive CTE to generate all the dates in the range, then I've selected the DISTINCT week numbers from them using DATEPART. Then I've concatenated then into your comma-separated string into a variable called #OUT
DECLARE #startDate as date = '20190104';
DECLARE #endDate as date = '2019-01-28';
DECLARE #OUT as nvarchar(max);
WITH CTE AS (SELECT #startDate As X
UNION ALL
SELECT DATEADD(y, 1, X) X FROM CTE where x < #endDate),
CTE2 AS (SELECT DISTINCT datepart(wk, X) wk from cte)
select #out = coalesce(#out + ', ', '') + cast(wk as nvarchar(4)) from cte2
OPTION (MAXRECURSION 0);
select #out;
You can do it as follows:
using :
- [CTE][1] common Table expression
- [Sql recursion union all][2]
- [Concatenation of multiple rows into one line][3]
declare #startDate as date ;
declare #endDate as date ;
set #startDate='2019-01-04';
set #endDate='2019-01-28' ;
DECLARE #weeks VARCHAR(8000) ;
with cte as (select #startDate as mydate,datepart(week,#startDate) w
union all select
dateadd(day,1,mydate),datepart(week,dateadd(day,1,mydate)) from cte
where mydate < #endDate) , cte2 as (select distinct(w) from cte)
select #weeks=COALESCE(#weeks + ', ', '') +cast(w as varchar(2)) from
cte2 OPTION (MAXRECURSION 360) select #weeks [Result]

Get all startdate and enddate as comma seperate in stuff query

Hi I have startdate as GetDate()-5 and enddate as GetDate()
I am passing this date in variable i.e. this dates are not stored in any table so how can I get records as
GetDate()-5,GetDate()-4,GetDate()-3,GetDate()-2,GetDate()-1,GetDate()
in sql server
I have tried below query which is totally wrong, Can any one please suggest me
declare #startdate date
declare #enddate date
set #startdate =GETDATE()-5
set #enddate =GETDATE()
STUFF((SELECT DISTINCT ','+CONVERT(VARCHAR, CONVERT(DATE,GETDATE()-5))
FROM [Table]--What should be here?
WHERE CONVERT(date,AETE.TimeEntryDate) between CONVERT(DATE,#startdate)
AND CONVERT(DATE, #enddate)
FOR XML PATH('')), 1,1,'')
A CTE might come in handy here:
declare #startdate date = dateadd(D, -5, getdate())
declare #enddate date = getdate();
with cte(d) as (
select #startdate
union all
select dateadd(D, 1, d)
from cte
where d < #enddate
)
select stuff((
select ', ' + convert(nvarchar(255), d, 101)
from cte
for xml path('')), 1, 2, '')
gives you:
08/09/2018, 08/10/2018, 08/11/2018, 08/12/2018, 08/13/2018, 08/14/2018
Note that this gives you 6 days---the 5 days less than today, as well as today's date.

How to sort varchar string properly with numeric values on both ends?

I'm building a Common Table Expression (CTE) in SQL Server 2008 to use in a PIVOT query.
I'm having difficulty sorting the output properly because there are numeric values that sandwich the string data in the middle. Is it possible to do this?
This is a quick and dirty example, the real query will span several years worth of values.
Example:
Declare #startdate as varchar(max);
Declare #enddate as varchar(max);
Set #startdate = cast((DATEPART(yyyy, GetDate())-1) as varchar(4))+'-12-01';
Set #enddate = cast((DATEPART(yyyy, GetDate())) as varchar(4))+'-03-15';
WITH DateRange(dt) AS
(
SELECT CONVERT(datetime, #startdate) dt
UNION ALL
SELECT DATEADD(dd,1,dt) dt FROM DateRange WHERE dt < CONVERT(datetime, #enddate)
)
SELECT DISTINCT ',' + QUOTENAME((cast(DATEPART(yyyy, dt) as varchar(4)))+'-Week'+(cast(DATEPART(ww, dt) as varchar(2)))) FROM DateRange
Current Output:
,[2012-Week48]
,[2012-Week49]
,[2012-Week50]
,[2012-Week51]
,[2012-Week52]
,[2012-Week53]
,[2013-Week1]
,[2013-Week10]
,[2013-Week11]
,[2013-Week2]
,[2013-Week3]
,[2013-Week4]
,[2013-Week5]
,[2013-Week6]
,[2013-Week7]
,[2013-Week8]
,[2013-Week9]
Desired Output:
,[2012-Week48]
,[2012-Week49]
,[2012-Week50]
,[2012-Week51]
,[2012-Week52]
,[2012-Week53]
,[2013-Week1]
,[2013-Week2]
,[2013-Week3]
,[2013-Week4]
,[2013-Week5]
,[2013-Week6]
,[2013-Week7]
,[2013-Week8]
,[2013-Week9]
,[2013-Week10]
,[2013-Week11]
EDIT
Of course after I post the question my brain started working. I changed the DATEADD to add 1 week instead of 1 day and then took out the DISTINCT in the select and it worked.
DECLARE #startdate AS VARCHAR(MAX);
DECLARE #enddate AS VARCHAR(MAX);
SET #startdate = CAST((DATEPART(yyyy, GetDate())-1) AS VARCHAR(4))+'-12-01';
SET #enddate = CAST((DATEPART(yyyy, GetDate())) AS VARCHAR(4))+'-03-15';
WITH DateRange(dt) AS
(
SELECT CONVERT(datetime, #startdate) dt
UNION ALL
SELECT DATEADD(ww,1,dt) dt FROM DateRange WHERE dt < CONVERT(datetime, #enddate)
)
SELECT ',' + QUOTENAME((CAST(DATEPART(yyyy, dt) AS VARCHAR(4)))+'-Week'+(CAST(DATEPART(ww, dt) AS VARCHAR(2)))) FROM DateRange
I can't see the sample SQL code (that site is blacklisted where I am).
Here is a trick for sorting that data in the proper order is to use the length first and then the values:
select col
from t
order by left(col, 6), len(col), col;
Have you considered to sort on two temporary columns (year in smallint and week in tinyint to save space … or directly using the datepart integer if space is not a problem to you and you prefer fast run) along with the use of "order by year, week" ?
If you store dates using a more suitable type (what I suggest), it would then become :
WITH [Define the CTE expression name and column list]
AS
(
SELECT CAST(DATEPART(yyyy, dt) as smallint(4)) year, cast(DATEPART(ww, dt) as tinyint(2)) week, [your columns here]
FROM DateRange WHERE dt < #enddate
)
[Define the outer query referencing the CTE name]
ORDER BY year, week;
GO
Also, please note that string operations will slow your queries so avoid them when possible !
I like Gordon's answer, but if you were hell-bent on text manipulation in your order by:
ORDER BY CAST(REPLACE(LEFT('[2012-Week48]',5),'[','')AS INT)
,CAST(REPLACE(RIGHT('[2012-Week48]',CHARINDEX('Week','[2012-Week48]')-4),']','') AS INT)
Here is another option converting the beginning and ending parts of the column to integer.
SELECT *
FROM YourTable
ORDER BY CAST(SUBSTRING(yourcolumn,1,4) as int),
CAST(SUBSTRING(yourcolumn,CHARINDEX('Week',yourcolumn)+4,len(yourcolumn)) as int)
SQL Fiddle Demo
This will work assuming the format of the data is always the same.
Since you are using dt to generate the string, you should sort by using the date's parts:
WITH DateRange(dt) ...
SELECT DISTINCT ',' + QUOTENAM...
ORDER BY DATEPART(yyyy, dt), DATEPART(ww, dt)
I needed to change the DATEADD portion of the query and remove the DISTINCT. Once changed the order sorted properly on it's own
DECLARE #startdate AS VARCHAR(MAX);
DECLARE #enddate AS VARCHAR(MAX);
SET #startdate = CAST((DATEPART(yyyy, GetDate())-1) AS VARCHAR(4))+'-12-01';
SET #enddate = CAST((DATEPART(yyyy, GetDate())) AS VARCHAR(4))+'-03-15';
WITH DateRange(dt) AS
(
SELECT CONVERT(datetime, #startdate) dt
UNION ALL
SELECT DATEADD(ww,1,dt) dt FROM DateRange WHERE dt < CONVERT(datetime, #enddate)
)
SELECT ',' + QUOTENAME((CAST(DATEPART(yyyy, dt) AS VARCHAR(4)))+'-Week'+(CAST(DATEPART(ww, dt) AS VARCHAR(2)))) FROM DateRange

Pivot In Values with dates?

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