I have my getdate() = '2022-03-21 09:24:34.313'
I'd like to build Start Month and End Month dates intervals with SQL language (SQL server) , with the following screen :
You can use EOMONTH function and DATEADD function to get the data you want.
But, the best approach would be to use a calendar table and map it against the current date and get the data you want.
DECLARE #DATE DATE = getdate()
SELECT DATEADD(DAY,1,EOMONTH(#DATE,-1)) AS MonthM_Start, EOMONTH(#DATE) AS MonthM_End,
DATEADD(DAY,1,EOMONTH(#DATE,-2)) AS MonthOneBack_Start, EOMONTH(#DATE,-1) AS MonthOneBack_End,
DATEADD(DAY,1,EOMONTH(#DATE,-3)) AS MonthTwoBack_Start, EOMONTH(#DATE,-2) AS MonthTwoBack_End,
DATEADD(DAY,1,EOMONTH(#DATE,-4)) AS MonthThreeBack_Start, EOMONTH(#DATE,-3) AS MonthThreeBack_End
MonthM_Start
MonthM_End
MonthOneBack_Start
MonthOneBack_End
MonthTwoBack_Start
MonthTwoBack_End
MonthThreeBack_Start
MonthThreeBack_End
2022-03-01
2022-03-31
2022-02-01
2022-02-28
2022-01-01
2022-01-31
2021-12-01
2021-12-31
You can use a recursive CTE to avoid having to hard-code an expression for each month boundary you need, making it very easy to handle fewer or more months by just changing a parameter.
Do you really need the end date for processing? Seems more appropriate for a label, since date/time types can vary - meaning the last day of the month at midnight isn't very useful if you're trying to pull any data from after midnight on the last day of the month.
This also shows how to display the data for each month even if there isn't any data in the table for that month.
DECLARE #number_of_months int = 4,
#today date = DATEFROMPARTS(YEAR(GETDATE()), MONTH(GETDATE()), 1);
;WITH m(s) AS
(
SELECT #today UNION ALL SELECT DATEADD(MONTH, -1, s) FROM m
WHERE s > DATEADD(MONTH, 1-#number_of_months, #today)
)
SELECT MonthStart = m.s, MonthEnd = EOMONTH(m.s)--, other cols/aggs
FROM m
--LEFT OUTER JOIN dbo.SourceTable AS t
--ON t.datetime_column >= m
--AND t.datetime_column < DATEADD(MONTH, 1, m);
Output (without the join):
MonthStart
MonthEnd
2022-03-01
2022-03-31
2022-02-01
2022-02-28
2022-01-01
2022-01-31
2021-12-01
2021-12-31
Example db<>fiddle
But, as mentioned in a comment, you could easily store this information in a calendar table, too, and just outer join to that:
SELECT c.TheFirstOfMonth, c.TheLastOfMonth --, other cols/aggs
FROM dbo.CalendarTable AS c
LEFT OUTER JOIN dbo.SourceTable AS t
ON t.datetime_column >= c.TheFirstOfMonth
AND t.datetime_column < c.TheFirstOfNextMonth
WHERE c.FirstOfMonth >= DATEADD(MONTH, -4, GETDATE())
AND c.FirstOfMonth < GETDATE();
I have a MS SQL table which contains a column containing month numbers (stored as an int).
I would like to get the number of minutes in the month using the month number.
After looking through Stack Overflow I came across the following code which I thought I could adapt to tell me the number of minutes by replacing the word DAY with MINUTE but to no avail.
DECLARE #ADate DATETIME
SET #ADate = GETDATE()
SELECT DAY(EOMONTH(#ADate)) AS DaysInMonth
The month is a 1 or 2 digit int depending on the month.
UPDATE:
I do have the year as well stored in another column.
Use DATEDIFF with MINUTE.
IF OBJECT_ID('tempdb..#Month') IS NOT NULL
DROP TABLE #Month
CREATE TABLE #Month (
MonthNumber INT,
Year INT)
INSERT INTO #Month (
MonthNumber,
Year)
VALUES
(1, 2018),
(2, 2018),
(2, 2016), -- Leap
(12, 2018)
SELECT
M.MonthNumber,
M.Year,
FirstDay = DATEFROMPARTS(M.Year, M.MonthNumber, 1),
FirstDayNextMonth = DATEADD(
MONTH,
1,
DATEFROMPARTS(M.Year, M.MonthNumber, 1)),
Minutes = DATEDIFF(
MINUTE,
DATEFROMPARTS(M.Year, M.MonthNumber, 1), -- FirstDay
DATEADD(MONTH, 1, DATEFROMPARTS(M.Year, M.MonthNumber, 1))) -- FirstDayNextMonth
FROM
#Month AS M
Results:
MonthNumber Year FirstDay FirstDayNextMonth Minutes
1 2018 2018-01-01 2018-02-01 44640
2 2018 2018-02-01 2018-03-01 40320
2 2016 2016-02-01 2016-03-01 41760
12 2018 2018-12-01 2019-01-01 44640
That's a deceptively tricky question. It's not only that the number of days in February changes in leap years. The number of hours in a date changes during the transition to Summer/Winter time. To calculate the correct difference in minutes between two dates you need to know the correct time offset as well.
Instead of looking up the offset for each date though, you can use the timezone. The timezone information contains all the rules needed to calculate the time offset for past and future dates, provided the OS's timezone information is kept up to date.
SQL Server 2016 and later support timezones with the AT TIME ZONE expression :
For example these queries:
select datetimefromparts(2018,3,25,0,0,0,0) at TIME ZONE 'E. Europe Standard Time'
select datetimefromparts(2018,3,26,0,0,0,0) at TIME ZONE 'E. Europe Standard Time'
Return
2018-03-25 00:00:00.000 +02:00
2018-03-26 00:00:00.000 +03:00
March 25 had 23 hours instead of 24. The difference of the two days in minutes is 1380 instead of the usual 1440:
select datediff(mi,
datetimefromparts(2018,3,25,0,0,0,0) at TIME ZONE 'E. Europe Standard Time',
datetimefromparts(2018,3,26,0,0,0,0) at TIME ZONE 'E. Europe Standard Time')
-----
1380
You can pass the timezone name as a parameter :
declare #mytimezone nvarchar(40)='E. Europe Standard Time'
select datetimefromparts(2018,3,25,0,0,0,0) at TIME ZONE #mytimezone
select datetimefromparts(2018,3,26,0,0,0,0) at TIME ZONE #mytimezone
To calculate the correct number of minutes in a month, you could use the difference in minutes between the current and the next month's 1st day on a specific timezone with AT TIME ZONE. This would ensure the correct offsets are used.
A query that calculates the difference in minutes correctly could look like this :
declare #mytimezone nvarchar(40)='E. Europe Standard Time'
select year,month,
datediff(mi,
datetimefromparts(M.Year,M.Month,1,0,0,0,0) at TIME ZONE #mytimezone,
dateadd(MONTH,1,datetimefromparts(M.Year,M.Month,1,0,0,0,0)) at TIME ZONE #mytimezone
)
from (values
(2016,2),
(2018,1),
(2018,2),
(2018,3),
(2018,4),
(2018,5),
(2018,6),
(2018,7),
(2018,8),
(2018,9),
(2018,10),
(2018,11),
(2018,12) ) M(Year,Month)
You can create a function to calculate the minutes. Either a scalar function that can be used in the SELECT clause :
CREATE FUNCTION MinutesInMonth
(
#Year INT,
#Month INT,
#timezone nvarchar(40)
)
RETURNS INT
AS
BEGIN
Return
datediff(mi,
datetimefromparts(#year,#month,1,0,0,0,0) at TIME ZONE #timezone,
dateadd(MONTH,1,datetimefromparts(#year,#month,1,0,0,0,0)) at TIME ZONE #timezone
)
END
...
select year,month,dbo.MinutesInMonth(year,month,#mytimezone)
from (values
(2018,10),
(2018,11),
(2018,12)
) M(Year,Month)
Or an inline table function that can be used in the FROM clause and gets inlined in the query itself :
CREATE FUNCTION MinutesInMonth
(
#Year INT,
#Month INT,
#timezone nvarchar(40)
)
RETURNS TABLE
RETURN (select datediff(mi,
datetimefromparts(#year,#month,1,0,0,0,0) at TIME ZONE #timezone,
dateadd(MONTH,1,datetimefromparts(#year,#month,1,0,0,0,0))
At TIME ZONE #timezone) as Minutes
)
select year,month,Minutes
from (values
(2016,2),
(2018,1),
(2018,2),
(2018,3),
(2018,4),
(2018,5),
(2018,6),
(2018,7),
(2018,8),
(2018,9),
(2018,10),
(2018,11),
(2018,12)
) M(Year,Month)
cross apply dbo.MinutesInMonth(year,month,#mytimezone)
Try this out :-
DECLARE #Year INT = 2016, #Month INT = 2;
DECLARE #Date DATE = DATETIMEFROMPARTS(#Year,#Month, 1, 0, 0, 0, 0) AT TIME ZONE 'Sri Lanka Standard Time';
SELECT DAY(EOMONTH(#Date)) * 24 * 60;
Alternatively you can create a scalar function as follows:-
CREATE FUNCTION GetMinutesOfMonth
(
#Year INT,
#Month INT,
#TimeZone VARCHAR(50) = NULL
)
RETURNS INT
AS
BEGIN
DECLARE #MinutesOfMonth INT, #CurrentTimeZone VARCHAR(50);
EXEC MASTER.dbo.xp_regread 'HKEY_LOCAL_MACHINE',
'SYSTEM\CurrentControlSet\Control\TimeZoneInformation',
'TimeZoneKeyName',#CurrentTimeZone OUT;
SET #TimeZone = ISNULL((SELECT [name] FROM SYS.time_zone_info WHERE [name] = #TimeZone), #CurrentTimeZone)
DECLARE #Date DATE = DATETIMEFROMPARTS(#Year, #Month, 1, 0, 0, 0, 0) AT TIME ZONE #TimeZone;
SELECT #MinutesOfMonth = DAY(EOMONTH(#Date)) * 24 * 60;
RETURN #MinutesOfMonth;
END
GO
and use it as:-
DECLARE #Year INT = 2016, #Month INT = 2;
SELECT dbo.GetMinutesOfMonth(#Year, #Month, 'Sri Lanka Standard Time');
If the timezone of the current machine is same as the dates being considered, you may use the function as follows:-
DECLARE #Year INT = 2016, #Month INT = 2;
SELECT dbo.GetMinutesOfMonth(#Year, #Month, DEFAULT);
I'm trying to query our database to find all records that were created between 6am yesterday and 6am today. This will be run in a report at any point during the day so set times/dates are useless.
I have this so far:-
SELECT * FROM DaySummaryDetail DSD
WHERE DSD.FromDateTime BETWEEN DATEADD(DAY, -1, GetDate())
AND DATEADD(Day, 1, GetDate())
But obviously this only works for 24 hours ago from right now until right now. I can't figure out how to apply a time as well as date.
Every example I find online seems slightly different and uses set dates/times ie, >= 20/02/2015 06:00:00.
I normally use Oracle SQL which would simply work using this:-
ptt.mod_date_time >= TRUNC (SYSDATE - 1) - 2 / 24
AND ptt.mod_date_time <= TRUNC (SYSDATE - 1) + 22 / 24
This would return results from 10pm to 10pm but the format appears totally different in SQL Server.
You can get the datetime values you are after by doing the following:
SELECT DATEADD(HOUR,6,CONVERT(DATETIME, CONVERT(DATE ,GETDATE()))) Today6AM,
DATEADD(HOUR,-18,CONVERT(DATETIME, CONVERT(DATE ,GETDATE()))) Yesterday6AM
By doing this: CONVERT(DATE ,GETDATE()) you are stripping off the time portion of today's date. Converting it back to datetime gives you midnight for today.
The query adds 6 hours to midnight of the current day for 6am today and subtracts 18 hours from midnight of the current day to give you 6am on the previous day.
Output:
Today6AM Yesterday6AM
================================================
2015-02-20 06:00:00.000 2015-02-19 06:00:00.000
So adding that to your query:
SELECT *
FROM DaySummaryDetail DSD
WHERE DSD.FromDateTime
BETWEEN DATEADD(HOUR,-18,CONVERT(DATETIME, CONVERT(DATE ,GETDATE())))
AND DATEADD(HOUR,6,CONVERT(DATETIME, CONVERT(DATE ,GETDATE())))
DECLARE #StartTimestamp datetime
DECLARE #EndTimestamp datetime
DECLARE #HourPartOfSearchRange nvarchar(6)
SET #HourPartOfSearchRange = ' 06:30'
SET #StartTimestamp =
CAST((CONVERT(varchar(11), DATEADD(DAY,-1,#CurrentUTCDateTime), 106) + #HourPartOfSearchRange) AS datetime)
SET #EndTimestamp =
CAST((CONVERT(varchar(11), #CurrentUTCDateTime, 106) + #HourPartOfSearchRange) AS datetime)
SELECT * FROM dbo.Test Where Timestamp Between #StartTimestamp AND #EndTimestamp
today 6am is
dateadd(hour,6,cast(cast(getdate() as date) as datetime))
cast(getdate() as date) truncates the timepart, cast it back as datetime because dateadd won't add hours otherwise and add 6hours
One solution would be like so:
select *
from DaySummaryDetail DSD
where DSD.FromDateTime between cast(cast(cast(getdate()-1 as date) as varchar(30)) + ' 06:00:00.000' as datetime)
and cast(cast(cast(getdate() as date) as varchar(30)) + ' 06:00:00.000' as datetime)
This should help ...
SELECT DATEADD( hour, 6, CAST(CAST(GETDATE(), AS Date) AS DateTime) ) AS 'Today#6am'
SELECT DATEADD( hour, 6, CAST(CAST(GETDATE()-1, AS Date) AS DateTime) ) AS 'Yesterday#6am'
In SQL Server 2012 you can use SMALLDATETIMEFROMPARTS to construct a datetime value that is today at 6am like this:
SMALLDATETIMEFROMPARTS(YEAR(GETDATE()), MONTH(GETDATE()), DAY(GETDATE()), 6, 0)
Output: 2015-02-20 06:00:00
then you can use the above expression in place of GETDATE() in the WHERE clause:
DECLARE #TodayAt6AM DATETIME = SMALLDATETIMEFROMPARTS(YEAR(GETDATE()),
MONTH(GETDATE()),
DAY(GETDATE()),
6,
0)
SELECT *
FROM DaySummaryDetail DSD
WHERE DSD.FromDateTime BETWEEN DATEADD(DAY, -1, #TodayAt6AM) AND
DATEADD(Day, 1, #TodayAt6AM)