How to subtract one month from a Date Column - sql

I know about Dateadd and datediff, but I cannot find any information how to use these functions on an actual date column rather than something like today's date with SQL Server.
Say I have the following Column
Dated
06/30/2015
07/31/2015
Now I want to add the following derived column that subtracts one month from every row in the Dated column.
Dated Subtracted
06/30/2015 05/31/2015
07/31/2015 06/30/2015
Thank you

The short answer: I suspect this is what you want:
dateadd(day, -datepart(day, Dated), Dated)
However, if you want "regular" subtract one month behavior in tandem with sticking to the end of month, having June 30 fall back to May 31 is slightly trickier. There's a discrepancy between the title or your question and the example where it appears you want the final day of month to stay anchored. It would be helpful for you to clarify this.
dateadd(month, -1, ...) doesn't handle that when the previous month has more days than the starting month although it works the other way around. If that's truly what you need I think this should handle it:
case
when datediff(month, Dated, dateadd(day, 1, Dated)) = 1
then dateadd(day, -datepart(day, Dated), Dated)
else dateadd(month, -1, Dated)
end
There's also a flavor of several date functions in that expression and a sense of how this date stuff can get complicated. The condition in the when looks to see if Dated is the last day of the month by checking that the following day is in a different calendar month. If so we extract the day of month and subtract that many days to jump back to the last day of the previous month. (Months start at one not zero. So for example, counting backward 17 days from the 17th lands in the month before.) Otherwise it uses regular dateadd(month, -1, ...) calculations to jump backward to the same day of month.
Of course if all your dates fall on the end of the month then this simple version will be adequate by itself because it always returns the last day of the previous month (regardless of where it falls in the starting month):
dateadd(day, -datepart(day, Dated), Dated) /* refer back to the top */
dateadd(day, -day(Dated), Dated) /* same thing */
And just for fun and practice with date expressions, another approach is that you could start on a known month with 31 days and calculate relative to that:
dateadd(month, datediff(month, '20151231', Dated) - 1, '20151231')
This finds the number of months between your date and a reference date. It works for all dates since it doesn't matter whether the difference is positive or negative. So then subtracting one from that difference and adding that many months to the reference point is the result you want.
People will come up with some pretty crazy stuff and I'm often amazed (for differing reasons) at some of the variations I see. chancrovsky's answer is a good example for closer examination:
dateadd(month, datediff(month, -1, Dated) - 1, -1)
It relies on the fact that date -1, when treated as implicitly converted to datetime, is the day before January 1, 1900, which does happen to be a month of 31 days as required. (Note that the - 1 in the middle is regular arithmetic and not a date value.) I think most people would advise you to be careful with that one as I'm not sure that it is guaranteed to be portable when Microsoft deprecates features in the future.

Why don't you just get the last day of the previous month? If this solve your problem, here's the sql server syntax, just replace the variable #yourDate with your column name.
DECLARE #yourDate DATE = '20160229'
select DATEADD(MONTH, DATEDIFF(MONTH, -1, #yourDate)-1, -1)

This also works if you're looking to find dates exactly 6 months behind.
Example:
DATEADD(DAY, DATEDIFF(DAY, -1, dates)-184, -31)

Related

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.

Calculating orders placed on the end of the month

I'm currently studying SQL Server using the book Ben-Gan, Itzik. T-SQL Fundamentals. Below is a query used to select order placed at end of the month. (I know that function EOMONTH() can also be used)
SELECT orderid, orderdate, custid, empid
FROM Sales.Orders
WHERE orderdate = DATEADD( month, DATEDIFF( month, '18991231', orderdate), '18991231');
The author's explanation is:
This expression first calculates the difference in terms of whole
months between an anchor last day of some month (December 31, 1899, in
this case) and the specified date. Call this difference diff. By
adding diff months to the anchor date, you get the last day of the
target month.
However, I'm still a bit confused as to how it actually works. Would someone kindly explain it?
That seems like a rather arcane way to do this. What the code is doing is calculating the number of months since the last day of some month. Then, it adds this number of months to that date. Because of the rules of dateadd(), the month remains the last date.
However, I prefer a simpler method:
where day(dateadd(day, 1, orderdate)) = 1
I find this much clearer.
select DATEDIFF(MONTH, '20160131', '20160201')
give us 1 month and
SELECT DATEADD(month, 1, '20160131')
give us 2016-02-29 00:00:00.000
that's ok
I tried out the query myself and seem to have got the hang of it. here is what i wrote just in case anyone else is interested
SELECT DATEADD(month, DATEDIFF(MONTH, '20160131', '20160201'), '20160131');
result:
2016-02-29 00:00:00.000
so my interpretation is that adding one or more "month" to a particular date in which the last date of the month is 31 will always return a date in which the date is the last day of the month. if this sentence makes any sense...

SQL Query Subtract 1 month

I need to query SQL for data that falls into a last month date. I thought I was using the correct query logic but I get no results and I know there are results. The snippet of code is this:
MONTH(n.JOIN_DATE) = DATEADD(month, - 1, GETDATE())
This is giving me no results and I need to get anyone who has a join date of last month. What am I missing?
Use this:
MONTH(n.JOIN_DATE) = MONTH(DATEADD(month, - 1, GETDATE()))
You need to compare apples with apples, so compare the numerical month on both sides of the equation.
Massive credit to #PaulL for figuring this out before I did.
Update:
As #DasBlinkenLight and Matt pointed out, just comparing by month leaves the door open for multiple years to be returned. One possible fix would be to also compare the years, e.g.
WHERE MONTH(n.JOIN_DATE) = MONTH(DATEADD(month, - 1, GETDATE())) AND
YEAR(n.JOIN_DATE) = YEAR(DATEADD(month, - 1, GETDATE()))
MONTH(...) produces a month number. You should not compare it to the result returned by DATEADD, which is actually a date.
If you are looking for everyone who has joined less than a month ago, you can do it like this:
WHERE DATEADD(month, 1, n.JOIN_DATE) > GETDATE()
This takes into account the year and the day as well, not only the month.
If you are looking for everyone who joined last month, no matter on what day, you can use a more complex condition:
WHERE MONTH(DATEADD(month, -1, GETDATE()) = MONTH(n.JOIN_DATE)
AND YEAR (DATEADD(month, -1, GETDATE()) = YEAR (n.JOIN_DATE)
The second condition is necessary to avoid confusion between members joining last month and members joining on the same month one or more years ago.
MONTH(n.JOIN_DATE) returns a numeric which indicate the month in date m.JOIN_DATE
DATEADD(month, - 1, GETDATE()) returns a date which indicate date in last month.
So, you can use this instead :
MONTH(n.JOIN_DATE)= MONTH(DATEADD(month, - 1, GETDATE()))
OR
n.JOIN_DATE = DATEADD(month, - 1, GETDATE())
MONTH(n.JOIN_DATE) will only return the numerical value of the month (e.g.: 11 or 5).
DATEADD(MONTH, -1, GETDATE()) will simply subtract one month from the current date. It is still in a DATETIME format.
You may be looking for:
MONTH(n.JOIN_DATE) = MONTH(DATEADD(MONTH, -1, GETDATE()))
SELECT
DATEFROMPARTS(YEAR(DATEADD(month,-1,GETDATE())),MONTH(DATEADD(month,-1,GETDATE())),1) AS StartOfLastMonth
,DATEADD(day,-1,(DATEFROMPARTS(YEAR(GETDATE()),MONTH(GETDATE()),1))) AS EndOfLastMonthAsDate
,DATEADD(day,-3,CAST(DATEFROMPARTS(YEAR(GETDATE()),MONTH(GETDATE()),1) AS DATETIME)) AS EndOfLastMonthMidngith
,CAST(DATEADD(month,-1,GETDATE()) AS DATE) AS OneMonthAgoStrartOfDay
,CAST(GETDATE() AS DATE) AS StartOfToday
,DATEADD(MS,-3,CAST(CAST(GETDATE() AS DATE) AS DATETIME)) AS MidnightLastNight
Okay As people have definitely illustrated there are a lot of different answers to your question and all are built upon similar premise. using DATEADD() with a negative number to go back a month. Or to compare month and year to see if they are the same. The former being geared at 1 month ago to today and the later being last month.
All of the answers so far, expect #DasBlinkenLight and #TimBiegeleisen, fail to take into account TIME component of your column and the GETDATE() function if you have one. If your column is DATETIME you will need to take that into account. The above SELECT query that will arm you with some ways of getting to different dates that i suspect will meet your needs.
As far as using BETWEEN with dates be careful! because the values you put in are inclusive so if you put GETDATE() on the right side of the between statement you will get today's results too but you might really want UP TO to today in which case you should change your right side argument. Also I am not sure about Oracle, mysql, etc. but Micrsofot SQL-Server is accurate to .003 milliseconds. So if you really want to look at midnight of a date you should look at 23:59:59.997 because .998 and .999 will round up to the next day.
Also to further simplify if you don't want time components you can also cast your column to DATE and it essentially drops off the time and the BETWEEN because a little clearer too, e.g. CAST(n.JOIN_DATE AS DATE)
There are definitely other questions on this subject on stackoverflow I encourage you to research.
I knew this was a simple one and I was missing something to it. My code was wrong based upon the many responses I received on this and I was comparing apples to oranges and not apples to apples. Once I added the Month() around the dateadd function, it worked.
The reply to this answer are correct. But in the light of best practice writing your query this way will make it less SARGABLE, hence making it ignore indexes if you have one. It might be better to write it as
WHERE n.JOIN_DATE between DATEADD(MONTH, DATEDIFF(MONTH, 0, GETDATE())-1, 0) AND DATEADD(MONTH, DATEDIFF(MONTH, -1, GETDATE())-1, -1)
based on the comment below I have modified the query. I guess I did not read the question in depth.

Get Week Before Last

Hi I am trying to pull data which will excluded the previous 2 weeks and include the 2 weeks beofre that.
so for example if today is the 31st of the month i want to exclude all data between the 15th and 31st and want to see only the 1st to the 15th
AND E.EventCreatedDateD between DATEADD(day,-14,GETDATE()) AND DATEADD(day,-31,GETDATE())
You are getting 0 values from your statement because the dates are backwards. Put the earlier date first:
AND E.EventCreatedDateD between DATEADD(day, -31, GETDATE()) AND DATEADD(day, -14, GETDATE())
The order of the expressions for the between is important. The lower value always needs to be first and the higher value second.

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.