I have been using this format for years to truncate dates and times
SELECT DATEADD(HOUR, DATEDIFF(HOUR, 0, '1980-02-05 12:45'), 0) AS Hour ,
DATEADD(DAY, DATEDIFF(DAY, 0, '1980-02-05 12:45'), 0) AS Day ,
DATEADD(MONTH, DATEDIFF(MONTH, 0, '1980-02-05 12:45'), 0) AS Month ,
DATEADD(YEAR, DATEDIFF(YEAR, 0, '1980-02-05 12:45'), 0) AS Year;
But I have a need to store very early dates like 1400-01-01 and therefore I can use DateTime2.
But how would I support the ability to still truncate like above using DateTime2?
Changing the year above to 1400 from 1980 will then result in
SELECT DATEADD(HOUR, DATEDIFF(HOUR, 0, '1400-02-05 12:45'), 0) AS Hour ,
DATEADD(DAY, DATEDIFF(DAY, 0, '1400-02-05 12:45'), 0) AS Day ,
DATEADD(MONTH, DATEDIFF(MONTH, 0, '1400-02-05 12:45'), 0) AS Month ,
DATEADD(YEAR, DATEDIFF(YEAR, 0, '1400-02-05 12:45'), 0) AS Year;
The conversion of a varchar data type to a datetime data type resulted
in an out-of-range value.
SO casting to DateTime2
SELECT DATEADD(HOUR, DATEDIFF(HOUR, 0, CAST('1400-02-05 12:45' AS DATETIME2)),0) AS Hour ,
DATEADD(DAY, DATEDIFF(DAY, 0, CAST('1400-02-05 12:45' AS DATETIME2)),0) AS Day ,
DATEADD(MONTH, DATEDIFF(MONTH, 0, CAST('1400-02-05 12:45' AS DATETIME2)), 0) AS Month ,
DATEADD(YEAR, DATEDIFF(YEAR, 0, CAST('1400-02-05 12:45' AS DATETIME2)), 0) AS Year;
The conversion of a datetime2 data type to a datetime data type
resulted in an out-of-range value.
I am assuming that the 0 is being treated as a DateTime data type and effectly casting it to DateTime.
Trying to cast the 0 to DateTime2 using CAST(0 AS DATETIME2) gives me this error
Explicit conversion from data type int to datetime2 is not allowed.
In the end I am wanting to use these as persisted columns in a table which worked fine with DateTime data types but not so easy with DateTime2
You should use some specific base date instead of 0. 0 can be implicitly converted into datetime type. For datetime2 such implicit conversion is not allowed. In addition, the base date should have a datetime2 type. Then DATEDIFF and DATEADD would work with datetime2 values.
Another reason for using explicit base date is that you need this base date to be the first day of the year and to have 00:00:00 time for the formula to work correctly. Implicit starting dates, like 0 converted to datetime or '' converted to datetime2 also have these properties right now, but do you really want to rely on internal details of the type implementation? It is better to spell out such things explicitly and it makes the formula easier to understand for a new person.
Besides, if you ever want to truncate to the week boundary using the same approach, then you'd have to pick a base date that is Monday (if your week starts on Monday) or Sunday (if your week starts on Sunday). The formula remains the same, but base date is important.
Example 1 - works
DECLARE #VarBase datetime2 = '2000-01-01';
DECLARE #VarValue datetime2 = '1400-02-05 12:45';
SELECT
DATEADD(HOUR, DATEDIFF(HOUR, #VarBase, #VarValue), #VarBase) AS Hour,
DATEADD(DAY, DATEDIFF(DAY, #VarBase, #VarValue), #VarBase) AS Day,
DATEADD(MONTH, DATEDIFF(MONTH, #VarBase, #VarValue), #VarBase) AS Month,
DATEADD(YEAR, DATEDIFF(YEAR, #VarBase, #VarValue), #VarBase) AS Year;
Example 2 - works
SELECT
DATEADD(HOUR, DATEDIFF(HOUR, #VarBase, '1400-02-05 12:45'), #VarBase) AS Hour,
DATEADD(DAY, DATEDIFF(DAY, #VarBase, '1400-02-05 12:45'), #VarBase) AS Day,
DATEADD(MONTH, DATEDIFF(MONTH, #VarBase, '1400-02-05 12:45'), #VarBase) AS Month,
DATEADD(YEAR, DATEDIFF(YEAR, #VarBase, '1400-02-05 12:45'), #VarBase) AS Year;
Example 3 - doesn't work
SELECT
DATEADD(HOUR, DATEDIFF(HOUR, '2000-01-01', '1400-02-05 12:45'), '2000-01-01') AS Hour,
DATEADD(DAY, DATEDIFF(DAY, '2000-01-01', '1400-02-05 12:45'), '2000-01-01') AS Day,
DATEADD(MONTH, DATEDIFF(MONTH, '2000-01-01', '1400-02-05 12:45'), '2000-01-01') AS Month,
DATEADD(YEAR, DATEDIFF(YEAR, '2000-01-01', '1400-02-05 12:45'), '2000-01-01') AS Year;
Adding a value to a 'datetime' column caused an overflow.
It doesn't work, because literal 2000-01-01 is converted into datetime, not datetime2.
Example 4 - works
SELECT
DATEADD(HOUR, DATEDIFF(HOUR, CAST('2000-01-01' AS datetime2), '1400-02-05 12:45'), CAST('2000-01-01' AS datetime2)) AS Hour,
DATEADD(DAY, DATEDIFF(DAY, CAST('2000-01-01' AS datetime2), '1400-02-05 12:45'), CAST('2000-01-01' AS datetime2)) AS Day,
DATEADD(MONTH, DATEDIFF(MONTH, CAST('2000-01-01' AS datetime2), '1400-02-05 12:45'), CAST('2000-01-01' AS datetime2)) AS Month,
DATEADD(YEAR, DATEDIFF(YEAR, CAST('2000-01-01' AS datetime2), '1400-02-05 12:45'), CAST('2000-01-01' AS datetime2)) AS Year;
Try to use:
DECLARE #Default DATETIME2 = CAST('' AS DATETIME2)
SELECT DATEADD(HOUR, DATEDIFF(HOUR, #Default, CAST('1400-02-05 12:45' AS DATETIME2)), #Default) AS Hour ,
DATEADD(DAY, DATEDIFF(DAY, #Default, CAST('1400-02-05 12:45' AS DATETIME2)), #Default) AS Day ,
DATEADD(MONTH, DATEDIFF(MONTH, #Default, CAST('1400-02-05 12:45' AS DATETIME2)), #Default) AS Month ,
DATEADD(YEAR, DATEDIFF(YEAR, #Default, CAST('1400-02-05 12:45' AS DATETIME2)), #Default) AS Year;
I found that instead of using CAST ('DateTimeData' as DateTime2) if I use Convert it can be a deterministic result.
As documented on this post: Cannot persist computed column - not deterministic
ALTER TABLE dbo.QuakeRawJSON
ADD [Date] AS (DATEADD(DAY,DATEDIFF(DAY, CONVERT(DATETIME2,'',112)
,origintime),CONVERT(DATETIME2,'',112))) PERSISTED;
Thanks for your help.
Related
I would like some help with the filter for summing Quarterly data.
For Yearly data I have added the formula.
For Quarterly data, I have hard coded for now but need to get the system to calculate.
So for example next quarter which will be Quarter 3, I need to calculate figures for current year/current quarter (summing figures for 3 months in that quarter)
SNIPPET of filter code
WHERE [Status] = 'Complete'
AND (YEAR([datefield]) = YEAR(GETDATE()) - 1
OR YEAR([datefield]) = YEAR(GETDATE()) - 2
OR YEAR([datefield]) = YEAR(GETDATE())
AND MONTH([datefield]) IN(4, 5, 6)) --Apr/May/June.
Sorry for providing an incomplete answer. I have thought of this after I've answered.
SELECT *
FROM table
WHERE [Status] = 'Complete'
AND [datefield] BETWEEN CONVERT(date, DATEADD(M, -2, DATEADD(D, -DAY(DATEADD(D, -1, GETDATE())), GETDATE())))
AND EOMONTH(DATEADD(D, -DAY(DATEADD(D, -1, GETDATE())), GETDATE()))
Explanation:
For the filter to be accurate, you need to get the first day and last day of the month.
First Day:
Get current date:
SELECT GETDATE()
2018-09-06 17:03:35.467
Get current date minus 1
SELECT DATEADD(D, -1, GETDATE())
2018-09-05 17:03:35.467
Subtract "current date minus 1" (the above result) to the current date.
SELECT DATEADD(D, -DAY(DATEADD(D, -1, GETDATE())), GETDATE())
2018-09-01 17:03:35.467
Get date part only:
SELECT CONVERT(date, DATEADD(D, -DAY(DATEADD(D, -1, GETDATE())), GETDATE()))
2018-09-01
Last Day:
Same as getting the first day, but changing CONVERT to EOMONTH since the latter already returns the result as date part only
SELECT EOMONTH(DATEADD(D, -DAY(DATEADD(D, -1, GETDATE())), GETDATE()))
2018-09-30
So you now have
SELECT CONVERT(date, DATEADD(D, -DAY(DATEADD(D, -1, GETDATE())), GETDATE()))
,EOMONTH(DATEADD(D, -DAY(DATEADD(D, -1, GETDATE())), GETDATE()))
2018-09-01
2018-09-30
Get the previous 2 months from the current month.
SELECT CONVERT(date, DATEADD(M, -2, DATEADD(D, -DAY(DATEADD(D, -1, GETDATE())), GETDATE())))
Notice that I just added DATEADD(M, -2, ) to the First Day value.
Combine the first day of 2 months ago and the last day of the current month you get
SELECT CONVERT(date, DATEADD(M, -2, DATEADD(D, -DAY(DATEADD(D, -1, GETDATE())), GETDATE())))
,EOMONTH(DATEADD(D, -DAY(DATEADD(D, -1, GETDATE())), GETDATE()))
2018-07-01
2018-09-30
I am developing a report using SQL and SSRS that gives a day by day breakdown of stats from the 1st of the month to the previous day.
However, on the 1st of the month the report comes up blank when I need it to show the previous months information (e.g. on the 1st of February I want a report that gives me a day by day breakdown of January from the 1st to 31st.
I tried the following case statement in the SQL where clause to see if it would fix the issue but it doesn't seem to work:
(dIntervalStart BETWEEN dbo.DateAndTime(CASE WHEN DAY(GEtDate()) = 1 THEN DATEADD(mm, DATEDIFF(mm, 0, GETDATE() - 1), 0) ELSE DATEADD(mm, DATEDIFF(mm, 0, GETDATE()), 0) END, '00:00:00')
Is there something else that might work?
declare #StartOfMOnth bit
declare #startDate date
declare #endDate date
select #StartOfMOnth =
case when datepart(dd, getdate()) <> 1 then 0
else 1
end
IF (#StartOfMOnth = 0) --If not start of month
BEGIN
SELECT #startDate = DATEADD(month, DATEDIFF(month, 0, GETDATE()), 0) --First day of current month
SELECT #endDate = DATEADD(DD, -1, GETDATE())--Previous day
END
ELSE --If start of month
BEGIN
SELECT #startDate = DATEADD(MONTH, -1, DATEADD(month, DATEDIFF(month, 0, GETDATE()), 0)) --First day of last month
SELECT #endDate = DATEADD(DD, -1, DATEADD(month, DATEDIFF(month, 0, GETDATE()), 0))--Last day of last month
END
--Once you have start and end dates, do what needs to be done in the dataset
The query below grabs the max date from column Time_Stamp as StartDate from Survey. Adding 90 days to it for EndDate. Then creating 2 more ranges with 90 day intervals by just adding more to the original EndDate.
I'm trying to have the StartDate fall within 4 buckets of either Jan1st, April1st, July1st, or Oct1. If the Max Time_Stamp is before 1 of these dates then that will be the first StartDate of my ranges...So for my example below, the max Time_Stamp is June4th so the StartDate needs to be July 1st. Is this doable within sql server?
Time_Stamp for Hospital1
Time_Stamp
-----------
2014-06-04 16:01:14.000
2014-06-04 15:55:33.000
2014-06-04 15:45:05.000
2014-06-04 15:36:15.000
2014-06-04 15:00:34.000
2014-06-04 14:35:24.000
2014-06-04 14:04:50.000
2014-06-04 13:46:55.000
2014-06-04 13:23:57.000
2014-06-04 11:27:51.000
Current output:
StartDate EndDate
----------- -----------
Jun 4 2014 Sep 2 2014
Sep 3 2014 Dec 2 2014
Dec 3 2014 Mar 3 2015
query
WITH Start AS
(
SELECT
MAX(Time_Stamp) as StartDate,
DATEADD(day, 90, MAX(Time_Stamp)) as EndDate
FROM Survey
WHERE MainHospital = 'Hospital1'
),
Results AS
(
SELECT StartDate, EndDate from Start
UNION
SELECT DATEADD(DAY, 1, EndDate), DATEADD(day, 91, EndDate) FROM Start
UNION
SELECT DATEADD(DAY, 92, EndDate), DATEADD(day, 182, EndDate) FROM Start
)
SELECT LEFT(StartDate,11) AS StartDate, LEFT(EndDate,11) AS EndDate FROM Results
Just an update, this gives me what I need for the 1st StartDate...
--Return first day of next quarter
SELECT DATEADD(qq, DATEDIFF(qq, 0, MAX(Time_Stamp)) + 1, 0)
FROM Survey
WHERE MainHospital = 'Hospital1'
DECLARE #Year DATE = '2013-01-01'
DECLARE #Quarter INT = 1;
SELECT DATEADD(QUARTER, #Quarter - 1, #Year) ,
DATEADD(DAY, -1, DATEADD(QUARTER, #Quarter, #Year))
You can use DATEPART(QUARTER, #Date) to figure out which quarter the record being selected and then you can use this query to find the begin/end dates of that quarter.
Use it like this:
SELECT DATEADD(QUARTER, DATEPART(QUARTER, Time_Stamp) - 2, DATEADD(YEAR, DATEDIFF(YEAR, 0, Time_Stamp), 0)) AS StartDate,
DATEADD(SECOND, -1, DATEADD(QUARTER, DATEPART(QUARTER, Time_Stamp) - 1, DATEADD(YEAR, DATEDIFF(YEAR, 0, Time_Stamp), 0))) AS EndDate
FROM Hospital1
It seems that you are just trying to calculate the quarter of your date, Am i correct?
If so, you can just use:
SELECT DATENAME(Quarter, CAST(CONVERT(VARCHAR(8), GETDATE()) AS DATETIME)) as Quarter
to calculate a quarters date ranges:
select DATEADD(qq, datediff(qq, 0, getdate()),0) as first
select dateadd(dd, -1, DATEADD(qq, datediff(qq, 0, getdate()) +1, 0)) as last
How can I get the range between a day dynamically for e.g. BETWEEN 23.05.2012 00:00 AND 23.05.2012 23:59 using MSSQL ? I got the first part;
WHERE
AND s.SCHEDULE_START_DATE BETWEEN dateadd(DAY, datediff(DAY, 0, getdate()), 0) AND FILL HERE
You will have to try something like
s.SCHEDULE_START_DATE >= dateadd(DAY, datediff(DAY, 0, getdate()), 0)
AND s.SCHEDULE_START_DATE < (dateadd(DAY, datediff(DAY, 0, getdate()), 0) + 1)
Have a look at the below example
SQL Fiddle DEMO
Try this one -
Query:
DECLARE #Dates TABLE
(
SCHEDULE_START_DATE DATETIME
)
INSERT INTO #Dates
VALUES
('20130522'),
('20130523'),
('20130524'),
('20130523 18:00:00'),
('20130523 23:59:59')
DECLARE
#DateFrom DATETIME = DATEADD(DAY, DATEDIFF(DAY, 0, GETDATE()), 0)
, #DateTo DATETIME = DATEADD(SECOND, 86399, DATEADD(DAY, DATEDIFF(DAY, 0, GETDATE()), 0))
SELECT *
FROM #Dates
WHERE SCHEDULE_START_DATE BETWEEN #DateFrom AND #DateTo
Output:
SCHEDULE_START_DATE
-----------------------
2013-05-23 00:00:00.000
2013-05-23 18:00:00.000
2013-05-23 23:59:59.000
Here is a great way of how to get day of week for a date, Deterministic scalar function to get day of week for a date.
Now, could anyone help me to create a deterministic scalar function to get week of year for a date please? Thanks.
This works deterministically, I can use it as a computed column.
datediff(week, dateadd(year, datediff(year, 0, #DateValue), 0), #DateValue) + 1
Test code:
;
with
Dates(DateValue) as
(
select cast('2000-01-01' as date)
union all
select dateadd(day, 1, DateValue) from Dates where DateValue < '2050-01-01'
)
select
year(DateValue) * 10000 + month(DateValue) * 100 + day(DateValue) as DateKey, DateValue,
datediff(day, dateadd(week, datediff(week, 0, DateValue), 0), DateValue) + 2 as DayOfWeek,
datediff(week, dateadd(month, datediff(month, 0, DateValue), 0), DateValue) + 1 as WeekOfMonth,
datediff(week, dateadd(year, datediff(year, 0, DateValue), 0), DateValue) + 1 as WeekOfYear
from Dates option (maxrecursion 0)