I would like to write a simple SELECT statement in SQL Server 2005 which does the following computation with date arithmetic:
Starting from the present date (this means getdate()), determine the previous Monday, and then subtract 70 days from that Monday, showing in output the resulting date.
How could I achieve this?
My difficulty is mainly to determine the previous Monday.
Of course, if getdate() is Monday, the previous Monday is getdate()
Thank you in advance for your kind help.
UltraCommit
EDIT: Please note that in Italy the first day of the week is Monday and not Sunday, so if the input is Sunday, July 29th, 2012, the output has to be 23rd July, and not 30th July.
This will retrieve monday for the current week
Select DATEADD(wk, DATEDIFF(wk,0,GETDATE()), 0)
and then you need to subtract 70 from the above day
SELECT Dateadd(DAY,-70,DATEADD(wk, DATEDIFF(wk,0,GETDATE()), 0))
Edit : Please go through the answer posted in SO
Monday is displayed as Current Week because DATEFIRST which indicates the 1st day of the week is set to monday .In order to set it to Sunday ,you need to change the setting to Sunday
Set DATEFIRST 7
Else as suggested in the above SO link ,you need to change your code
DECLARE #dt DATE = '1905-01-01';
SELECT [start_of_week] = DATEADD(WEEK, DATEDIFF(WEEK, #dt, CURRENT_TIMESTAMP), #dt);
To get last monday look at
http://blog.sqlauthority.com/2007/08/20/sql-server-find-monday-of-the-current-week/
This should get you started. It will find the past Monday for the current week.
SELECT DATEADD(wk, DATEDIFF(wk,0,GETDATE()), 0) MondayOfCurrentWeek
To substract 70 days, just add -70 to the end:
SELECT DATEADD(wk, DATEDIFF(wk,0,GETDATE()), 0)-70 as SomeMondayInHistory
Related
I have a table full of daily aggregate data, but I occasionally need to pull weekly aggregate data, and provide info on increases or decreases. For that reason, I was considering using T-SQL DATEPART functionality to get week-number and year info for dates.
For example, I can get the following info using today's date (9/11/2020):
#nowWeekNumber int = datepart(wk,#today), --yields 37
#nowYear int = datepart(year,#today), --yields 2020
Using that logic, I could then gather info on records where year is 2020 and weekNumber is 36, and then I could compare those numbers to get a weekly increase/decrease. (Or maybe I'd compare weeks 35 and 36 to ensure that I'm dealing w/ entire weeks, but you get the picture)
However, if the date is 2021-01-03, that's going to return a year of 2021, and a weekNumber of 2. If I subtract a week, I'm going to get year 2021 and weekNumber 1. That weekNumber is only going to contain January 1st and 2nd, because 12/27 thru 12/31 are considered year 2020 and weekNumber 53 (even though the calendar week is 12/27 thru 1/2).
In other words, I don't think I can use weekNumber to gather weekly data, even though that would be fairly convenient. I'm aware that I can use DATEADD functions to grab the start and end-date for consecutive weeks, and I can then gather aggregate data for records BETWEEN those dates, but is there a more-convenient way to do this?
Why don't you consider using dateDiff as key function? As...
select dateDiff(wk, 0, getDate())
Returns a single integer for the whole week (6297 for '20200911') and :
select dateAdd(wk, dateDiff(wk, 0, getDate()), 0),
dateAdd(dd, 6, dateAdd(wk, dateDiff(wk, 0, getDate()), 0))
or
select dateAdd(wk, 6297, 0),
dateAdd(dd, 6, dateAdd(wk, 6297, 0))
gives you the 1st and last day of that week.
You can use DATEPART but instead of wk you can use the iso week. Then you don't have the problem with a week being split in 2. To be sure also use SET DATEFIRST to define exactly on which day the week starts.
SET DATEFIRST 1; --use monday as first day of the week
SELECT datepart(iso_week,'2021-01-01');
SELECT datepart(iso_week,'2021-01-03');
SELECT datepart(iso_week,'2021-01-04');
The other option is to create your own calendar table and join that to your daily table.
EDIT: for a week start on sunday
SET DATEFIRST 7;
SELECT DATEPART(WEEK, DATEADD( DAY, 1-DATEPART(WEEKDAY,'2020-12-27'),'2020-12-27' ) )
SELECT DATEPART(WEEK, DATEADD( DAY, 1-DATEPART(WEEKDAY,'2020-12-28'),'2020-12-28' ) )
SELECT DATEPART(WEEK, DATEADD( DAY, 1-DATEPART(WEEKDAY,'2021-01-01'),'2021-01-01' ) )
SELECT DATEPART(WEEK, DATEADD( DAY, 1-DATEPART(WEEKDAY,'2021-01-02'),'2021-01-02' ) )
Using TSQL on SQL Server...
I need to produce extracts that use a pay day column in a database that only holds the day number, for example 26 to produce extracts for the 26th day of the month with a twist if that pay day falls on a weekend then the data for that extract should be extracted the Friday before the weekend.
Has anybody attempted this and able to offer some ways of achieving this through TSQL?
Thanks
You have said in today's comment above that you are assuming the current month.
First turn your payday day number into a proper date:
select dateadd(month,((year(getdate())-1900)*12)+month(getdate())-1,payday-1)
as paydate
and then use a case statement against it:
case datename(weekday,paydate)
when 'Saturday' then dateadd(day,-1,paydate)
when 'Sunday' then dateadd(day,-2,paydate)
else paydate
end as paydateadjust
(Obviously these will need rewriting into a proper SQL statement. Date formula borrowed from Michael Valentine Jones' super useful SQL Server date function.)
I'm assuming you have the month and year available too, in which case you can construct a real date, and get the day of week from it as a 1-based number starting at Sunday. So, 6 equals Friday. This will produce 5 for today, as Thursday = 5
SELECT DATEPART(dw, DATEFROMPARTS(2018, 07, 26)) AS [DayOfWeek]
EDIT:
I'll make another assumption, this time that this is somehow possible, like it's always run for the current month. This is a bit dodgy as it all falls apart if it's run too early or too late, but at least it's achievable.
SELECT DATEPART(dw, DATEFROMPARTS(
DATEPART(YEAR, GETDATE()),
DATEPART(MONTH, GETDATE()),
26)) AS [DayOfWeekThisMonth]
EDIT 2:
OK, with the understanding we are working on this month, we can do this:
DECLARE #PayDayNumber INT = 29; -- Replace this with a SELECT to get your value
SELECT CASE(DATEPART(dw, DATEFROMPARTS(DATEPART(YEAR, GETDATE()), DATEPART(MONTH, GETDATE()), #PayDayNumber)))
WHEN 1 THEN #PayDayNumber - 2
WHEN 7 THEN #PayDayNumber - 1
ELSE #PayDayNumber
END AS [WeekendSafePayDay]
For this month (August 2018) the 29th will return 29th. You can change DATEPART(MONTH, GETDATE()) for 9 to test September, which will return 28 because 29th September is a Saturday, or change it to 7 to test July, which will return 27 because 29th July is a Sunday.
Sorry if the Title is confusing but it's hard to explain what I'm after in one phrase.
I'm currently producing a report based on the production for the week. I start off my CTE construction with the following to get the days Monday to Friday of the current week:
WITH
cte_Date AS
(
SELECT
CAST(DateTime AS date) AS Date
FROM
( VALUES
(GETDATE()
)
, (DATEADD(day,-1,GETDATE()))
, (DATEADD(day,-2,GETDATE()))
, (DATEADD(day,-3,GETDATE()))
, (DATEADD(day,-4,GETDATE()))
, (DATEADD(day,-5,GETDATE()))
, (DATEADD(day,-6,GETDATE())) ) AS LastSevenDays(DateTime)
WHERE
DATENAME(weekday, DateTime) = 'Monday'
UNION ALL
SELECT
DATEADD(day,1,Date)
FROM
cte_Date
WHERE
DATENAME(weekday,Date) <> 'Friday'
)
This is working fine. I have made the report available to users so they can run it anytime however sometimes nobody is available to run it last thing Friday. This means they don't get to see the full production for Friday and then the following week the CTE days change.
I'm trying to keep this a one-click affair so rather than introduce date parameters I proposed to the users that we adjust the query such that if they run the report before midday on "Monday" then it will show them last week's figures and they were happy with this (me and my big mouth). I put Monday in quotes because what we really mean of course is the first production day of the week.
My primary data table (which we'll call MyData) has a datetime field named DateTime (really!) that I can reference to determine the first day of production for the week.
One final caveat: Due to the layout of the report the users insisted that they always want to see the five days Monday to Friday, even if there is no production on a given day. (Consequently I do a LEFT JOIN from cte_Date to all other tables required.) So to be clear, right now as I'm typing this it's 11:45am local time on Tuesday and yesterday happened to be a public holiday here so running the report now should return Monday to Friday last week, but running it in 20 minutes time should return Monday to Friday this week.
Please help, my poor brain is getting twisted trying to figure it out.
There are a few different ways you can tackle this, but they all boil down to the same thing: you need a way of figuring out whether it's before or after 12pm on the first working day of the current week, then you need to get the Monday of the current "production week".
Let's just say, for simplicity's sake, you have some sort of table that contains public holidays (or non-production days). To find out whether it's the first day of the current production week, you basically just have to add the number of days in a row since the start of the week that have been public holidays.
Then you need to figure out whether it's before or after 12pm of that day.
If it's before you want last week's Monday-Friday. If it's after, you want this week's Monday-Friday.
Here's one way you might do this:
DECLARE #NonProductionDays TABLE (NPD DATE UNIQUE NOT NULL); -- Public holiday table.
INSERT #NonProductionDays (NPD) VALUES ('2017-09-25');
DECLARE #i INT = -- You don't need a variable for this, but just to keep things simple...
(
SELECT COUNT(*) -- Extract number of public holidays in a row this week before current date.
FROM #NonProductionDays AS N
WHERE DATEDIFF(WEEK, 0, N.NPD) = DATEDIFF(WEEK, 0, GETDATE())
AND N.NPD <= GETDATE()
AND (DATENAME(WEEKDAY, N.NPD) = 'Monday' OR EXISTS (SELECT 1 FROM #NonProductionDays AS N2 WHERE N2.NPD = DATEADD(DAY, -1, N.NPD)))
);
SELECT D = CAST(DATEADD(DAY, T.N, DATEADD(WEEK, DATEDIFF(HOUR, DATEADD(DAY, #i, '1900-01-01 12:00:00'), GETDATE()) / 24 / 7, '1900-01-01')) AS DATE)
FROM (VALUES (0), (1), (2), (3), (4)) AS T(N);
/*
Breaking this down:
X = DATEADD(DAY, #i, '1900-01-01 12:00:00')
-- Adds the number of NPD days this week to '1900-01-01 12:00:00'
-- So, for example, X would be '1900-01-02 12:00:00' this week
Y = DATEDIFF(HOUR, X, GETDATE()) / 24 / 7
-- The number of weeks between X and now, by taking the number of hours and dividing by 24 then by 7
-- The division is necessary to compare the hour.
-- So, for example, as of 11am on the September 26 2017, you'd get 6142.
-- As of 12pm on September 26 2017, you'd get 6143.
Z = DATEADD(WEEK, Y, '1900-01-01')
-- Just adds Y weeks to 1900-01-01, which was a Monday. This tells you the Monday of the current "production week".
-- So, for example, as of 11am on September 26 2017, you'd get '2017-09-18 00:00:00.000'.
-- As of 12pm on September 26 2017, you'd get '2017-09-25 00:00:00.000'.
Then we cast this as a date and add 0/1/2/3/4 days to it to get Monday, Tuesday, Wednesday, Thursday and Friday of the current "production week".
*/
I'm not sure I came up with the most efficient approach, but after a week of tossing it about in my brain this is what I came up with. I approached the problem from the opposite direction of that suggested by #ZLK.
My existing logic was already giving me the Monday of this week so in a subquery I looked for the first production record after Monday, stripped off the time with a DATEDIFF and made it midday with a DATEADD. I was then able to compare the current Date/Time with midday of the first production day to determine whether to reduce the date by one week.
I replaced this SELECT clause:
SELECT
CAST(DateTime AS date) AS Date
with this one:
SELECT -- Monday this week if it's after midday on the first production day otherwise Monday last week
DATEADD(week,IIF(GETDATE()>=DATEADD(hour,12,(
SELECT DATEDIFF(day,0,MIN(DateTime))
FROM MyData
WHERE CAST(MyData.DateTime AS date) >= CAST(LastSevenDays.DateTime AS date)
)),0,-1),CAST(LastSevenDays.DateTime AS date)) AS Date
To cater for the case where a new week has commenced but the operator runs the report before production starts I carefully arranged the boolean condition inside my IIF clause so that the empty result set from the subquery would mean the test returned FALSE and the operator would still see last week's figures.
(#ZLK, Thanks for your input - you did help my thinking a bit but I don't think your answer should be marked as correct. What I've come up with here is what I was originally requesting and didn't require the use of a static table.)
How to find the date of the first day of the current week in transact-sql(t-sql)?. What T-SQL statement do we need to use for this?.
This is always be a Sunday. For example if we run the sql statement on today(2015-02-18) or any day of the current week it should give the result 2015-02-15 which is a Sunday.
One of possible way to get the date on Sunday for the current week.
SELECT DATEADD(wk, DATEDIFF(wk, 0, GETDATE()), -1) AS SundayOfCurrentWeek
Using DATEPART
It depends on the settings of the server, but if it is configured to think that weeks start on Sundays, then this would work:
SELECT CAST(DATEADD(day, 1-DATEPART(weekday, GETDATE()), GETDATE()) AS date)
DATEPART with weekday parameter gives the day of the week. Then this number is subtracted from the current date and converted to date to remove the time part.
When datepart is week (wk, ww) or weekday (dw), the return value
depends on the value that is set by using SET DATEFIRST.
To see the current setting of SET DATEFIRST, use the ##DATEFIRST
function. The setting of SET DATEFIRST is set at execute or run time
and not at parse time.
So to play it safe make sure that DATEFIRST is set to 7 (default, U.S. English), which means Sunday.
Using DATEDIFF
Another variant uses DATEDIFF:
SELECT DATEADD(wk, DATEDIFF(wk, '20150104', GETDATE()), '20150104')
It would always return Sunday. Magic date 20150104 can be any date that is Sunday.
Specifying SET DATEFIRST has no effect on DATEDIFF. DATEDIFF always
uses Sunday as the first day of the week to ensure the function is
deterministic.
On the one hand it is good, because it is guaranteed to return Sunday, even if you forget to set DATEFIRST. But, on the other hand it is not flexible and doesn't suit those who consider, say, Monday to be the first day of the week (those that are not in US).
use this
Select dateadd(wk, datediff(wk, 0, getdate()) - 1, 0) as LastWeekStart
Select dateadd(wk, datediff(wk, 0, getdate()), 0) as ThisWeekStart
Select dateadd(wk, datediff(wk, 0, getdate()) + 1, 0) as NextWeekStart
for more information
GETDATE last month
I´m writing a query where i get the last month, but with the time in zeros (if today is 2013-05-21 then i want to get 2013-04-21 00:00:00.000).
So I tried:
select (dateadd(month,datediff(month,(0),getdate())-1,(0)));
But I get the first day of the previous month.
Then I tried:
select dateadd(month, -1, GETDATE());
I get the right day, but I also get the current time (2013-04-21 11:41:31.090), and I want the time in zeros.
So how should my query be in order to get something like: 2013-04-21 00:00:00.000
Thanks in advance.
In SQL Server 2008 there is the date data type, which has no time attached. You can thus remove the time portion quite easily simply by converting, then performing the DateAdd.
SELECT DateAdd(month, -1, Convert(date, GetDate()));
This will return a date data type. To force it to be datetime again, you can simply add one more Convert:
SELECT Convert(datetime, DateAdd(month, -1, Convert(date, GetDate())));
You may not need the explicit conversion to datetime, though.
Note: "One month ago from today" could be defined in many different ways. The way it works in SQL server is to return the day from the previous month that is the closest to the same day number as the current month. This means that the result of this expression when run on March 31 will be February 28. So, you may not get expected results in certain scenarios if you don't think clearly about the ramifications of this, such as if you performed the one-month calculation multiple times, expecting to get the same day in a different month (such as doing March -> February -> January).
See a live demo at SQL Fiddle
The demo shows the values and resulting data types of each expression.
Try like this..
select Cast(Cast(dateadd(month, -1, GETDATE()) as Date) as Datetime);
You can use this , it's pretty simple and worked for me -
SELECT SUM( amount ) AS total FROM expenses WHERE MONTH( date ) = MONTH( curdate() ) -1
To get the previous month start date and end date
DECLARE #StartDate date;
DECLARE #EndDate date;
select #StartDate= DATEADD(MONTH, DATEDIFF(MONTH, 0, GETDATE())-1, 0)
select #EndDate= DATEADD(MONTH, DATEDIFF(MONTH, -1, GETDATE())-1, -1)
Here are many common dates you may need to pull with logic
SELECT DATEADD(dd,DATEDIFF(dd,0,GETDATE()),0) -- Today Midnight
SELECT DATEADD(dd,DATEDIFF(dd,0,GETDATE()),-1) -- Yesterday Midnight
SELECT DATEADD(d,0,DATEADD(mm, DATEDIFF(m,0,GETDATE())-1,0)) -- First of Last Month
SELECT DATEADD(d,DATEPART(DD,GETDATE()-1),DATEADD(mm, DATEDIFF(m,0,GETDATE())-1,0)) -- Same Day Last Month
SELECT DATEADD(d,-1,DATEADD(mm, DATEDIFF(m,0,GETDATE()),0)) -- Last of Last Month
SELECT DATEADD(d,0,DATEADD(mm, DATEDIFF(m,0,GETDATE()),0)) -- First of this month
SELECT DATEADD(d,-1,DATEADD(mm, DATEDIFF(m,0,GETDATE())+1,0)) -- Last of this month
SELECT DATEADD(d,0,DATEADD(mm, DATEDIFF(m,0,GETDATE())+1,0)) -- First of next month
SELECT DATEADD(d,DATEPART(DD,GETDATE()-1),DATEADD(mm, DATEDIFF(m,0,GETDATE())+1,0)) -- Same Day Next Month
SELECT DATEADD(d,-1,DATEADD(mm, DATEDIFF(m,0,GETDATE())+2,0)) -- Last of next month
SELECT DATEADD(d,-1,DATEADD(mm, DATEDIFF(m,0,GETDATE())+12,0)) -- Last of prior month one year from now
SELECT DATEADD(dd,DATEDIFF(dd,0,DATEADD(DAY, 13-(##DATEFIRST + (DATEPART(WEEKDAY,GETDATE()) %7)), GETDATE())),0) -- Next Friday Midnight