SQL 'Round' Up a Date to a Given Day of the week - sql

My company groups all tasks into individual weeks that end on a Thursday. Thus a task due on 3/20/19 would be grouped into the 3/21 week and tasks due on 3/22 group into the 3/28/19 week.
I'm looking to calculate this field (called duedate_Weekdue) based on an input duedate.
The following works but doesn't seem like the simplest way to do this. Anyone have more elegant methods?
Select
getdate() as duedate,
datepart(yy,getdate()) as duedate_yr,
datepart(ww,getdate()) as duedate_ww,
CASE
When datename(dw,Dateadd(day,1,getdate()))='Thursday' Then Dateadd(day,1,getdate())
When datename(dw,Dateadd(day,2,getdate()))='Thursday' Then Dateadd(day,2,getdate())
When datename(dw,Dateadd(day,3,getdate()))='Thursday' Then Dateadd(day,3,getdate())
When datename(dw,Dateadd(day,4,getdate()))='Thursday' Then Dateadd(day,4,getdate())
When datename(dw,Dateadd(day,5,getdate()))='Thursday' Then Dateadd(day,5,getdate())
When datename(dw,Dateadd(day,6,getdate()))='Thursday' Then Dateadd(day,6,getdate())
When datename(dw,Dateadd(day,0,getdate()))='Thursday' Then Dateadd(day,0,getdate())
END as duedate_Weekdue;

You can reduce that to one line of code that uses a little math, and some SQL Engine trivia.
The answers that depend on DATEPART return non-deterministic results, depending on the setting for DATEFIRST, which tells the SQL Engine what day of the week to treat as the first day of the week.
There's a way to do what you want without the risk of getting the wrong result based on a change to the DATEFIRST setting.
Inside SQL Server, day number 0 is January 1, 1900, which happens to have been a Monday. We've all used this little trick to strip the time off of GETDATE() by calculating the number of days since day 0 then adding that number to day 0 to get today's date at midnight:
SELECT DATEADD(DAY, DATEDIFF(DAY, 0, GETDATE()),0)
Similarly, day number 3 was January 4, 1900. That's relevant because that day was a Thursday. Applying a little math to the number of days, and relying on the DATEDIFF function to drop fractions, which it does, this calculation will always return the next Thursday for you:
SELECT DATEADD(DAY, (DATEDIFF(DAY, 3, GETDATE())/7)*7 + 7,3);
Credit to this answer for the assist.
So your final query comes down to this:
Select
getdate() as duedate,
datepart(yy,getdate()) as duedate_yr,
datepart(ww,getdate()) as duedate_ww,
DATEADD(DAY, (DATEDIFF(DAY, 3, GETDATE())/7)*7 + 7,3) as duedate_Weekdue;

If the first day of the week is Sunday, by using the modulo operator %:
cast(dateadd(day, (13 - datepart(dw, getdate())) % 7, getdate()) as date) as duedate_Weekdue
I also applied the casting of the result to date.

Try identifying number of day in week with DATEPART and then adding enough days to go to next thursday:
declare #dt date = '2019-03-22'
declare #weekDay int
SELECT #weekDay = DATEPART(dw, #dt)
if #weekDay <= 5
select DATEADD(day, 5 - #weekDay ,#dt)
else
select DATEADD(day, 12 - #weekDay ,#dt)

Related

Needing to get first of month in SQL Server, but from yesterday's date

I am working on a report that will send daily with the full months data minus the current day as it sends early and won't have relevant data from today.
Currently the parameter for the start as =dateadd(“m”,1,dateserial(year(Today),month(Today),0))
and the end as =DateAdd("d",-1,Today())
The problem is that this will skip the last day of each month. I want to change the start date to the first of the month from today - 1 day, but I am not sure how to do this?
I could change it to send with the current days data as well but with it sending at 8 AM the data would still be missing.
Probably least amount of code is to (1) subtract a day from today, (2) take the eomonth of the previous month, and (3) add a day.
SELECT DATEADD(DAY, 1, EOMONTH(DATEADD(DAY, -1, GETDATE()),-1));
I'm not sure what language you're doing dateserial stuff in but I'd personally do this inside SQL Server.
Also the best way to drive a report for any period is to say:
col >= start of the period
AND
col < start of *next* period
For example, for a report to cover all of last month (I realize that's not quite what you're doing, it's just a simpler example), I would say:
DECLARE #start date = DATEADD(DAY, 1, EOMONTH(GETDATE(), -2));
...
WHERE date_column >= #start
AND date_column < DATEADD(MONTH, 1, #start);
This way you don't get bitten by all of the problems that can happen with various date/time types as you try to figure out what is the end of a day or month.
For month-to-date covering yesterday and not today, I would say:
DECLARE #today date = GETDATE();
DECLARE #start date = DATEADD(DAY, 1, EOMONTH(DATEADD(DAY, -1, #today),-1));
... WHERE date_column >= #start
AND date_column < #today;

How to pull week to date data in SQL Server?

I'm trying to pull week to date data for a recurring report that needs to go out daily. For eg: report that goes out Monday needs to have data for Monday, report that goes out Tuesday will have data for Monday and Tuesday etc. for the current week.
I know how to pull last X days data with :
my_date > DATEADD(day, -6, GETDATE())
How do I pull only week to date?
Use below SQL code in where condition:
where
my_date > DATEADD(dd, -((DATEPART(WEEKDAY, mydate) + 5) % 7), DATEADD(dd, DATEDIFF(dd, 0, GETDATE()), 0))
The correct adjustment requires a bit of extra modular arithmetic and so the proper number of days to adjust backward works out to the expression below. I am also assuming that datefirst is set to Sunday:
-((DATEPART(WEEKDAY, mydate) + 5) % 7)
If you just subtract two from the day number given by datepart() then you'll end up with a negative number on Sundays which then cause dateadd() to jump forward. You can see a listing of dates and comparison here:
https://rextester.com/IUY88999

Data Preparation End OF Every Month - Moving Over 12 Months

I have data prep procedure in SQL. I want to have data preparation at the end of every month
Say I want the procedure run on last day of month e.g. on 31 January 2020 it should prep data from 1 January to 31 January.
So it's kind of moving window over all months of the year. Because I need data for evaluation at the end of each month.
I tried this, however, this does not give automation. Its sort of manual running end of every month
select '2020-10-01' as beginDate_AnalysisWindow
, '2020 -01-31' as endDate_AnalysisWindow
into #AnalysisWindow --create temporary table #AnalysisWindow
I also tried the following, however, I’m not sure if it does for the whole month or just one day?
SELECT START_OF_MONTH_DATE AS beginDate_AnalysisWindow
,END_OF_MONTH_DATE AS endDate_AnalysisWindow
INTO #AnalysisWindow
FROM [dbo].[Date] WITH (NOLOCK)
WHERE DATE = DATEADD(dd, - 1, CAST(GETDATE() AS DATE))
Could someone pls help me/give me some suggestions.
Thanks in advance
If you want the last day of the current month, use eomonth():
WHERE DATE = CONVERT(date, EOMONTH(GETDATE()))
Note: This assumes that the date column has no time component. If that is the case:
WHERE CONVERT(date, DATE) = CONVERT(date, EOMONTH(GETDATE()))
SQL Server will still use an index (if available) even for this type of conversion.
EDIT:
To get the current months data, one method is:
WHERE DATE <= CONVERT(date, EOMONTH(GETDATE())) AND
DATE > CONVERT(date, EOMONTH(GETDATE(), -1))
The second argument to EOMONTH() is a months offset.
You can also try:
where getdate() <= EOMONTH(GETDATE()) AND
getdate() > DATEADD(DAY, 1, EOMONTH(GETDATE(), -1))
Instead of getdate(), you can use your date column.

How to write a query that selects last specific day and goes back 1 week from there?

I use this query on SEDE. I have just been editing the WHERE p.CreationDate > '2014-12-21T00:00:00.001' like every week to select the last week's records only. So for example previous edits were just changing to 12-21 from 12-14 from 12-7, etc..
I'm trying to edit that part so that I don't have to keep editing it every week.
I was thinking I could do something like
WHERE DATEDIFF(DAY, p.creationDate, GETDATE()) <= 7
which would select only results from the last 7 days.
However, this will only work on like Sunday when SEDE is updated. If I run a query on Wednesday, then this query will be missing three days of results.
How could I write this where statement to like find the last nearest Sunday, or is Sunday, and then go back one week from there?
To find previous sunday use this piece of code.
select DateAdd(dd, -1, DateAdd(wk, DateDiff(wk, 0, getdate()), 0)) [Previous Sunday]
Where clause should be something like.
Select ... from tablename
WHERE DATEDIFF(DAY, p.creationDate, DateAdd(dd, -1, DateAdd(wk, DateDiff(wk, 0, getdate()), 0))) <= 7
There are several solutions, similar to this.
Replace the GetDate in your where clause with some thing like
Cast(DateAdd(day,-DatePart(weekday,GetDate())+1,GetDate()) as Date)
where the +1 is adjusted larger or smaller to move to the specific day of the week you want to start with. The +1 causes this to becaome sunday.
-- Assuming your db ##DATEFIRST in on sunday (i.e 7)
WHERE p.creationdate >= DATEADD( day, -6 - DATEPART(weekday,GETDATE()), GETDATE() )

SQL Date Diff disregarding Year

i want to make a select, where the users birthday(date field) is less than 30 days.
what is the best way to to do it? i tried datediff, but i don't know how to put the year aside.
Thanks
You could just use DATEPART function with dayofyear datepart value.
EDIT: honestly, there is a boundary issue in my previous answer (many thanks to Damien): e.g. 2010-12-25 and 2011-01-07 => the difference should be less then 30 days, but DATEPART(dayofyear, #date) - DATEPART(dayofyear, [Birthday]) < 30 condition would skip this record. So I added an additional contition to my answer:
DATEPART(dy, #d) - DATEPART(dy, [Birthday]) < 30 OR
(
DATEPART(mm, #d) = 12 AND
DATEPART(dy, DATEADD(m, 1, #d)) - DATEPART(dy, DATEADD(m, 1, [Birthday])) < 30
)
it adds one month to the each date in the case when the month part of the first date is December and compares the difference.
A common way is to compose a formatted date, as text, and replace the year with the current year; and parse back into a date. Apply datediff on that.
If you find out datediff returns something negative thus the birthday of this year is in the past, add 1 year, and try again. This is for the time period around New Year.
SELECT *
FROM dbo.CheckBirthDay
WHERE (CASE WHEN YEAR(BirthDay) <= YEAR(CURRENT_TIMESTAMP) THEN DATEDIFF(DD,BirthDay,CURRENT_TIMESTAMP) END < 30 )