Move Date to Monday in TSQL - sql

I need to create a function in TSQL that will move the date to Monday if the date passed is either a Saturday or Sunday only.
For example:
5/6/2012 should return 5/7/2012
5/12/2012 should return 5/14/2012
If the date is not a Saturday or Sunday, return the input date

This will give you the Monday following if the date is a Saturday or Sunday; otherwise, it will give you the date you provided.
DECLARE #Date DATETIME; SET #Date = GETDATE();
SELECT
CASE (##DATEFIRST + DATEPART(dw, #Date)) % 7
WHEN 0 THEN DATEADD(day, 2, #Date) -- Saturday
WHEN 1 THEN DATEADD(day, 1, #Date) -- Sunday
ELSE #Date
END AS Weekday
EDIT: Included Conrad Frix's solution to the ##DATEFIRST issue.

You can use the DatePart function in T-SQL. For each date, you can extract day component, and if it's a Saturday, increment the date by two days. If it's Sunday, increment the date by one day. You can use DateAdd to increment a date.

Depending upon your SET DATEFIRST options (US English default is 7, for Sunday = 1):
CASE
WHEN DATEPART(weekday, #dt) = 1 -- Sunday
THEN DATEADD(day, 1, #dt)
WHEN DATEPART(weekday, #dt) = 7 -- Saturday
THEN DATEADD(day, 2, #dt)
ELSE
#dt
END

A version that is not depending on set datefirst
select dateadd(day,
case datediff(day, '17530101', #D) % 7
when 5 then 2
when 6 then 1
else 0
end,
#D)
Same thing perhaps prettier.
select case datediff(day, '17530101', #D) % 7
when 5 then dateadd(day, 2, #D)
when 6 then dateadd(day, 1, #D)
else #D
end
The constant 17530101 is a Monday and works for datetime. If you are using date you can use 00010101 instead.

How about this:
SELECT CASE datepart(dw, #date) WHEN 7 THEN #date + 2 WHEN 1 THEN #date + 1 ELSE #date END

This should do it for you. Basically adds 2 days on Saturday or 1 day on Sunday to move the sample date to Monday
DECLARE #Temp TABLE
(
dtSample DATETIME
)
INSERT INTO #Temp VALUES ('5/1/2012')
INSERT INTO #Temp VALUES ('5/2/2012')
INSERT INTO #Temp VALUES ('5/3/2012')
INSERT INTO #Temp VALUES ('5/4/2012')
INSERT INTO #Temp VALUES ('5/5/2012')
INSERT INTO #Temp VALUES ('5/6/2012')
INSERT INTO #Temp VALUES ('5/7/2012')
INSERT INTO #Temp VALUES ('5/8/2012')
INSERT INTO #Temp VALUES ('5/9/2012')
INSERT INTO #Temp VALUES ('5/10/2012')
INSERT INTO #Temp VALUES ('5/11/2012')
INSERT INTO #Temp VALUES ('5/12/2012')
INSERT INTO #Temp VALUES ('5/13/2012')
INSERT INTO #Temp VALUES ('5/14/2012')
SELECT
dtSample
,CASE WHEN DATEPART(WEEKDAY, dtSample) = 7
THEN dtSample + 2
ELSE
CASE WHEN DATEPART(WEEKDAY, dtSample) = 1
THEN dtSample + 1
ELSE dtSample
END
END AS dtConverted
FROM #Temp
Results:
dtSample dtConverted
2012-05-01 00:00:00.000 2012-05-01 00:00:00.000
2012-05-02 00:00:00.000 2012-05-02 00:00:00.000
2012-05-03 00:00:00.000 2012-05-03 00:00:00.000
2012-05-04 00:00:00.000 2012-05-04 00:00:00.000
2012-05-05 00:00:00.000 2012-05-07 00:00:00.000
2012-05-06 00:00:00.000 2012-05-07 00:00:00.000
2012-05-07 00:00:00.000 2012-05-07 00:00:00.000
2012-05-08 00:00:00.000 2012-05-08 00:00:00.000
2012-05-09 00:00:00.000 2012-05-09 00:00:00.000
2012-05-10 00:00:00.000 2012-05-10 00:00:00.000
2012-05-11 00:00:00.000 2012-05-11 00:00:00.000
2012-05-12 00:00:00.000 2012-05-14 00:00:00.000
2012-05-13 00:00:00.000 2012-05-14 00:00:00.000
2012-05-14 00:00:00.000 2012-05-14 00:00:00.000

Use a combination of DATEPART and DATEADD:
select date
, CASE DATEPART(weekday, date)
WHEN 6 THEN DATEADD(day, 2, date) -- Saturday
WHEN 7 THEN DATEADD(day, 1, date) -- Sunday
ELSE date
END AS NewDate
from #dates
order by date
Sample data:
declare #dates table(date datetime);
insert into #dates values('20120401');
insert into #dates values('20120402');
insert into #dates values('20120403');
insert into #dates values('20120404');
insert into #dates values('20120405');
insert into #dates values('20120406');
insert into #dates values('20120407');
insert into #dates values('20120408');
insert into #dates values('20120409');
insert into #dates values('20120410');
Result:
date NewDate
2012-04-01 00:00:00.000 2012-04-02 00:00:00.000
2012-04-02 00:00:00.000 2012-04-02 00:00:00.000
2012-04-03 00:00:00.000 2012-04-03 00:00:00.000
2012-04-04 00:00:00.000 2012-04-04 00:00:00.000
2012-04-05 00:00:00.000 2012-04-05 00:00:00.000
2012-04-06 00:00:00.000 2012-04-06 00:00:00.000
2012-04-07 00:00:00.000 2012-04-09 00:00:00.000
2012-04-08 00:00:00.000 2012-04-09 00:00:00.000
2012-04-09 00:00:00.000 2012-04-09 00:00:00.000
2012-04-10 00:00:00.000 2012-04-10 00:00:00.000

I extended the solution provided by zimdanen on 8th May as when the start date was a friday it failed .
DECLARE #FromDate DATETIME
BEGIN
SET #FromDate='04/07/2014'
SELECT
#FromDate as [StartDate]
, CASE (##DATEFIRST + DATEPART(dw, (DATEADD(DD,3,#FromDate)))) % 7
WHEN 0 THEN DATEADD(day, 2, (DATEADD(DD,3,#FromDate))) -- Saturday
WHEN 1 THEN
CASE
WHEN DATEPART(dw,#FromDate)=5 Then DATEADD(DD,5,#FromDate) -- Thursday
ELSE DATEADD(day, 1, (DATEADD(DD,3,#FromDate))) -- Sunday
END
ELSE
CASE
WHEN DATEPART(dw,#FromDate)=6 Then DATEADD(DD,5,#FromDate) -- Friday
ELSE DATEADD(DD,3,#FromDate)
END
END AS Weekday
END

Related

SQL - Return dates for 2 or 3 weeks intervals (before and after) based on a datetime value

Is there a way to specify a date (2020-01-20) and then return every 2 weeks forward and backwards between a start and finish period. So 01 Jan 2020 to 01 Mar 2020 for example. A list of of all the dates every 2 weeks from this input date.
Sample of table data for reference...
DECLARE #Source TABLE(bookingcode int, NextWeekCommDate DATETIME, Cycle int)
insert into #Source (bookingcode, NextWeekCommDate, cycle)
select 556789, '23 Mar 2020', 2
insert into #Source (bookingcode, NextWeekCommDate, cycle)
select 556790, '30 Mar 2020', 3
select * from #Source
declare #from datetime = '01 Mar 2020'
declare #to datetime = '01 Jun 2020'
Then I am trying to output the following results based on the declared#to and #from dates
bookingcode CycleOccurDate
556789 2020-03-09 00:00:00.000
556789 2020-03-23 00:00:00.000
556789 2020-04-06 00:00:00.000
556789 2020-04-20 00:00:00.000
556789 2020-05-04 00:00:00.000
556789 2020-05-18 00:00:00.000
556790 2020-03-30 00:00:00.000
556790 2020-04-20 00:00:00.000
556790 2020-05-11 00:00:00.000
So it works backwards as well from the NextWeekCommDate if the #from date is before this Thank you again
You can use a recursive CTE:
with dates as (
select convert(date, '2020-01-01') as dte
union all
select dateadd(week, 2, dte)
from cte
where dte < '2020-03-01'
)
select *
from dates;
If this can return more than 100 rows, then use option (maxrecursion 0).
It is not clear to me what '2020-01-20' has to do with the question.
Sure, you can add directly or substract fom a date field
SELECT GETDATE(), GETDATE()-14, GETDATE()+14
or in date
SELECT CAST(GETDATE() AS DATE), CAST(GETDATE()-14 AS DATE), CAST(GETDATE()+14 AS DATE)
Take a look at DATE FUNCTIONS in SQLSERVER, there are plenty of them that you may use
https://learn.microsoft.com/es-es/sql/t-sql/functions/date-and-time-data-types-and-functions-transact-sql?view=sql-server-ver15

SQL - Generate bi-weekly end date on custom start date

I want to get a custom Fort Night period based on a given start date.
I have a table that looks like this:
IF OBJECT_ID('tempdb..#tbl1') IS NOT NULL DROP TABLE #tbl1
SET DATEFIRST 1
DECLARE #StartDateTime DATETIME
DECLARE #EndDateTime DATETIME
SET #StartDateTime = '2016-09-03'
SET #EndDateTime = '2017-01-28';
WITH DateRange(DateData) AS
(
SELECT #StartDateTime as Date
UNION ALL
SELECT DATEADD(d,1,DateData)
FROM DateRange
WHERE DateData < #EndDateTime
)
SELECT ROW_NUMBER() OVER(ORDER BY DateData ASC) As ROWNum,DateData AS Date1
into #tbl1
FROM DateRange
OPTION (MAXRECURSION 0)
GO
SELECT top 10 * FROM #tbl1
Date1
2016-09-09 00:00:00.000
2016-09-10 00:00:00.000
2016-09-11 00:00:00.000
2016-09-12 00:00:00.000
2016-09-13 00:00:00.000
2016-09-14 00:00:00.000
2016-09-15 00:00:00.000
2016-09-16 00:00:00.000
2016-09-17 00:00:00.000
2016-09-18 00:00:00.000
2016-09-19 00:00:00.000
2016-09-20 00:00:00.000
2016-09-21 00:00:00.000
2016-09-22 00:00:00.000
I want to say the the first date of my bi-weekly period is 2016-09-09 and it ends 2016-09-22. How do I get bi-weekly end date for each of those dates.
So I want it to look like
Date1 FortNightEndDate
2016-09-09 00:00:00.000 2016-09-22 00:00:00.000
2016-09-10 00:00:00.000 2016-09-22 00:00:00.000
2016-09-11 00:00:00.000 2016-09-22 00:00:00.000
2016-09-12 00:00:00.000 2016-09-22 00:00:00.000
2016-09-13 00:00:00.000 2016-09-22 00:00:00.000
2016-09-14 00:00:00.000 2016-09-22 00:00:00.000
2016-09-15 00:00:00.000 2016-09-22 00:00:00.000
2016-09-16 00:00:00.000 2016-09-22 00:00:00.000
2016-09-17 00:00:00.000 2016-09-22 00:00:00.000
2016-09-18 00:00:00.000 2016-09-22 00:00:00.000
2016-09-19 00:00:00.000 2016-09-22 00:00:00.000
2016-09-20 00:00:00.000 2016-09-22 00:00:00.000
2016-09-21 00:00:00.000 2016-09-22 00:00:00.000
2016-09-22 00:00:00.000 2016-09-22 00:00:00.000
I'm using SQL Server 2005.
ANSWER:
I was able to solve it using the following code. Essentially I just created 3 tables:
StartDates
EndDates
InbetweenDates
The Start/EndDates tables had just the start and End of my 2 week period and an ID (Row Number)
The InbetweenDates table had all the dates between the 2 dates and also had a ID column but instead of going up 1 every row, it went up 1 every 14 rows.
Then I just joined the 3 tables. Essentially, the Start/EndDates tables were lookup tables.
I got the RowNumber on every 14 days code from here.
-- BEtween Dates
IF OBJECT_ID('tempdb..#BetweenDates') IS NOT NULL DROP TABLE #BetweenDates
DECLARE #StartDateTime DATETIME
DECLARE #EndDateTime DATETIME
SET #StartDateTime = '2016-09-09'
SET #EndDateTime = '2017-04-30';
WITH DateRange(DateData) AS
(
SELECT #StartDateTime as Date
UNION ALL
SELECT DATEADD(d,1,DateData)
FROM DateRange
WHERE DateData < #EndDateTime
)
SELECT
DateData
into #BetweenDates
FROM DateRange
OPTION (MAXRECURSION 0)
GO
select
(case when convert(int, (ROW_NUMBER() OVER (Order by (select 0)) % 14))=0 then 0 else 1 end)
+ convert(int, (ROW_NUMBER() OVER (Order by (select 0)) / 14)) as ID
,DateData
INTO #BetweenDates_ID
from #BetweenDates
-- Start Dates
IF OBJECT_ID('tempdb..#StartDates') IS NOT NULL DROP TABLE #StartDates
DECLARE #StartDateTime DATETIME
DECLARE #EndDateTime DATETIME
SET #StartDateTime = '2016-09-09'
SET #EndDateTime = '2017-04-30';
WITH DateRange(DateData) AS
(
SELECT #StartDateTime as Date
UNION ALL
SELECT DATEADD(d,14,DateData)
FROM DateRange
WHERE DateData < #EndDateTime
)
SELECT ROW_NUMBER() OVER(ORDER BY DateData ASC) As ID,DateData
into #StartDates
FROM DateRange
OPTION (MAXRECURSION 0)
GO
-- End Dates
IF OBJECT_ID('tempdb..#EndDates') IS NOT NULL DROP TABLE #EndDates
DECLARE #StartDateTime DATETIME
DECLARE #EndDateTime DATETIME
SET #StartDateTime = '2016-09-22'
SET #EndDateTime = '2017-04-30';
WITH DateRange(DateData) AS
(
SELECT #StartDateTime as Date
UNION ALL
SELECT DATEADD(d,14,DateData)
FROM DateRange
WHERE DateData < #EndDateTime
)
SELECT ROW_NUMBER() OVER(ORDER BY DateData ASC) As ID,DateData
into #EndDates
FROM DateRange
OPTION (MAXRECURSION 0)
GO
--SELECT * FROM #StartDates
--SELECT * FROM #EndDates
--SELECT * FROM #BetweenDates_ID
SELECT
st.DateData AS StartDate
,ed.DateData AS EndDate
,bd.DateData AS BetweenDate
FROM
#StartDates st
JOIN
#EndDates ed
ON st.ID = ed.ID
LEFT JOIN
#BetweenDates_ID bd
ON st.ID = bd.ID
Try this:
SELECT Date1,
DATEADD(DD, -1, DATEADD(WW,2,Date1)) AS FortNightEndDate
FROM #tbl1

sql how to find all week start and end dates between two date

I have a table with two columns
start_date 03/09/2016
end_date 03/15/2016
Now I need all the week start and end dates between these two dates
week_start_date week_end_date
03/07/2016 03/11/2016
03/14/2016 03/18/2016
How can I achieve this using sql query. No procedures or t-sql please.
first you have to find all dates between #start_date and #end_date :
declare #start_date datetime
declare #end_date datetime
set #start_date='03/09/2016'
set #end_date='03/15/2016'
;
WITH dates AS (
SELECT #start_date AS dt
UNION ALL
SELECT DATEADD(dd, 1, dt)
FROM dates s
WHERE DATEADD(dd, 1, dt) <= #end_date)
result:
2016-03-09 00:00:00.000
2016-03-10 00:00:00.000
2016-03-11 00:00:00.000
2016-03-12 00:00:00.000
2016-03-13 00:00:00.000
2016-03-14 00:00:00.000
2016-03-15 00:00:00.000
second you need to find out weekday ,'2' is Monday week_start_date
'6' is friday week_end_date
select DATEPART(dw,'03/11/2016') -- friday =6
select DATEPART(dw,'03/14/2016') --- monday =2
here is the final query:
declare #start_date datetime
declare #end_date datetime
set #start_date='03/01/2016'
set #end_date='03/31/2016'
;
WITH sample AS (
SELECT #start_date AS dt
UNION ALL
SELECT DATEADD(dd, 1, dt)
FROM sample s
WHERE DATEADD(dd, 1, dt) <= #end_date)
, dayofWeekTemp as(
SELECT Cast(DATEPART(dw,s.dt) as int) dayOfWeekValue , s.dt dateValue
FROM sample s)
select Case when dayOfWeekValue =6 then 'End of week' when dayOfWeekValue=2 then 'Start of week' End,dateValue from dayofWeekTemp
where dayOfWeekValue=2 Or dayOfWeekValue=6
Results
End of week 2016-03-04 00:00:00.000
Start of week 2016-03-07 00:00:00.000
End of week 2016-03-11 00:00:00.000
Start of week 2016-03-14 00:00:00.000
End of week 2016-03-18 00:00:00.000
Start of week 2016-03-21 00:00:00.000
End of week 2016-03-25 00:00:00.000
Start of week 2016-03-28 00:00:00.000

Find out the number of Days and nights using sql query

I would like to find out the number of days and nights in a given date range in sql. Can anyone help me? Thanks in advance.
Try something like this:
declare #t table(datefrom datetime, dateto datetime)
insert #t values('2012-01-01 11:30', '2012-01-01 12:30')
insert #t values('2012-01-01 11:30', '2012-01-02 00:30')
insert #t values('2012-01-01 12:30', '2012-01-02 13:00')
insert #t values('2012-01-01 12:30', '2012-01-02 00:30')
insert #t values('2012-01-01 00:00', '2012-01-03 00:00')
select datefrom, dateto,
datediff(day, datefrom - .5,dateadd(minute, -1, dateto)) nights,
datediff(day, datefrom, dateadd(minute, -1, dateto)+.5) days
from #t t
Result:
datefrom dateto nights days
2012-01-01 11:30 2012-01-01 12:30 1 1
2012-01-01 11:30 2012-01-02 00:30 2 1
2012-01-01 12:30 2012-01-02 13:00 1 2
2012-01-01 12:30 2012-01-02 00:30 1 1
2012-01-01 00:00 2012-01-03 00:00 2 2
See the AnandPhadke's comment, and here is the code :)
DECLARE #StartDate DATETIME = '2012-01-01'
DECLARE #EndDate DATETIME = '2012-02-01'
SELECT DATEDIFF(DAY, #StartDate, #EndDate) AS [Days], DATEDIFF(DAY, #StartDate, #EndDate) AS [Nights ]
Have you tried Datediff
Nights
select DATEDIFF (d, getdate()-1,getdate())
Days
select DATEDIFF (d, getdate()-1,getdate()) - 1

SQL query stuck - comparison on different lines

I m working on a very weird problem with SQL where I have to compare previous rows
Number start_date end_date
----- ------- ------------
1 2011-06-07 00:00:00.000 2011-07-10 00:00:00.000
2 2011-10-11 00:00:00.000 2011-10-11 00:00:00.000
3 2011-10-26 00:00:00.000 2011-10-29 00:00:00.000
4 2011-10-29 00:00:00.000 2011-11-15 00:00:00.000
Here , I have to compare the start_date and end_date on the two different line and create a view out of it.
(If the start_date is less than the previous end_date , then criteria is set to 1).
Well it should compare 2011-10-26 00:00:00.000 for 3 and 2011-10-27 00:00:00.000 on 2 for 30 days
Number start_date end_date Criteria
----- ----------- ---------------- ------------
1 2011-06-07 00:00:00.000 2011-07-10 00:00:00.000 0
2 2011-10-11 00:00:00.000 2011-10-11 00:00:00.000 0
3 2011-10-26 00:00:00.000 2011-10-29 00:00:00.000 1
4 2011-10-30 00:00:00.000 2011-11-15 00:00:00.000 1
I m confused how should I proceed with this.
Any help would be helpful !!!!
Thanks !!!
The most straightforward way to do this is to use a subquery:
select A.number, a.start_date, a.end_date,
CASE WHEN start_date < dateadd(d,30,(select TOP(1) b.end_date
from mytable B
where B.number < A.number
order by B.number desc)) then 1 else 0 end Criteria
from mytable A
Note: If the start date is the 29th day following the previous row's end date, Criteria becomes 1. By the 30th day onwards, it is 0. Tweak the 30 in the query as required.
Sample:
create table mytable (
Number int primary key,
start_date datetime,
end_date datetime);
insert mytable
select 1, '2011-06-07', '2011-07-10' union all
select 2, '2011-10-11', '2011-10-27' union all
select 3, '2011-10-26', '2011-10-29' union all
select 4, '2011-10-29', '2011-11-15'
Result:
number start_date end_date Criteria
1 2011-06-07 00:00:00.000 2011-07-10 00:00:00.000 0
2 2011-10-11 00:00:00.000 2011-10-27 00:00:00.000 0
3 2011-10-26 00:00:00.000 2011-10-29 00:00:00.000 1
4 2011-10-29 00:00:00.000 2011-11-15 00:00:00.000 0
Try using case like this:
create view vDates as
select Number,start_date,end_date,
case
when start_date<end_date
then 0
else 1
end as Criteria
from tab
SQL Fiddle Demo
A more readable way is create a function and send the correct dates:
Function:
create function [dbo].[CompareDates] (
#START_DATE datetime,
#PREVIOUS_END_DATE datetime
)
RETURNS int
AS
BEGIN
if #START_DATE < #PREVIOUS_END_DATE
return 1
return 0
END
Query (using subquery):
declare #dates table
(
number int,
start datetime,
end_date datetime
)
insert into #dates values
(1, '2011-06-07 00:00:00.000', '2011-07-10 00:00:00.000'),
(2, '2011-10-11 00:00:00.000', '2011-10-27 00:00:00.000'),
(3, '2011-10-26 00:00:00.000', '2011-10-29 00:00:00.000'),
(4, '2011-10-29 00:00:00.000', '2011-11-15 00:00:00.000')
select *, dbo.CompareDates(dates.end_date, dates.previous_end_date) from
(
select number, start, end_date,
(select TOP 1 end_date
from #dates d2
where d2.number < d1.number
order by d2.number desc) as previous_end_date
from #dates d1
) dates