SQL 1st of the month to the end of the month - sql

I have an #StartDate and #EndDate.
I need the #StartDate to be the day the query is ran(which will always be the first of the month) and the #EndDate to be exaclty at the end of the month no matter if the month is 30 or 31 days, etc.

A worked example:
DECLARE #StartDate DATETIME, #EndDate DATETIME
SET #StartDate = '2010-01-01 00:00:00.000'
SET #EndDate = DATEADD(m, 1, #StartDate)
SELECT #StartDate, #EndDate - 1
Basically you want to take the start date, add one month (that's what the DATEADD is doing), and then deduct one day.
The output from that query is:
StartOfMonth EndOfMonth
----------------------- -----------------------
2010-01-01 00:00:00.000 2010-01-31 00:00:00.000

Related

Setting Start and End dates

I have a simple query that needs to pull in all employees that were hired in the past 10 days, up until end of day yesterday at 11:59, excluding the current date, using Start & End date variables.
I know there's MANY ways of setting this date range.
What is the most efficient SQL server expression that can give you the start and end dates for the past 10 days, excluding the current date?
DECLARE #StartDate = ???
DECLARE #EndDate = ???
SELECT #StartDate, #EndDate
Desired outcome:
2020-04-02 00:00:00.000, 2020-04-11 23:59:59.000
The :59 schemes are a bad idea. Comparisons should be less than the (start of) next day in order to include the full previous day.
SELECT #StartDate = DATEADD(dd,-10,CAST(getdate() AS DATE)), #EndDate = CAST(getdate() AS DATE)
However if you still want it:
SELECT #StartDate = DATEADD(dd,-10,CAST(getdate() AS DATE)), #EndDate = DATEADD(ss,-1,CAST(CAST(getdate() AS DATE) AS DATETIME))
Hope this Code Works fine for your Case:
SELECT
#Start=CAST(CAST(GETDATE()-10 AS DATE) AS DATETIME),
#End=CONCAT(CAST(GETDATE()-1 AS DATE),' 23:59:59.000') ---

How to account for 31 days using SQL

I am trying to generate rolling data for month. However, I am having an issue where the query doesnt generate any data when the month has 31 days.
I am defining my days here:
declare #today datetime
set #today = getdate()
declare #enddate datetime
set #enddate = #today
declare #begindate datetime
set #begindate = dateadd(mm, datediff(m,0,#today),0)
Then I am calling the days in my query using:
PURCHDATE BETWEEN #begindate AND #enddate
I can see my issue where my begin date is first day of the month and end date is today. How can I make this work for 31 day months?
You could do this
WHERE YEAR(PURCHDATE) = YEAR(GETDATE()) AND MONTH(PURCHDATE) = MONTH(GETDATE())
I would recommend:
PURCHDATE >= CONVERT(date, DATEADD(day, 1 - day(getdate()), getdate())) AND
PURCHDATE < CONVERT(date, DATEADD(day, 1, EOMONTH(getdate())))

tsql: How to retrieve the last date of each month between given date range

I have two date for example 08/08/2013 and 11/11/2013 and I need last date of each month starting from August to November in a table so that i can iterate over the table to pick those dates individually.
I know how to pick last date for any month but i am stucked with a date range.
kindly help, it will be highly appreciated.
Note : I am using Sql 2008 and date rang could be 1 month , 2 month or 6 month or a year or max too..
You can use CTE for getting all last days of the month within the defined range
Declare #Start datetime
Declare #End datetime
Select #Start = '20130808'
Select #End = '20131111'
;With CTE as
(
Select #Start as Date,Case When DatePart(mm,#Start)<>DatePart(mm,#Start+1) then 1 else 0 end as [Last]
UNION ALL
Select Date+1,Case When DatePart(mm,Date+1)<>DatePart(mm,Date+2) then 1 else 0 end from CTE
Where Date<#End
)
Select * from CTE
where [Last]=1 OPTION ( MAXRECURSION 0 )
DECLARE #tmpTable table (LastDates DATE);
DECLARE #startDate DATE = '01/01/2012'; --1 Jan 2012
DECLARE #endDate DATE = '05/31/2012'; --31 May 2012
DECLARE #tmpEndDate DATE;
SET #startDate = DATEADD(s,-1,DATEADD(mm, DATEDIFF(m,0,#startDate)+1,1));
SET #tmpEndDate = DATEADD(DAY, 1, #endDate);
WHILE (#startDate <= #tmpEndDate)
BEGIN
INSERT INTO #tmpTable (LastDates) values (DATEADD(DAY, -1, #startDate));
SET #startDate = DATEADD(MONTH, 1, #startDate);
END
SELECT [LastDates] FROM #tmpTable;
Output:
Example: 1
#startDate DATE = '01/01/2012'; --1 Jan 2012
#endDate DATE = '05/31/2012'; --31 May 2012
LastDates
----------
2012-01-31
2012-02-29
2012-03-31
2012-04-30
2012-05-31
Example: 2
#startDate DATE = '11/01/2011'; --1 Nov 2011
#endDate DATE = '03/13/2012'; --13 Mar 2012
LastDates
----------
2011-11-30
2011-12-31
2012-01-31
2012-02-29
I've created a table variable, filled it with all days between #startDate and #endDate and searched for max date in the month.
declare #tmpTable table (dates date)
declare #startDate date = '08/08/2013'
declare #endDate date = '11/11/2013'
while #startDate <= #endDate
begin
insert into #tmpTable (dates) values (#startDate)
set #startDate = DATEADD(DAY, 1, #startDate)
end
select max(dates) as [Last day] from #tmpTable as o
group by datepart(YEAR, dates), datepart(MONTH, dates)
Results:
Last day
2013-08-31
2013-09-30
2013-10-31
2013-11-11
To also get last day of November this can be used before loop:
set #endDate = DATEADD(day, -1, DATEADD(month, DATEDIFF(month, 0, #endDate) + 1, 0))
Following script demonstrates the script to find last day of previous, current and next month.
----Last Day of Previous Month
SELECT DATEADD(s,-1,DATEADD(mm, DATEDIFF(m,0,GETDATE()),0))
LastDay_PreviousMonth
----Last Day of Current Month
SELECT DATEADD(s,-1,DATEADD(mm, DATEDIFF(m,0,GETDATE())+1,0))
LastDay_CurrentMonth
----Last Day of Next Month
SELECT DATEADD(s,-1,DATEADD(mm, DATEDIFF(m,0,GETDATE())+2,0))
LastDay_NextMonth
If you want to find last day of month of any day specified use following script.
--Last Day of Any Month and Year
DECLARE #dtDate DATETIME
SET #dtDate = '8/18/2007'
SELECT DATEADD(s,-1,DATEADD(mm, DATEDIFF(m,0,#dtDate)+1,0))
LastDay_AnyMonth
ResultSet:
LastDay_AnyMonth
Source - SQL Server Central.
You can use a recursive CTE to do this, note the MAXRECURSION OPTION prevents an infinite loop:
DECLARE #StartDate DATE = '2013-08-08'
DECLARE #EndDate DATE = '2013-11-11'
;WITH dateCTE
AS
(
SELECT CAST(DATEADD(M, 1,DATEADD(d, DAY(#StartDate) * -1, #StartDate)) AS DATE) EndOFMonth
UNION ALL
SELECT CAST(DATEADD(M, 2,DATEADD(d, DAY(EndOFMonth) * -1, EndOFMonth)) AS DATE)
FROM dateCTE
WHERE EndOFMonth < DATEADD(d, DAY(#EndDate) * -1, #EndDate)
)
SELECT *
FROM dateCTE
OPTION (MAXRECURSION 30);
This returns
EndOFMonth
----------
2013-08-31
2013-09-30
2013-10-31
try this
the last row(where) is optional for date filtering
declare #table table
(
thisdate date
)
insert into #table values ('12/01/2013'),('05/06/2013'),('04/29/2013'),('02/20/2013')
select *,DATEADD(s,-1,DATEADD(mm, DATEDIFF(m,0,thisdate)+1,0))
LastDay from #table
where thisdate between 'givendate' and 'givendate'
The Example Below is for all dates
thisdate lastday
2013-12-01 2013-12-31 23:59:59.000
2013-05-06 2013-05-31 23:59:59.000
2013-04-29 2013-04-30 23:59:59.000
2013-02-20 2013-02-28 23:59:59.000
The following CTE gives you the last day of every month from February 1900 until the middle of the 26th century (on my machine):
;with LastDaysOfMonths as (
select DATEADD(month,
ROW_NUMBER() OVER (ORDER BY so.object_id),
'19000131') as Dt
from sys.objects so,sys.objects so1
)
select * from LastDaysOfMonths
It should be easy enough to use it as part of a larger query or to filter it down to just the dates you want. You can adjust the range of years as needed by changing the constant 19000131. The only important thing to do is make sure that you use a month that has 31 days in it and always have the constant be for day 31.
No need to use a common table expression or anything like that - this simple query will do it:
SELECT DATEADD(d, -1, DATEADD(mm, DATEDIFF(m, 0, DATEADD(m, number, '2013-08-08')) + 1, 0)) AS EndOfMonth
FROM master.dbo.spt_values
WHERE 'P' = type
AND DATEADD(m, number, '2013-08-08') < '2013-11-11';
Although the question is about the last day which #bummi has already answered.
But here is the solution for the first date which might be helpful for someone.
Get the first dates of all the months in-between the #FromDate and #ToDate.
DECLARE #FromDate DATETIME = '2019-08-13'
DECLARE #ToDate DATETIME = '2019-11-25'
;WITH CTE
AS
(
SELECT DATEADD(DAY, -(DAY(#FromDate) - 1), #FromDate) AS FirstDateOfMonth
UNION ALL
SELECT DATEADD(MONTH, 1, FirstDateOfMonth)
FROM CTE
WHERE FirstDateOfMonth < DATEADD(DAY, -(DAY(#ToDate) - 1), #ToDate)
)
SELECT * FROM CTE
Here is the result
--Result
2019-08-01 00:00:00.000
2019-09-01 00:00:00.000
2019-10-01 00:00:00.000
2019-11-01 00:00:00.000

How to get date to be the same day and month from one variable but in the year based on another variable?

I am trying to get the specific day of a Year.
Here's what I have tried till now:-
-- Declare few variables
DECLARE #Currentdate AS DATETIME
DECLARE #DueDate AS DATETIME
DECLARE #NewDate AS DATETIME
-- Set the variables properly, just for testing
SET #Currentdate = GETDATE()
SET #DueDate = DATEADD(MONTH, 2, DATEADD(YEAR, 1, #Currentdate))
-- Check the output
SELECT #Currentdate -- 2013-09-30 00:00:00.000
SELECT #DueDate -- 2014-11-30 00:00:00.000
So, I want to get the #NewDate based on the #Currentdate year.
For this I tried:-
SELECT #NewDate = DATEADD(DAY, DAY(DATEDIFF(day, 1, #DueDate)), DATEADD(MONTH, DATEDIFF(MONTH, 0, #Currentdate), 0))
SELECT #NewDate -- 2013-09-30 00:00:00.000
But it didn't worked. :(
My expected result is like:
-- 2013-11-30 00:00:00.000
-- Having the due date month and date same, but the year as current date one.
Any help is appreciated!
UPDATE
Sorry for all the confusion I have created. My question in simple words is:-
I want to get the a new date variable having the date and the month same as #DueDate variable but the year as given in the #Currentdate variable.
I hope that would clear things up a bit.
If the question is "given I have a particular datetime value in one variable, can I set another variable to be for the same day and month but in the current year" then the answer would be:
declare #DueDate datetime
declare #NewDate datetime
set #DueDate = '20141130'
--Need to set #NewDate to the same month and day in the current year
set #NewDate = DATEADD(year,
--Here's how you work out the offset
DATEPART(year,CURRENT_TIMESTAMP) - DATEPART(year,#DueDate),
#DueDate)
select #DueDate,#NewDate
I want to get the a new date variable having the date and the month same as #DueDate variable but the year as given in the #Currentdate variable.
Well, that's simply the above query with a single tweak:
set #NewDate = DATEADD(year,
--Here's how you work out the offset
DATEPART(year,#Currentdate) - DATEPART(year,#DueDate),
#DueDate)
Try this instead ?
DECLARE #Currentdate AS DATETIME
DECLARE #DueDate AS DATETIME
-- Set the variables properly, just for testing
SET #Currentdate = GETDATE()
SET #DueDate = DATEADD(MONTH, 2, DATEADD(YEAR, 1,
DateAdd(day, datediff(day, 0, #currentDate), 0)))
-- Check the output
SELECT #Currentdate -- 2013-09-30 18:32:35.310
SELECT #DueDate
Using DateAdd(day, datediff(day, 0, #DateTime), 0) strips off the time portion. You should also check out this SO Question/answer.
Try this one:
CAST(CAST( -- cast INT to VARCHAR and then to DATE
YEAR(GETDATE()) * 10000 + MONTH(#DueDate) * 100 + DAY(#DueDate) -- convert current year + #DueDate's month and year parts to YYYYMMDD integer representation
+ CASE -- change 29th of February to 28th if current year is a non-leap year
WHEN MONTH(#DueDate) = 2 AND DAY(#DueDate) = 29 AND ((YEAR(GETDATE()) % 4 = 0 AND YEAR(GETDATE()) % 100 <> 0) OR YEAR(GETDATE()) % 400 = 0) THEN 0
ELSE -1
END
AS VARCHAR(8)) AS DATE)

ADD time 23:59:59.999 to end date for between

I have been having an issue with using the following:
Column_Name BETWEEN #StartDate AND #EndDate.
This is because the #EndDate = 00:00:00.000 for the time, which doesn't pick up all the values for that day.
How would I convert the #EndDate (Always 00:00:00.000) to always be Date + 23:59:59.999?
One option that avoids needing to add EndDate + 23:59:59.999 is to not use the between comparison and instead use column_name >= #StartDate and column_name < #EndDate +1
Please note the accuracy and rounding of the DATETIME type in SQL Server 2005:
datetime values are rounded to increments of .000, .003, or .007 seconds
SQL Server 2008 introduced the DATETIME2 type which has an accuracy of 100 nanoseconds. So in SQL Server 2008 you could do:
DECLARE #d DATETIME = '2011-10-07 00:00:00.000'
SELECT DATEADD(MS, -1, DATEADD(D, 1, CONVERT(DATETIME2, #d)))
Alternatively you may want to avoid the BETWEEN operator in this case:
#StartDate <= Column_Name AND Column_Name < DATEADD(D, 1, #EndDate)
Since the advent of datetime2 datatype, I have been struggling with this problem. To calculate the end of day as a datetime2 datatype I add the number of seconds in a day to the =date= then subtract 100 nanoseconds. Voila:
declare #bod datetime2
declare #eod datetime2
set #bod = cast (GETDATE() as DATE)
set #eod = DATEADD(ns, -100, DATEADD(s, 86400, #bod))
print #bod
print #eod
-- answer:
2013-12-01 00:00:00.0000000
2013-12-01 23:59:59.9999999
Now I'm off to datetimeoffset data type.
You can change the time in a date like this (I'm using getdate() as an example):
select cast(convert(char(8), getdate(), 112) + ' 23:59:59.99' as datetime)
Explanation:
convert(char(8), getdate(), 112) converts the date to yyyymmdd format (as string).
Then you can just append the desired time, and convert the whole string to datetime again.
EDIT:
It slows the performance when you do the casting on a database column, yes.
But he has a datetime variable and he just uses the casting to change the time in the variable once
--> I see no performance issue if he uses my code to change his #EndDate variable.
Valid point, however. Casting is not a good solution in all situations.
You could also do this:
select #endDate = dateadd(ms,-3,dateadd(day,1,DATEADD(dd, DATEDIFF(dd,0,#endDate), 0)))
when #endDate is '5/3/2013'
--Execution / post date is 1 Feb. 2020
-- sql server this month start and end
select DATEADD(month, DATEDIFF(month, 0, getdate()), 0) -- 2020-02-01 00:00:00.000
select DATEADD(second,-1, datediff(day,0,EOMONTH(getdate()))+1) -- 2020-02-29 23:59:59.000
-- sql server this day start and end
select DATEADD(day, DATEDIFF(day, 0, getdate()), 0) -- 2020-02-01 00:00:00.000
select DATEADD(second,-1, datediff(dd,0,getdate())+1) -- 2020-02-01 23:59:59.000
-- sql server last 30 days start and end
select DATEADD(day, -30, DATEDIFF(day, 0, getdate())) -- 2020-01-02 00:00:00.000
select DATEADD(second,-1, datediff(dd,0,getdate())+1) -- 2020-02-01 23:59:59.000
You can use between if your end date is set to 00:00:00 of the next day:
ColumnName between #StartDate and convert(datetime, convert(date, #EndDate + 1))
This converts the next day to a date, which removes the hours information, then you convert it back to a datetime which adds default hour information: 00:00:00.
You can use any variant of the date from parts functions. For example, let's use DATETIMEFROMPARTS:
DECLARE #d DATETIME2(0) = '2011-10-07 04:32:12.000';
SELECT DATETIMEFROMPARTS(YEAR(#d), MONTH(#d), DAY(#d), 23, 59, 59, 0);
-- 2011-10-07 23:59:59
I first convert the original datetime to begin of the day, then add hours and seconds to it:
DECLARE #start DATETIME, #end DATETIME
SET #start = DATEADD(DAY, DATEDIFF(DAY, 0, GETDATE()), 0)
SET #end = DATEADD(HOUR, 23, DATEADD(n, 59, #start))
PRINT #start
PRINT #end
Oct 27 2017 12:00AM
Oct 27 2017 11:59PM