SQL - Determine last day of previous week with flexibility - sql

I'm trying to create a flexible way to determine the last day of the previous week, knowing that this will be run in different environments that have different planning weeks (some customers use Sat-fri; Sun-Sat; Mon-Sun). I was planning on creating a variable to use as each customer's start day of the week (see below)
Set #BeginningWeek = 1 -- 1=Mon; 2=Tues; 3=Wed; 4=Thurs; 5=Fri; 6=Sat; 7=Sun
I've been trying to use a combination of dateadd and datepart, along with the variable above to try to get the previous last day of the week, but I'm having trouble with the logic. I do not want to use Set DateFirst, these scripts will be run on a customer environment, so I do not want to change anything in their DB.
Assuming the script is run on a wednesday (9/3); and the customer's planning week is sat-fri; I would want to set another variable #EndDate = the previous friday(8/29). I need the flexibility to use the same script (changing only the #BeginningWeek value) with another customer whose planning week is Mon-Sun and set #EndDate = the previous sunday (8/31).

This will get you the previous Saturday.
SELECT DATEADD(day,
-1 - (DATEPART(dw, GETDATE()) + ##DATEFIRST - 2) % 6,
GETDATE()
)
You should be able to replace the %6 bit with a parameter based on what the actual desired day is.

Related

Improper calculation of a date difference in sql

I need to calculate the difference between DFU.HISTSTART and the last Sunday which is for today is 2/27. It should be dynamic and change every Sunday.
For some reason for this calculation I am getting 3 and should get 4.
,ABS(DATEDIFF(wk,
DATEADD(wk,
DATEDIFF(wk,6,GETDATE()), 0), DFU.HISTSTART))
AS '#WKS of Hist'
Does someone have any ideas?
You have two problems... the first is that you're trying to do the old offset trick with the "6". That works on other date parts but not on week. From the Microsoft Documentation...
For a week (wk, ww) or weekday (dw) datepart, the DATEPART return
value depends on the value set by SET DATEFIRST.
If your DATEFIRST is set to 7 (you can verify by running SELECT ##DATEFIRST;) AND your weeks start on Sundays, the following will work just fine and return a "4".
--===== Setup just the dates in question for a demo
DECLARE #HistStart DATE = '01-30-22'
,#Today DATE = '02-27-22'
;
--===== Demo the "right" way to use "wk".
-- I say "right" way because I don't trust DATEFIRST.
SELECT DATEDIFF(wk,#HistStart,#Today)
;
GO
The second thing is that it's generally a really bad practice to depend on the DATEFIRST setting in this global computing environment. Instead, do the much more universal/bullet-proof method of using Integer math to calculate the number of weeks it's been since date-serial 6, which you correctly identified as a Sunday.
--===== Setup just the dates in question for a demo
DECLARE #HistStart DATE = '01-30-22'
,#Today DATE = '02-27-22'
;
--===== Demo "Bulletproof" Way to calculate the difference in Weeks starting on Sunday
SELECT DATEDIFF(dd,6,#Today)/7 - DATEDIFF(dd,6,#HistStart)/7
;
If you need to calculate week differences in weeks a lot, you might want to turn that into a function so that if the company decides to change the day of the week that is the start of the week, you'll only need to change it in one place. In fact, you might want to have the function read it (the date-serial for the starting day of the week) from a "general settings table".
Another way of doing this:
with last_sunday as (
SELECT
case DAYNAME(current_date())
when 'Sun' then current_date()
when 'Mon'then current_date()-1
when 'Tue'then current_date()-2
when 'Wed'then current_date()-3
when 'Thu'then current_date()-4
when 'Fri'then current_date()-5
when 'Sat'then current_date()-6
else '2020-01-01' end "SUNDAY_DATE"
)
SELECT
DFU.HIST_START_DATE
,LAST_SUNDAY.SUNDAY_DATE
,datediff(week,DFU.HIST_START_DATE,LAST_SUNDAY.SUNDAY_DATE) weeks_diff
FROM DFU
JOIN LAST_SUNDAY
;

How to write a variable within SQL using Dateadd and DateDiff for finding the last TWO days of the previous month

I am trying to write a variable using the dateadd and datediff that shows the last two days of previous month. One variable will be the second to last day of the previous month, the one I am having trouble with. The other will be the last day of the previous month, the one I was able to get. I am using SQL Server.
I've tried looking for it on Stack and I have only seen the last day of the previous month given and NOT the second to last day. I tried learning the dateadd and datediff, (which I still want to do).
This is what I tried so far:
Declare #CurrentMonth as date = '3/1/2019'
Declare #SecLastDayPrevMonth as date = DATEADD(MONTH, DATEDIFF(MONTH, 0, #currentmonth), -2)
Declare #LastDayPrevMonth as date = DATEADD(MONTH, DATEDIFF(MONTH, 0, #currentmonth), -1)
The results I am getting for the seclastdayPrevMonth is 2/28/2019. Instead I would want 2/27/2019
I am also getting 2/28/2019 for lastdayprevmonth which is what I want.
I am writing variables because the current month will change every month, and instead of having to update the other days I need within my query, I want to use variables so I am only updating the current month and everything else is flowing through.
And explanation as to why my dateadd/datediff is wrong and an explanation for why the correct dateadd/datediff is the way it is, will be very helpful
Why not refer to the last day when calculating the second last day? Also, your usage of DATEADD is very weird. The syntax is DATEADD(interval, increment, datetime)
Declare #recmonth as date = '3/1/2019'
Declare #LastDayPrevMonth as date = EOMONTH(DATEADD(MONTH, -1, #RecMonth))
Declare #SecLastDayPrevMonth as date = DATEADD(DAY, -1, #LastDayPrevMonth)
SELECT #SecLastDayPrevMonth, #LastDayPrevMonth
So we can calculate the last day of the previous month by subtracting one month from a date and then calling EOMONTH, which returns the last day of a given month. Then the second last day is just subtracting one day from that.
Yields:
SecLastDayPrevMonth LastDayPrevMonth
------------------- ----------------
2019-02-27 2019-02-28
As to "why", DATEDIFF() takes 3 arguments: datepart (string representation of a specific date part), startdate, enddate (both of which must be convertible to a date-ish object).
0 is essentially SQL's epoch, which, in this case is 1/1/1900. So the difference in months between 0 and 3/1/2019 is (119*12)+2 (+2 because we exclude March, since we aren't calculating a full month) = 1430 months difference.
Then, we are trying to add 2 months to our value. DATEADD() takes 3 arguments: datepart, number, date. But, in the example, you are adding 1430 months to whatever date -2 gets converted to (in this case, I believe it would be 12/30/1899, or 2 days before epoch). So, 1430 months after 12/30/1899 would be 2/30/2019, but February only has 28 days in 2019, so it returns 2/28/2019. In a Leap Year, it probably would return 2/29/2019.
To get your #LastDayPrevMonth and #SecLastDayPrevMonth with only DATEDIFF() and DATEADD(), you just need to change your calculations a little.
First thing you want to do is find the First Day of your Given Month. That can be done with DATEADD(month,DATEDIFF(month,0,#CurrentDate),0). We're essentially using the same thing we used above to calculate the number of months since epoch, but then we are adding those months back to epoch.
Now that we know the First Day of the Given Month, all we have to do is subtract days to get a day from the prior month.
So,
DECLARE #CurrentDate date = '2019-03-15' ; -- Changed it to something in the middle of the month.
DECLARE #FirstDayOfGivenMonth = DATEADD(month,DATEDIFF(month,0,#CurrentDate),0) ; -- 3/1/2019
DECLARE #LastDayOfPrevMonth date = DATEADD(day,-1,#FirstDayOfThisMonth) ; -- 2/28/2019
DECLARE #SecLastDayOfPrevMonth date = DATEADD(day, -2, #FirstDayOfThisMonth) ; -- 2/27/2019
SELECT #LastDayOfPrevMonth AS LDPM, #SecLastDayOfPrevMonth AS SLDPM ;
DECLARE #FourDaysLeftInPrevMonth date = DATEADD(day, -4, #FirstDayOfThisMonth) ; -- 2/25/2019
SELECT #FourDaysLeftInPrevMonth AS FourDaysLeftPrev ;
Granted, since SQL 2012, this can all be accomplished much easier with the EOM() function to get to the last day of a month. But if you only could use the two original functions from your original question, this would be one way to get to your needed values.

WHERE statement to choose record previous day but choose Friday record when current day is Monday Microsoft SQL

I need a WHERE statement where the date of the record is the previous day. I have the below code which will do this
WHERE DOC_DATE = dateadd(day,datediff(day,1,GETDATE()),0)
However I need this statement to get Friday's record when the current day is Monday. I have the below code but it will not work for me. No errors come back on SQL although no records results come back either. I have the below code for this
WHERE DOC_DATE = DATEADD(day, CASE WHEN datepart(dw,(GETDATE())) IN (2) then -3 ELSE -1 END ,0)
Important to add that this needs to be in a WHERE clause. This is for a Docuware administrative view I am creating. I have no control on how to write the SELECT statement, I only have access to edit the WHERE clause:
Here's a slightly "magical" way to compute the value that doesn't depend on any particular server settings such as datefirst. It's probably not immediately obvious how it works:
WHERE DOC_DATE = dateadd(day,datediff(day,'20150316',getdate()),
CASE WHEN DATEPART(weekday,getdate()) = DATEPART(weekday,'20150330')
THEN '20150313'
ELSE '20150315' END)
In the first line, we're computing the number of days which have elapsed since some arbitrary date. I picked a day in March 2015 to use as my base date.
The second line asks what today's day of the week is and if it's the same as some arbitrary "Known good" Monday. Just taking one value and comparing it to 2 depends on what your DATEFIRST setting is so I prefer not to write that.
In the third line, we decide what to do if it's a monday - I give a date that is 3 days before my arbitrary date above. If it wasn't a monday, we pick the day before.
Adding it all together, when we add the days difference from the arbitrary date back to one of these two dates from lines 3 and 4, it has the effect of shifting the date backwards 1 or 3 days.
It's can be an odd structure to see if you're not familiar with it - but combining dateadd/datediff and exploiting relationships between an arbitrary date and other dates computed from it can be useful for performing all kinds of calculations. A similar structure can be used for computing e.g. the last day of the month 15 months ago using just dateadd/datediff, an arbitrary date and another date with the right offset from the first:
SELECT DATEADD(month,DATEDIFF(month,'20010101',GETDATE()),'19991031')
As I said in a comment though, usually doing this sort of thing is only a short step away from needing to properly model your organisation's business days, at which point you'd typically want to introduce a calendar table. At one row per day, 20 years worth of pre-calculated calendar (adjusted as necessary as the business changes) is still less than 10000 rows.
You can try this.
WHERE DOC_DATE = DATEADD(DAY, CASE WHEN datepart(dw, GETDATE()) = 2 THEN -3 ELSE -1 END, CAST(GETDATE() AS DATE))

Values over a time period

I would like to calculate a value in SQL that does the following:
if the user's inputted month parameter (I'm writing the SQL to aggregate information first then using SSRS) is 90 days or more past today (or the most recent data entry), then use the value for that month. Otherwise, if the month parameter is within 90 days of the report run date (or most recent data entry), use the most recent value that IS in fact 90 days past. For example, if I run the report in August and specify the month parameter as August, I will want the value from May. If I specify the month parameter as April, I want April's value since 90 days has gone by since then. Apologies if this isn't concise. I just would like some direction.
Cheers.
As suggested by #DanBracuk in the comments, you should probably take the parameter from SSRS and just update it based on the current date in your T-SQL code.
Say you have an SSRS parameter Parameters!Month.Value which gets passed to your stored procedure as #Month.
You can use GETDATE() and date functions like DATEDIFF() and DATEADD() to adjust #Month as required, e.g. in the stored procedure code something like:
select #Month = case when datediff(dd, #Month, getdate()) > 90
then #Month
else dateadd(dd, -90, #Month)
end
select *
from MyTable
where MyDate < #Month
Update the first statement to meet your requirements; they seem a bit ambiguous at this point - apologies if I haven't understood the issue completely.

I want to find first day and any other in a month in SQL query

I want to find first day month of month and also like 3rd day or 5th day ,15th day or any day of the month .So how to find through query.I know how to find first day and last day of month.Mainy I want find other days.
For those of you following along who may not know how to get the First Day of the month in SQL Server you can do so with something like this. This will also give you the 5th, 10th or whatever you need.
DECLARE #FirstDay DATETIME
SET #FirstDay = (DATEADD(MONTH, DATEDIFF(MONTH, -1, GETDATE()) - 1, -1) + 1)
SELECT GETDATE() AS CurrentDay
, #FirstDay AS FirstDay
, DATEADD(d, 10, #FirstDay-1) AS TenthDay
The -1 after the #FirstDay in the DateAdd is because the DateAdd will add the numbers of days onto the firstday, which will give you the 11th in that example. Of course you could just add one less day to make it work without the -1 but I prefer including it. Suit yourself.
If you know how to find the first day of a month, you can add the 2-day, the 4-day or the 14-day interval to the first day of the month to get, respectively, the 3rd, the 5th or the 15th day of the month.
Similarly you can get any day of the month by simply adding the proper number of days.
Different RDBMSs may offer different syntax to achieve the goal. Assuming #MonthBeginning to be a date or datetime value representing the first day of a month, here's how you can get, for example, the 5th day of the same month in Microsoft SQL Server:
SELECT DATEADD(day, 4, #MonthBeginning) AS FifthDay
Again, it may not be the way you should do that if your RDBMS is not MS SQL Server.