Return next 10 days including weekday name - sql-server

Looking for a query or function which, for a given date, will return next 10 days (including the given date as a parameter) as two columns:
- date
- weekday name
I am using SQL Server 2019.
UPDATE (#Dale K):
I have calculated dates and weekdays, but those are as two separate queries and in new row instead of multiple rows
declare #mydate datetime
set #mydate = '2020-1-1'
select #mydate + 0, #mydate + 1, #mydate + 2, #mydate + 3, #mydate + 4, #mydate + 5, #mydate + 6
select
datename(dw, #mydate + 0),
datename(dw, #mydate + 1),
datename(dw, #mydate + 2),
datename(dw, #mydate + 3),
datename(dw, #mydate + 4),
datename(dw, #mydate + 5),
datename(dw, #mydate + 6)

If you always need 10 days, you can use a function such as this:
CREATE FUNCTION dbo.GetNext10Days(#FromDate DATE)
RETURNS TABLE AS RETURN
SELECT DATEADD(DAY,n,#FromDate) AS TheDate,
DATENAME(WEEKDAY,DATEADD(DAY,n,#FromDate)) AS WeekDayName
FROM (VALUES (0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) v(n)
GO
SELECT * FROM dbo.GetNext10Days(GETDATE())
If you might need a different number of days, you can create a function that returns a table of numbers and call that function instead of the VALUES clause (to generate the numbers, I prefer the method described in this answer: https://stackoverflow.com/a/1394239/1187211).

you can use dateadd and datename(weekday,date) function to do it.
create function dbo.getnext10days(#fromdate date)
returns table as return
with cte as (
select 1 val,dateadd(day,1,#fromdate) [date]
union all
select t2.val +1 as val,dateadd(day,t2.val+1,#fromdate) from (select 1 val) t1
inner join cte t2 on t2.val < 10
)
select
[date],datename(weekday,[date]) as [weekday name]
from cte;
online demo link

You can also use the inbuilt spt_values to generate the dates as shown below.
SELECT
DATEADD(DAY,number+1, getdate()) [Date],
datename(weekday, DATEADD(DAY,number+1, getdate())) [Week Day]
FROM master..spt_values
WHERE type = 'P'
AND DATEADD(DAY,number+1, getdate()) < DATEADD(day, 10, getdate())
Here is the db<>fiddle demo.

Related

Get working days,hours,minutes between two dates in sql server

Input
startdate - 2020-05-01 10:05:07.000
enddate - 2020-05-18 12:08:07.000
Expected Output
12D:02H:03M
I need output like aobve which is having hours minutes with days
Iam having query from which iam getting only working days ,below is the query
SELECT (DATEDIFF(dd, receive_date, GETDATE()) + 1)
-(DATEDIFF(wk, receive_date, GETDATE()) * 2)
-(CASE WHEN DATENAME(dw, receive_date) = 'Sunday' THEN 1 ELSE 0 END)
-(CASE WHEN DATENAME(dw, GETDATE()) = 'Saturday' THEN 1 ELSE 0 END) from email
other query i have like this
select dbo.fn_WorkDays(receive_date, getdate() ) from email
You could break it down to total seconds and then pull out the parts you need.
EDIT
Ok, I see now that you need to use only weekdays. I am certain there is a more eloquent way to do this, but this should give you a start. I would imagine using a calendar table might make it easier.
I made it so that if one of the start or end dates started on a weekend the calculation should still be correct.
Consider the following:
DECLARE #startdate as datetime = '2020-05-01 10:05:07.000'
DECLARE #enddate as datetime = '2020-05-18 12:08:07.000'
;WITH CTE AS
(
SELECT #startDate stDate, DATEADD(second, -1, DATEADD(day, DATEDIFF(day, 0, #startdate)+1, 0)) EndDate
UNION ALL
SELECT
CASE WHEN DATEADD(day,1,stDate) < DATEADD(dd, 0, DATEDIFF(dd, 0, #enddate))
THEN DATEADD(dd, 0, DATEDIFF(dd, 0, DATEADD(day,1,stDate) ))
ELSE #enddate
END,
CASE WHEN DATEADD(day,1,stDate) < DATEADD(dd, 0, DATEDIFF(dd, 0, #enddate))
THEN DATEADD(second, -1, DATEADD(day, DATEDIFF(day, 0, DATEADD(day,1,stDate))+1, 0))
ELSE #enddate
END EndDate
FROM CTE
WHERE DATEADD(day,1,stDate) <= #enddate
)
,
ctSeconds as
(
SELECT COUNT(*) DayCount,MIN(stDate) stDate, MAX(EndDate) EndDate
from CTE
WHERE DATENAME(dw,stDate) NOT IN ('SATURDAY', 'SUNDAY')
)
SELECT
FORMAT(DayCount, '00') + 'D:'
+ FORMAT ( (TotalSeconds % 86400) / 3600,'00') + 'H:'
+ FORMAT ( (TotalSeconds % 3600) / 60, '00') + 'M' AS SPAN
FROM
(
SELECT DATEDIFF(SECOND,stDate, EndDate) TotalSeconds, DayCount from ctSeconds
) x
RESULT
SPAN
12D:02H:03M
Check below query, It will help you
declare #startDate as datetime = '2020-05-02 15:05:06.000'
,#endDate as datetime = '2020-05-18 12:08:07.000'
-- Minutes calculation START
declare #startRemainingMin int=datediff(SECOND,cast('1900-01-01' as time),cast(#startDate as time))
,#endRemainingMin int=datediff(SECOND,cast('1900-01-01' as time),cast(#endDate as time))
,#diffMin int
set #diffMin = #endRemainingMin-#startRemainingmin
-- Uncount if #endDate is sunday or saturday
if(datename(DW,#endDate)='Sunday' or datename(DW,#endDate)='Saturday')
begin
select #diffMin=-1*#startRemainingmin
end
-- Uncount if #startDate is sunday or saturday
if(datename(DW,#startDate)='Sunday' or datename(DW,#startDate)='Saturday')
begin
select #diffMin=#endRemainingMin
end
--Minutes calculation END
if(#diffMin<0)
begin
select #diffMin=86400+#diffMin
end
;with cteAllDate as(
select #startDate as dt
union all
select
dt+1
from cteAllDate where dateadd(day,1,dt)<=#endDate
)
select cast(count(1)-sum(iif((datename(DW,dt)='Sunday' or datename(DW,dt)='Saturday'),1,0 )) as varchar(5)) + 'D:'
+ format ((#diffMin/3600),'00') + 'H:'
+ format ( (#diffMin%3600/60), '00') + 'M:'
+ format ( (#diffMin%60), '00') + 'S' as result
from cteAllDate
Output:
result
10D:12H:08M:07S

Get dates between 2 dates with equal intervals

I have 2 dates 01/04/2017 and 30/04/2017. I want all the dates between these 2 dates with 7 days interval.
Expected Output :
01/04/2017
08/04/2017
15/04/2017
22/04/2017
29/04/2017
Please help!!
DECLARE #StartDate DATETIME,
#EndDate DATETIME
SELECT #StartDate = '2017-04-01',
#EndDate = '2017-04-30'
SELECT DATEADD(DAY, number*7, #StartDate)
FROM master.dbo.spt_values
WHERE type='P'
AND #EndDate >= DATEADD(DAY, number*7, #StartDate)
One method would be to use a Calendar table. Then return the results from there using the modulus to get the 7th rows:
WITH Dates AS(
SELECT *,
ROW_NUMBER() OVER (ORDER BY [date]) AS RN
FROM DateTable
WHERE [Date] BETWEEN '20170401' AND '20170430')
SELECT *
FROM Dates
WHERE (RN - 1) % 7 = 0;
I've used this solution, as from your post you imply that you might supply any date range, and that the 1st day may not necessarily be a Monday (or other specific day).
Try this
DECLARE #STRT DATETIME='04/01/2017',#END DATETIME ='04/30/2017'
;WITH CTE
AS
(
SELECT
MyDate = CAST(#STRT AS DATETIME)
UNION ALL
SELECT
MyDate = CAST(MyDate AS DATETIME)+7
FROM CTE
WHERE CAST(MyDate AS DATETIME)+7 < CAST(#END AS DATETIME)
)
SELECT
*
FROM CTE
result
Declare #StartDate DATE=CONVERT(DATE,'01/04/2017',104),#EndDate DATE=CONVERT(DATE,'01/12/2017',104)
Declare #String NVARCHAR(MAX)=''
WHILE (#StartDate<=#EndDate AND DATEDIFF(wk,#StartDate,#EndDate)>=0)
BEGIN
SET #String=#String+CONVERT(NVARCHAR(100),#StartDate)+CHAR(10)+CHAR(13)
SET #StartDate=DATEADD(d,7,#StartDate)
END
PRINT #String
GO

Passing in Week Day name to get nearest date in SQL

I'm working on a query that deals with a frequency value (i.e. Mondays, Tuesdays, etc. - Think assignments).
So in my query I currently have a result of
jobId:1, personId:100, frequencyVal: 'Mondays'
jobId:2, personId:101, frequencyVal: 'Saturdays'
What I need is the next the 4 future(or current) dates for the frequencyVal.
So if today is 1/3/2015
I would need my result set to be
jobId:1, personId:100, frequencyVal: 'Mondays', futureDates: '1/5,1/12,1/19,1/26'
jobId:2, personId:102, frequencyVal: 'Saturdays', futureDates: '1/3,1/10,1/17,1/24'
I was looking at the following post:
How to find the Nearest (day of the week) for a given date
But that sets it for a specific date. And I'm looking at this being a web application and I want the dates for the current date. So if I try to run this query next Tuesday the future dates for jobId:1 would remove the 1/5 and add the 2/2.
Is there a way to pass in a weekday value to get the next nearest date?
I prefer a calendar table for this kind of query. Actually, I prefer a calendar table over date functions for most queries. Here's a minimal one. The one I use in production has more columns and more rows. (100 years of data is only 37k rows.)
create table calendar (
cal_date date not null primary key,
day_of_week varchar(15)
);
insert into calendar (cal_date) values
('2015-01-01'), ('2015-01-02'), ('2015-01-03'), ('2015-01-04'),
('2015-01-05'), ('2015-01-06'), ('2015-01-07'), ('2015-01-08'),
('2015-01-09'), ('2015-01-10'), ('2015-01-11'), ('2015-01-12'),
('2015-01-13'), ('2015-01-14'), ('2015-01-15'), ('2015-01-16'),
('2015-01-17'), ('2015-01-18'), ('2015-01-19'), ('2015-01-20'),
('2015-01-21'), ('2015-01-22'), ('2015-01-23'), ('2015-01-24'),
('2015-01-25'), ('2015-01-26'), ('2015-01-27'), ('2015-01-28'),
('2015-01-29'), ('2015-01-30'), ('2015-01-31'),
('2015-02-01'), ('2015-02-02'), ('2015-02-03'), ('2015-02-04'),
('2015-02-05'), ('2015-02-06'), ('2015-02-07'), ('2015-02-08'),
('2015-02-09'), ('2015-02-10'), ('2015-02-11'), ('2015-02-12'),
('2015-02-13'), ('2015-02-14'), ('2015-02-15'), ('2015-02-16'),
('2015-02-17'), ('2015-02-18'), ('2015-02-19'), ('2015-02-20'),
('2015-02-21'), ('2015-02-22'), ('2015-02-23'), ('2015-02-24'),
('2015-02-25'), ('2015-02-26'), ('2015-02-27'), ('2015-02-28')
;
update calendar
set day_of_week = datename(weekday, cal_date);
alter table calendar
alter column day_of_week varchar(15) not null;
alter table calendar
add constraint cal_date_matches_dow
check (datename(weekday, cal_date) = day_of_week);
create index day_of_week_ix on calendar (day_of_week);
Set the privileges so that
everyone can select, but
almost nobody can insert new rows, and
even fewer people can delete rows.
(Or write a constraint that can guarantee there are no gaps. I think you can do that in SQL Server.)
You can select the next four Mondays after today with a very simple SQL statement. (The current date is 2015-01-05, which is a Monday.)
select top 4 cal_date
from calendar
where cal_date > convert(date, getdate())
and day_of_week = 'Monday'
order by cal_date;
CAL_DATE
--
2015-01-12
2015-01-19
2015-01-26
2015-02-02
For me, this is a huge advantage. No procedural code. Simple SQL that is obviously right. Big win.
Your sample table
create table #t
(
jobId int,
personId int,
frequencyVal varchar(10)
);
insert into #t values (1,100,'Mondays'),(2,101,'Saturdays');
QUERY 1 : Select nearest 4 week of days in current month for particular week day
-- Gets first day of month
DECLARE #FIRSTDAY DATE=DATEADD(month, DATEDIFF(month, 0, GETDATE()), 0)
;WITH CTE as
(
-- Will find all dates in current month
SELECT CAST(#FIRSTDAY AS DATE) as DATES
UNION ALL
SELECT DATEADD(DAY,1,DATES)
FROM CTE
WHERE DATES < DATEADD(MONTH,1,#FIRSTDAY)
)
,CTE2 AS
(
-- Join the #t table with CTE on the datename+'s'
SELECT jobId,personId,frequencyVal,DATES,
-- Get week difference for each weekday
DATEDIFF(WEEK,DATES,GETDATE()) WEEKDIFF,
-- Count the number of weekdays in a month
COUNT(DATES) OVER(PARTITION BY DATENAME(WEEKDAY,CTE.DATES)) WEEKCOUNT
FROM CTE
JOIN #t ON DATENAME(WEEKDAY,CTE.DATES)+'s' = #t.frequencyVal
WHERE MONTH(DATES)= MONTH(GETDATE())
)
-- Converts to CSV and make sure that only nearest 4 week of days are generated for month
SELECT DISTINCT C2.jobId,C2.personId,frequencyVal,
SUBSTRING(
(SELECT ', ' + CAST(DATEPART(MONTH,DATES) AS VARCHAR(2)) + '/' +
CAST(DATEPART(DAY,DATES) AS VARCHAR(2))
FROM CTE2
WHERE C2.jobId=jobId AND C2.personId=personId AND C2.frequencyVal=frequencyVal AND
((WEEKDIFF<3 AND WEEKDIFF>-3 AND WEEKCOUNT = 5) OR WEEKCOUNT <= 4)
ORDER BY CTE2.DATES
FOR XML PATH('')),2,200000) futureDates
FROM CTE2 C2
SQL FIDDLE
For example, in Query2 the nearest date(here we take example as Saturday) of
2015-Jan-10 will be 01/03,01/10,01/17,01/24
2015-Jan-24 will be 01/10,01/17,01/24,01/31
QUERY 2 : Select next 4 week's dates for particular week day irrelevant of month
;WITH CTE as
(
-- Will find the next 4 week details
SELECT CAST(GETDATE() AS DATE) as DATES
UNION ALL
SELECT DATEADD(DAY,1,DATES)
FROM CTE
WHERE DATES < DATEADD(DAY,28,GETDATE())
)
,CTE2 AS
(
-- Join the #t table with CTE on the datename+'s'
SELECT jobId,personId,frequencyVal, DATES,
ROW_NUMBER() OVER(PARTITION BY DATENAME(WEEKDAY,CTE.DATES) ORDER BY CTE.DATES) DATECNT
FROM CTE
JOIN #t ON DATENAME(WEEKDAY,CTE.DATES)+'s' = #t.frequencyVal
)
-- Converts to CSV and make sure that only 4 days are generated for month
SELECT DISTINCT C2.jobId,C2.personId,frequencyVal,
SUBSTRING(
(SELECT ', ' + CAST(DATEPART(MONTH,DATES) AS VARCHAR(2)) + '/' +
CAST(DATEPART(DAY,DATES) AS VARCHAR(2))
FROM CTE2
WHERE C2.jobId=jobId AND C2.personId=personId AND C2.frequencyVal=frequencyVal
AND DATECNT < 5
ORDER BY CTE2.DATES
FOR XML PATH('')),2,200000) futureDates
FROM CTE2 C2
SQL FIDDLE
The following would be the output if the GETDATE() (if its Saturday) is
2015-01-05 - 1/10, 1/17, 1/24, 1/31
2015-01-24 - 1/24, 1/31, 2/7, 2/14
There's no built-in function to do it. But you can try this, you may place it inside a Scalar-Valued Function:
DECLARE #WeekDay VARCHAR(10) = 'Monday';
DECLARE #WeekDayInt INT;
SELECT #WeekDayInt = CASE #WeekDay
WHEN 'SUNDAY' THEN 1
WHEN 'MONDAY' THEN 2
WHEN 'TUESDAY' THEN 3
WHEN 'WEDNESDAY' THEN 4
WHEN 'THURSDAY' THEN 5
WHEN 'FRIDAY' THEN 6
WHEN 'SATURDAY' THEN 7 END
SELECT CONVERT(DATE, DATEADD(DAY, (DATEPART(WEEKDAY, GETDATE()) + #WeekDayInt) % 7, GETDATE())) AS NearestDate
UPDATE:
Looks like radar was right, here's the solution:
DECLARE #WeekDay VARCHAR(10) = 'Monday';
DECLARE #WeekDayInt INT;
DECLARE #Date DATETIME = GETDATE();
SELECT #WeekDayInt = CASE #WeekDay
WHEN 'SUNDAY' THEN 1
WHEN 'MONDAY' THEN 2
WHEN 'TUESDAY' THEN 3
WHEN 'WEDNESDAY' THEN 4
WHEN 'THURSDAY' THEN 5
WHEN 'FRIDAY' THEN 6
WHEN 'SATURDAY' THEN 7 END
DECLARE #Diff INT = DATEPART(WEEKDAY, #Date) - #WeekDayInt;
SELECT CONVERT(DATE, DATEADD(DAY, CASE WHEN #Diff >= 0 THEN 7 - #Diff ELSE ABS(#Diff) END, #Date)) AS NearestDate
Try this - based on king.code's answer to get the nearest date.
create table #t
(
jobId int,
personId int,
frequencyVal varchar(10)
);
insert into #t values (1,100,'Mondays'),(2,101,'Saturdays');
WITH cte(n) AS
(
SELECT 0
UNION ALL
SELECT n+1 FROM cte WHERE n < 3
)
select #t.jobId, #t.personId, #t.frequencyVal, STUFF(a.d, 1, 1, '') AS FutureDates
from #t
cross apply (SELECT CASE #t.frequencyVal
WHEN 'SUNDAYS' THEN 1
WHEN 'MONDAYS' THEN 2
WHEN 'TUESDAYS' THEN 3
WHEN 'WEDNESDAYS' THEN 4
WHEN 'THURSDAYS' THEN 5
WHEN 'FRIDAYS' THEN 6
WHEN 'SATURDAYS' THEN 7
END)tranlationWeekdays(n)
cross apply (select ',' + CONVERT(varchar(10), CONVERT(date,dateadd(WEEK, cte.n,CONVERT(DATE, DATEADD(DAY, (DATEPART(WEEKDAY, GETDATE()) + tranlationWeekdays.n) % 7, GETDATE()))))) from cte FOR XML PATH('')) a(d);
drop table #t;
Try this,
DECLARE #YEAR INT=2015
DECLARE #MONTH INT=1
DECLARE #DAY INT=1
DECLARE #DATE DATE = (SELECT DateFromParts(#Year, #Month, #Day))
DECLARE #TOTAL_DAYS INT =(SELECT DatePart(DY, #DATE));
WITH CTE1
AS (SELECT T_DAY=(SELECT DateName(DW, #DATE)),
#DATE AS T_DATE,
#DAY AS T_DDAY
UNION ALL
SELECT T_DAY=(SELECT DateName(DW, DateAdd(DAY, T_DDAY + 1, #DATE))),
DateAdd(DAY, T_DDAY + 1, #DATE) AS T_DATE,
T_DDAY + 1
FROM CTE1
WHERE T_DDAY + 1 <= 364)
SELECT DISTINCT T_DAY,
Stuff((SELECT ',' + CONVERT(VARCHAR(30), T_DATE)
FROM CTE1 A
WHERE A.T_DAY=CTE1.T_DAY AND A.T_DATE > GetDate() AND A.T_DATE<(DATEADD(WEEK,4,GETDATE()))
FOR XML PATH('')), 1, 1, '') AS FUTURE
FROM CTE1
ORDER BY T_DAY
OPTION (MAXRECURSION 365)
This is a simpler way I think, and I think it fits your requirements.
Note that I have changed your frequency_val column to an integer that represents the day of the week from SQL servers perspective and added a calculated column to illustrate how you can easily derive the day name from that.
declare #t table
(
jobId int,
personId int,
--frequencyVal varchar(10)
frequency_val int,
frequency_day as datename(weekday,frequency_val -1) + 's'
);
declare #num_occurances int = 4
declare #from_date date = dateadd(dd,3,getdate()) -- this will allow you to play with the date simply by changing the increment value
insert into #t
values
(1,100,1),--'Mondays'),
(2,101,6),--'Saturdays');
(3,101,7),--'Saturdays');
(4,100,2)--'Mondays'),
--select * from #t
;with r_cte (days_ahead, occurance_date)
as (select 0, convert(date,#from_date,121)
union all
select r_cte.days_ahead +1, convert(date,dateadd(DD, r_cte.days_ahead+1, #from_date),121)
from r_cte
where r_cte.days_ahead < 7 * #num_occurances
)
select t.*, r_cte.occurance_date
from
#t t
inner join r_cte
on DATEPART(WEEKDAY, dateadd(dd,##DATEFIRST - 1 ,r_cte.occurance_date)) = t.frequency_val
Having seen the use of DATENAME in some of the answers already given, I'd like to point out that return values of DATENAME might vary depending on your current language setting, but you can save the current language setting and ensure usage of us_english so you can be confident to use English weekday names.
Now here is my slightly different approach to get the 4 next dates that fall on a certain (known) weekday, using a user defined table valued function that allows to create a number sequence table (yes this is a pretty dull function, you have to pass MaxValue greater MinValue, but that could be easily enhanced, if needed, but hey, it does the job). Using that function span a table over 28 values (next 28 days should indeed include the next 4 relevant weekdays ;)), apply DATEADD on GETDATE and reduce the result set with WHERE to only those values that have the right weekday:
CREATE FUNCTION GetIntSequence(#MinValue INT, #MaxValue INT)
RETURNS #retSequence TABLE
(
IntValue INT NOT NULL
)
BEGIN
DECLARE #i INT = (SELECT #MinValue)
WHILE #i <= #MaxValue
BEGIN
INSERT INTO #retSequence (IntValue) SELECT #i
SELECT #i = #i + 1
END
RETURN
END
GO
DECLARE #weekDay NVARCHAR(MAX) = 'Monday' --(or Tuesday, wednesday, ...)
--save current language setting
DECLARE #languageBackup NVARCHAR(MAX) = (SELECT ##LANGUAGE)
--ensure us english language setting for reliable weekday names
SET LANGUAGE us_english;
SELECT FourWeeks.SomeDay FROM
(
SELECT
DATEADD(DAY, IntValue, GETDATE()) AS SomeDay
FROM dbo.GetIntSequence(1, 28)
) AS FourWeeks
WHERE DATENAME(WEEKDAY, SomeDay) = #weekDay
--restore old language setting
SET LANGUAGE #languageBackup;
GO
DROP FUNCTION dbo.GetIntSequence

SQL - Convert results into comma separated string

Using another article on StackOverflow (How to split date ranges based on months in SQL Server 2005) I have run a SELECT statement which splits up a date range by month and returns 2 columns (DateFrom, DateTo) in SQL.
DECLARE #SDate DATE = '2012/08/01'
DECLARE #EDate DATE = '2013/09/01'
SELECT
DATEADD(MONTH, n.Number, #SDate) as DateFrom,
DATEADD(day, -1, DATEADD(MONTH, n.Number, DATEADD(YEAR,-1,#EDate))) as DateTo
FROM
master.dbo.spt_values n
WHERE
n.Number < DATEDIFF(MONTH, #SDate, #EDate)
AND n.Type = 'P'
I now need to add each of the returned rows into a string separating the columns by | (Dateto|Datefrom) and each row by ,.
For example if your run the code above the result would be (just for this example im only using the first 4 rows but I need all of them in one string):
R | Date From | Date To
1 | 2012-08-01 | 2012-08-31
2 | 2012-09-01 | 2012-09-30
3 | 2012-10-01 | 2012-10-30
4 | 2012-11-01 | 2012-11-30
Code:
DECLARE #stralldates VarChar(MAX)
/* SET #stralldates = INSERTCODE */
PRINT #stralldates
What I need PRINT to return:
2012-08-01|2012-08-31,2012-10-01|2012-10-30,2012-10-01|2012-10-30,2012-11-01|2012-11-30
I have tried several suggestions from other similar questions on StackOverflow (such as CONCAT) with no success.
Any help or suggestions would be appreciated.
set #StrAllDates =
stuff((select ','+convert(char(10), SDate, 121)+'|'+convert(char(10), dateadd(day, -1, dateadd(month, 1, SDate)), 121)
from (
select dateadd(month, n.number, #SDate) as SDate
from master..spt_values as n
where n.number < datediff(month, #SDate, #EDate) and
n.type = 'P'
) as T
order by SDate
for xml path('')), 1, 1, '')
I only know Oracle which provides wm_concat (undocumented) or listagg depending on version.
You can use the below script to get the data in the format you want to. It basically uses the coalesce method for concatenanation. I created a temp table to hold the data which is then used to iterate over the rows.
DECLARE #SDate DATE = '2012/08/01'
DECLARE #EDate DATE = '2013/09/01'
SELECT
DATEADD(MONTH, n.Number, #SDate) as DateFrom,
DATEADD(day, -1, DATEADD(MONTH, n.Number, DATEADD(YEAR,-1,#EDate))) as DateTo
INTO #tmp_data
FROM master.dbo.spt_values n
WHERE
n.Number < DATEDIFF(MONTH, #SDate, #EDate)
AND n.Type = 'P'
declare #my_string varchar(8000);
select #my_string =
coalesce( #my_string + ',', '')
+ convert(varchar(10), d.DateFrom, 126)
+ '|' + convert(varchar(10), d.DateTo, 126)
from #tmp_data d
order by d.DateFrom
select #my_string
drop table #tmp_data
Also, if you want to use the row rumber, you can use use it by adding another column to the select clause - ROW_NUMBER() OVER(ORDER BY n.Number) as RowNum
The following query will give you the required comma seperated string:
DECLARE #Begin DATETIME
DECLARE #End DATETIME
Declare #test Table(startDate datetime, endDate datetime)
DECLARE #listStr VARCHAR(MAX)
SELECT #Begin = '20110101', #End = '20120101'
Insert into #test
SELECT DATEADD(MONTH, n.Number, #Begin) DateFrom, DATEADD(day, -1, DATEADD(MONTH, n.Number+1, #Begin)) DateTo
FROM master.dbo.spt_values n
WHERE
n.Number <= DATEDIFF(MONTH, #begin, #end)
AND n.Type = 'P'
select #listStr = COALESCE(#listStr+',' ,'')+ CONVERT(VARCHAR(10), startDate, 120) + '|' + CONVERT(varchar(10),endDate,120) from #test
select #listStr

Get dates from a week number in T-SQL

In Microsoft SQL Server, I have a week number
(from DATEPART(wk, datecol))
But what I would like to do is turn this back into the date span for that week.
For example,
SELECT DATEPART(wk, GETDATE())
yields 10. I would like to derive 3/1/2009 and 3/7/2009 from this number.
Is this possible?
Quassnoi's answer works, but kind of leaves you on the hook for cleaning up the dates if they are dates in the middle of the day (his start of week leaves you one day earlier than you need to be if you use a time in the middle of the day -- you can test using GETDATE()).
I've used something like this in the past:
SELECT
CONVERT(varchar(50), (DATEADD(dd, ##DATEFIRST - DATEPART(dw, DATECOL), DATECOL)), 101),
CONVERT(varchar(50), (DATEADD(dd, ##DATEFIRST - DATEPART(dw, DATECOL) - 6, DATECOL)), 101)
A side benefit of this is that by using ##DATEFIRST you can handle nonstandard week starting days (the default is Sunday, but with SET ##DATEFIRST you can change this).
It seems crazy that simple date manipulation in SQL Server has to be this arcane, but there you go...
You can set #WeekNum and #YearNum to whatever you want - in this example they are derived from the #datecol variable, which is set to GETDATE() for purposes of illustration. Once you have those values- you can calculate the date range for a week by using the following:
DECLARE #datecol datetime = GETDATE();
DECLARE #WeekNum INT
, #YearNum char(4);
SELECT #WeekNum = DATEPART(WK, #datecol)
, #YearNum = CAST(DATEPART(YY, #datecol) AS CHAR(4));
-- once you have the #WeekNum and #YearNum set, the following calculates the date range.
SELECT DATEADD(wk, DATEDIFF(wk, 6, '1/1/' + #YearNum) + (#WeekNum-1), 6) AS StartOfWeek;
SELECT DATEADD(wk, DATEDIFF(wk, 5, '1/1/' + #YearNum) + (#WeekNum-1), 5) AS EndOfWeek;
To answer your question:
--CHANGE A WEEK NUMBER BACK INTO A DATE FOR THE FIRST DATE OF THE WEEK
DECLARE #TaskWeek INT = 17
DECLARE #TaskYear INT = 2013
SELECT DATEADD(WEEK, #TaskWeek - 1,DATEADD(dd, 1 - DATEPART(dw, '1/1/' + CONVERT(VARCHAR(4),#TaskYear)), '1/1/' + CONVERT(VARCHAR(4),#TaskYear)))
If your week starts from Monday (on SQL Server 2008)
select datecol,
DATEPART(ISOWK, datecol) as week,
((DATEPART(dw, datecol)+5)%7)+1 as weekday,
(DATEADD(dd, -((DATEPART(dw, datecol)+5)%7), datecol)) as Monday,
(DATEADD(dd, -((DATEPART(dw, datecol)+5)%7)+6, datecol)) as Sunday
SELECT DATECOL - DATEPART(weekday, DATECOL), DATECOL - DATEPART(weekday, DATECOL) + 7
How about a function that jumps to the week before that week number and then steps through the next few days until the week number changes (max 7 steps), returning the new date?
CREATE FUNCTION dbo.fnGetDateFromWeekNo
(#weekNo int , #yearNo int)
RETURNS smalldatetime
AS
BEGIN
DECLARE #tmpDate smalldatetime
set #tmpdate= cast(cast (#yearNo as varchar) + '-01-01' as smalldatetime)
-- jump forward x-1 weeks to save counting through the whole year
set #tmpdate=dateadd(wk,#weekno-1,#tmpdate)
-- make sure weekno is not out of range
if #WeekNo <= datepart(wk,cast(cast (#yearNo as varchar) + '-12-31' as smalldatetime))
BEGIN
WHILE (datepart(wk,#tmpdate)<#WeekNo)
BEGIN
set #tmpdate=dateadd(dd,1,#tmpdate)
END
END
ELSE
BEGIN
-- invalid weeknumber given
set #tmpdate=null
END
RETURN #tmpDate
END
I've taken elindeblom's solution and modified it - the use of strings (even if cast to dates) makes me nervous for the different formats of dates used around the world. This avoids that issue.
While not requested, I've also included time so the week ends 1 second before midnight:
DECLARE #WeekNum INT = 12,
#YearNum INT = 2014 ;
SELECT DATEADD(wk,
DATEDIFF(wk, 6,
CAST(RTRIM(#YearNum * 10000 + 1 * 100 + 1) AS DATETIME))
+ ( #WeekNum - 1 ), 6) AS [start_of_week],
DATEADD(second, -1,
DATEADD(day,
DATEDIFF(day, 0,
DATEADD(wk,
DATEDIFF(wk, 5,
CAST(RTRIM(#YearNum * 10000
+ 1 * 100 + 1) AS DATETIME))
+ ( #WeekNum + -1 ), 5)) + 1, 0)) AS [end_of_week] ;
Yes, I know I'm still casting but from a number. It "feels" safer to me.
This results in:
start_of_week end_of_week
----------------------- -----------------------
2014-03-16 00:00:00.000 2014-03-22 23:59:59.000
Another way to do it:
declare #week_number int;
declare #start_weekday int = 0 -- Monday
declare #end_weekday int = 6 -- next Sunday
select #week_number = datediff(week, 0, getdate())
select
dateadd(week, #week_number, #start_weekday) as WEEK_FIRST_DAY,
dateadd(week, #week_number, #end_weekday) as WEEK_LAST_DAY
Explanation:
#week_number is computed based on the initial calendar date '1900-01-01'. Replace getdate() by whatever date you want.
#start_weekday is 0 if Monday. If Sunday, then declare it as -1
#end_weekday is 6 if next Sunday. If Saturday, then declare it as 5
Then dateadd function, will add the given number of weeks and the given number of days to the initial calendar date '1900-01-01'.
dateadd(
dd,
datepart(wk, #Date)*7,
convert(smalldatetime, convert(char,year(max(#Date)))+convert(char, '-01-01'))
)-1
Here you just have to pass year and week number.
DECLARE #Year VARCHAR(4)
SET #Year= '2012'
DECLARE #FirstDate DATETIME
SET #FirstDate = (SELECT DATEADD(dd,1,(SELECT DATEADD(wk,DATEPART(wk,GETDATE())-1,Convert(DAteTime,'01-01-' + #Year))))
)
DECLARE #LastDate DATETIME
SET #LastDate =(SELECT DATEADD(dd,4,#FirstDate))
SELECT #FirstDate
,#LastDate
This should work regardless of ##DATEFIRST
ALTER FUNCTION dbo.DEV_VW_WeekSerial
(#YearNum int,
#WeekNum int,
#DayNum int)
RETURNS Date AS
BEGIN
DECLARE #FirstDayYear As Date;
SET #FirstDayYear='01/01/' + CAST(#YearNum As varchar)
RETURN dateadd(d,(#DayNum-datepart(weekday,#FirstDayYear)),dateadd(week, #WeekNum-1,#FirstDayYear))
END
Give it #Year and #Week,
return first date of that week.
Declare #Year int
,#Week int
,#YearText varchar(4)
set #Year = 2009
set #Week = 10
set #YearText = #Year
print dateadd(day
,1 - datepart(dw, #YearText + '-01-01')
+ (#Week-1) * 7
,#YearText + '-01-01')
I just incorporated the SELECT with a CASE statement (For my situation Monday marked the first day of the week, and didn't want to deal with the SET DATEFIRST command:
CASE DATEPART(dw,<YourDateTimeField>)
WHEN 1 THEN CONVERT(char(10), DATEADD(DD, -6, <YourDateTimeField>),126) + ' to ' + CONVERT(char(10), <YourDateTimeField>,126)
WHEN 2 THEN CONVERT(char(10), <YourDateTimeField>,126) + ' to ' + CONVERT(char(10), DATEADD(DD, 6, <YourDateTimeField>),126)
WHEN 3 THEN CONVERT(char(10), DATEADD(DD, -1, <YourDateTimeField>),126) + ' to ' + CONVERT(char(10), DATEADD(DD, 5, <YourDateTimeField>),126)
WHEN 4 THEN CONVERT(char(10), DATEADD(DD, -2, <YourDateTimeField>),126) + ' to ' + CONVERT(char(10), DATEADD(DD, 4, <YourDateTimeField>),126)
WHEN 5 THEN CONVERT(char(10), DATEADD(DD, -3, <YourDateTimeField>),126) + ' to ' + CONVERT(char(10), DATEADD(DD, 3, <YourDateTimeField>),126)
WHEN 6 THEN CONVERT(char(10), DATEADD(DD, -4, <YourDateTimeField>),126) + ' to ' + CONVERT(char(10), DATEADD(DD, 2, <YourDateTimeField>),126)
WHEN 7 THEN CONVERT(char(10), DATEADD(DD, -5, <YourDateTimeField>),126) + ' to ' + CONVERT(char(10), DATEADD(DD, 1, <YourDateTimeField>),126)
ELSE 'UNK'
END AS Week_Range
The most votes answer works fine except the 1st week and last week of year. When datecol value is '2009-01-01', the result will be 01/03/2009 and 12/28/2008.
My solution:
DECLARE #Date date = '2009-03-01', #WeekNum int, #StartDate date;
SELECT #WeekNum = DATEPART(WEEK, #Date);
SELECT #StartDate = DATEADD(DAY, -(DATEPART(WEEKDAY, DATEADD(YEAR, DATEDIFF(YEAR, 0, #Date), 0)) + 6), DATEADD(YEAR, DATEDIFF(YEAR, 0, #Date), 0));
SELECT CONVERT(nvarchar, CASE WHEN #WeekNum = 1 THEN CAST(DATEADD(YEAR, DATEDIFF(YEAR, 0, #Date), 0) AS date) ELSE DATEADD(DAY, 7 * #WeekNum, #StartDate) END, 101) AS StartOfWeek
,CONVERT(nvarchar, CASE WHEN #WeekNum = DATEPART(WEEK, DATEADD(DAY, -1, DATEADD(YEAR, DATEDIFF(YEAR, 0, #Date) + 1, 0))) THEN DATEADD(DAY, -1, DATEADD(YEAR, DATEDIFF(YEAR, 0, #Date) + 1, 0)) ELSE DATEADD(DAY, 7 * #WeekNum + 6, #StartDate) END, 101) AS EndOfWeek;
This will display 01/01/2009 and 01/03/2009 for the 1st week, and display 03/01/2009 and 03/07/2009 for the 10th week.
I think this would be what you want exactly. You can replace the variables with their expressions as you wish.
declare #IntWeek as varchar(20)
SET #IntWeek = '201820'
SELECT
DATEADD(wk, DATEDIFF(wk, ##DATEFIRST, LEFT(#IntWeek,4) + '-01-01') +
(cast(RIGHT(#IntWeek, 2) as int) -1), ##DATEFIRST) AS StartOfWeek
SELECT DATEADD(week, #weekNumber - 1, DATEADD(DAY, ##datefirst - DATEPART(weekday, CAST(YEAR(GETDATE()) AS VARCHAR) + '-01-01') - 6, CAST(YEAR(GETDATE()) AS VARCHAR) + '-01-01'))
DECLARE #dayval int,
#monthval int,
#yearval int
SET #dayval = 1
SET #monthval = 1
SET #yearval = 2011
DECLARE #dtDateSerial datetime
SET #dtDateSerial = DATEADD(day, #dayval-1,
DATEADD(month, #monthval-1,
DATEADD(year, #yearval-1900, 0)
)
)
DECLARE #weekno int
SET #weekno = 53
DECLARE #weekstart datetime
SET #weekstart = dateadd(day, 7 * (#weekno -1) - datepart (dw, #dtDateSerial), #dtDateSerial)
DECLARE #weekend datetime
SET #weekend = dateadd(day, 6, #weekstart)
SELECT #weekstart, #weekend
Answer:
select DateAdd(day,-DATEPart(DW,<Date>), <Date>) [FirstDayOfWeek] ,DateAdd(day,-DATEPart(DW,<Date>)+6, <Date>) [LastDayOfWeek]
FROM <TABLE>
This works for me:
select
convert(varchar(50), dateadd(dd, - datepart(dw, DATECOL) + 1, DATECOL), 101),
convert(varchar(50), dateadd(dd, - datepart(dw, DATECOL) + 7, DATECOL), 101)
I didn't take the time to test out every answer on here, but nothing seems as simple and as efficient as this:
DECLARE #WeekNum int
DECLARE #YearNum char(4)
SELECT DATEADD(wk, DATEDIFF(wk, 6, '1/1/' + #YearNum) + (#WeekNum-1), 6) AS StartOfWeek
SELECT DATEADD(wk, DATEDIFF(wk, 5, '1/1/' + #YearNum) + (#WeekNum-1), 5) AS EndOfWeek

Resources