SQL Full day difference - sql

Does anyone know how to get the datdiff in full days between 2 days.
Im currentlly using
datediff(day,createddate,dateserved)
But need it to return how many full days
i.e
Created = 1/7/2010 2100
dateserved = 2/7/2010 2000
currently the datediff would show 1 day but i need it to show 0 until dateserved passes 2100
Any ideas
Sp

SELECT FLOOR(CAST(dateserved AS FLOAT) - CAST( createddate AS FLOAT))
Also the following seems to work and be more concise but may need some testing
SELECT FLOOR(CAST(dateserved-createddate AS FLOAT))

#Ian Jacobs got it in first, but here's how I'd do it in T-SQL. Assuming you're only concerned with hours:
DECLARE
#From datetime
,#Thru datetime
SET #From = 'Jan 1, 2010 21:00'
SET #Thru = 'Jan 3, 2010 20:00' -- 2/7/2010 2000
print datediff(dd, #From, #Thru)
print datediff(hh, #From, #Thru)
PRINT datediff(hh, #From, #Thru) / 24
...that is, calculate the hours difference between your datetimes, divide by 24, and truncate the decimal value. SQL appears to truncate, but if you're paranoid, use
print datediff(hh, #From, #Thru) / 24.0
PRINT floor(datediff(hh, #From, #Thru) / 24.0)
to ensure proper truncation. If you need precision down to the minute, second, or millisecond, add bit more arithmatic.

What you can do is go with the smallest possible resolution in the DATEDIFF() function you can feasibly get away with (minutes,seconds, whatever). Then to math to convert that to a day representations.
I'm basically proposing:
Floor(DATEDIFF(mi, createddate, dateserved)/60/24);

You could use:
DATEDIFF(dy, created_date, date_served) -
CASE
WHEN CAST(created_date AS TIME) > CAST(date_served AS TIME) THEN 1
ELSE 0
END
I originally proposed trying to use division, but when you get down to milliseconds you can quickly hit arithmetic overflows.

Related

Convert Ticks to Date Across Columns

I'm running into this SQL error and I'm not exactly sure how to get around it. It involves translating .Net ticks to date. I've come across other posts on this issue but only covered instances where I'd be doing this for one entry.
My problem is that I have a column of ticks that I'd like to convert, similar to below.
ACCCOUNT STARTDATE ENDDATE
0001 635200704000000000 635200704000000000
0002 635203296000000000 635203296000000000
How would I translate this to be.
ACCCOUNT STARTDATE ENDDATE
0001 11/14/2013 11/14/2013
0002 11/17/2013 11/17/2013
I'm not exactly sure how I'd go about this. Can anyone please point me in the right direction. Here's a snippet of SQL that I've managed to piece together in attempting to solve this. Though I know this won't give me the format that I want.
SELECT READING.ACCOUNT,
datename(month,(READING.STARTDATE-599266080000000000)/864000000000) +
space(1) +
datename(d,(READING.STARTDATE-599266080000000000)/864000000000) +
', ' +
datename(year,(READING..STARTDATE-599266080000000000)/864000000000) as
READDATE
FROM READING
If you need exact precision down to the tick:
declare #ticks bigint
set #ticks = 3155378975999999999 -- (DateTime.MaxValue.Ticks)
select DATEADD(D, #ticks / 864000000000,
DATEADD(MS, (#ticks % 864000000000) / 10000,
DATEADD(NS, (#ticks % 10000) * 100,
convert(datetime2, '0001-01-01'))))
-- ouputs: 9999-12-31 23:59:59.9999999
Though in your case, I think you're only asking for precision to the whole day:
declare #ticks bigint
set ticks = 635200704000000000
select DATEADD(D, #ticks / 864000000000, convert(datetime2, '0001-01-01'))
-- 635200704000000000 outputs 2013-11-15 00:00:00.0000000
-- 635203296000000000 outputs 2013-11-18 00:00:00.0000000
Like I said in comments, the values are one day off from what you said you expected, so you probably have a time zone issue as well.
Given your column names, you would modify your query to this:
SELECT ACCOUNT,
DATEADD(D, STARTDATE / 864000000000, convert(datetime2, '0001-01-01')) as StartDate,
DATEADD(D, ENDDATE / 864000000000, convert(datetime2, '0001-01-01')) as EndDate
FROM READING
You can easily convert ticks to Datetime and vice-versa in .NET. Here is sample code for your reference.
long ticks = (DateTime.Now).Ticks;
//Convert these ticks to DateTime
DateTime dtresult = new DateTime(ticks);

Rounding a datetime value down to the nearest half hour

I have a requirement to round a datetime2 value down to the nearest half hour. For example '10/17/2013 12:10:00.123' would round down to '10/17/2013 12:00:00.0' And '10/17/2013 12:34:17.123' would round down to 10/17/2013 12:30:00.0'. My first thought was to create a UDF which would break the date and time apart and do it that way. However, I'm wondering if something like this can be done in a single T-SQL statement?
I'm using SQL Server 2012 and the data type of the column is a dateTime2 (which cannot be converted to a float!!)
The answer by Ian is good, but it contains an unnecessary conversion. I suggest
SELECT CONVERT(smalldatetime, ROUND(CAST([columnname] AS float) * 48.0,0,1)/48.0) FROM [tableName]
If you want to round to the nearest half-hour instead of always rounding down, use
SELECT CONVERT(smalldatetime, ROUND(CAST([columnname] AS float) * 48.0,0)/48.0) FROM [tableName]
How about this
declare #d datetime = '2013-05-06 12:29.123'
select
case
when datepart(minute, #d) < 30 then cast(dateadd(minute, -datepart(minute,#d)-datepart(second,#d), #d) as smalldatetime)
when datepart(minute, #d) >= 30 then cast(dateadd(minute, -datepart(minute,#d)-datepart(second,#d)+30, #d) as smalldatetime)
end
Here is one way to do it:
update t set
d = dateadd(minute,datediff(minute,'19000101',d)/30*30,'19000101');
select cast(floor(cast(
cast('10/17/2013 12:34:00' as datetime)
as float(53)) * 48) / 48 as datetime)
EDIT
Works better if you use smalldatetime to avoid the extra precision
select cast(floor(cast(
cast('2012-01-02 11:33:14.097' as smalldatetime)
as float(53)) * 48) / 48 as smalldatetime)
Here is a slightly different approach that I used when I needed to round down to the nearest 5 minute interval. There is probably a way to simplify this further, but at least this got me what I needed.
DECLARE #now datetime = GETDATE()
SELECT #now as cur_datetime, DATEADD(MINUTE, -(DATEDIFF(MINUTE,DATEADD(HOUR,DATEDIFF(HOUR,0,#now), 0),DATEADD(MINUTE,DATEDIFF(MINUTE,0,#now), 0)) % 5), DATEADD(MINUTE,DATEDIFF(MINUTE,0,#now), 0)) as round_down_to_nearest_5_minute_mark
#Twinkles's answer works well in SQL server to round to closest half an hour.
However, in development, strongly recommend use FLOOR to round to last half an hour.
SELECT CONVERT(datetime, FLOOR(CAST([columnname] AS float) * 48.0)/48.0) FROM [tableName]
You can use DATETIME2FROMPARTS to reconstruct the date. To round the minutes down to 30 minute intervals use the formula minutes intdiv 30 * 30
SELECT
dt2,
DATETIME2FROMPARTS(
DATEPART(year, dt2),
DATEPART(month, dt2),
DATEPART(day, dt2),
DATEPART(hour, dt2),
DATEPART(minute, dt2) / 30 * 30,
0,
0,
0
)
FROM (VALUES
-- generic datetime2
(SYSDATETIME()),
-- 30 minute boundary
('2001-01-01 00:29:59.9999999'),
('2001-01-01 00:30:00.0000000'),
('2001-01-01 00:30:00.0000001'),
-- min and max date
('0001-01-01 00:00:00.0000000'),
('9999-12-31 23:59:59.9999999')
) AS v(dt2)

SQL for last six "full" months

I have table containing one datetime column. I need to return rows for only last 6 months. This can be done by
WHERE CloseTime >= DATEADD(Month, DATEDIFF(Month, 0, DATEADD(m, - 6, CURRENT_TIMESTAMP)), 0)
This gets me data for the month I am starting this script + 6 last months. So e.g. if I run this script today, Ill get the data for this month + all previous months till April (04).
Now I need to modify the condition so if I run the script today, the data will be obtained only for months 03-09 only, exluding days in this month (10).
Any advice, please?
If you want to have the previous 6 months regardless of whether today is the 1st, 3rd, 9th, 29th, whatever, then just subtract 7 months. Here is one way to do that: get the first of the month into a variable, then use an open-ended range in the query.
DECLARE #ThisMonth DATETIME;
SET #ThisMonth = DATEADD(MONTH, DATEDIFF(MONTH, '19000101', GETDATE()), '19000101');
SELECT...
WHERE CloseTime >= DATEADD(MONTH, -7, #ThisMonth)
AND CloseTime < #ThisMonth;
You could also use 0 in place of '19000101' but I prefer an explicit date than implicit shorthand (it was a very tough habit to break).
If you really don't like variables, then you can make the query a lot more complex by repeating the expression to calculate the first of this month (and in the start of the range, subtract 7 from the number of months):
SELECT...
WHERE CloseTime >= DATEADD(MONTH, DATEDIFF(MONTH, '19000101', GETDATE())-7, '19000101')
AND CloseTime < DATEADD(MONTH, DATEDIFF(MONTH, '19000101', GETDATE()), '19000101');
Yuck. Variables make this much tidier.
When creating queries you do not want to use a function on the search column since it will result in a full table scan.
The solution works and should pick up any index on CloseTime.
-- Get me data in months 3 (mar) to 9 (sep) of this year
select
*
from
my_table
where
CloseTime between
DATEADD(d, -1, '03-01-2013') and DATEADD(d, +1, '09-20-2013')
If the table is small and a full table scan is not a issue, a simple solution is to use the MONTH function.
-- Get me data in months 3 (mar) to 9 (sep) of this year
select
*
from
my_table
where
MONTH(CloseTime) IN (3,4,5,6,7,8,9) and YEAR(CloseTime) = 2013
I looked at Aaron's article. Its a very good read.
I wondered if there was some new function that was not tested since that article.
If you are using 2012, why not use the format function? Logically, you want date variables with a 01 day. The query plan still gets a clustered index scan.
Let's see how this solution stacks up using Aaron's test database.
-- Use the sample
use [DateTesting]
go
-- Johns - log by 6 months
CREATE PROCEDURE dbo.Johns_LogBy6Months
#date SMALLDATETIME
AS
BEGIN
SET NOCOUNT ON;
DECLARE #cmp_date SMALLDATETIME = format(#date, 'yyyyMM01');
DECLARE #c INT;
SELECT #c = COUNT(*)
FROM dbo.SomeLogTable
WHERE DateColumn >= dateadd(m, -7, #cmp_date)
AND DateColumn < #cmp_date
END
GO
-- Aarons - log by 6 months
CREATE PROCEDURE dbo.Aarons_LogBy6Months
#date SMALLDATETIME
AS
BEGIN
SET NOCOUNT ON;
DECLARE #c INT;
DECLARE #cmp_date SMALLDATETIME = DATEADD(MONTH, DATEDIFF(MONTH, '19000101', #date), '19000101');
SELECT #c = COUNT(*)
FROM dbo.SomeLogTable
WHERE
DateColumn >= dateadd(m, -7, #cmp_date)
AND DateColumn < #cmp_date
END
GO
Lets make 1000 calls to the functions.
-- Sample calls x 1000
PRINT CONVERT(char(23), GETDATE(), 121);
GO
EXEC dbo.Johns_LogCountByDay #date = '20091005';
GO 1000
PRINT CONVERT(char(23), GETDATE(), 121);
GO
EXEC dbo.Aarons_LogBy6Months #date = '20091005';
GO 1000
PRINT CONVERT(char(23), GETDATE(), 121);
GO
Here are the executions times.
2013-10-10 11:58:49.547
Beginning execution loop
Batch execution completed 1000 times.
2013-10-10 11:58:52.837
Beginning execution loop
Batch execution completed 1000 times.
2013-10-10 11:58:55.883
In summary a call to the new format() function and a implicit cast to a small date time takes a little more time than a dateadd() and datediff() with two string (date) literals.
The format() solution seems more intuitive or self documenting to me. The time difference was 3.3 versus 3.0 sec.
I have to give the speed test a win to Aaron's solution. Stick with in-equality comparisons of date variables. They are faster.
In short, I will have to fix my bad habits.
I had a similar request. I needed last six months and next six months of appointments. But just like you I needed the full months. So a simple getdate +- 180 wouldn't do.
I took a more simple approach. I get the year and month and turn it into a number like 201912. Then do a between clause. Its dynamic and I get full months.
I'm sure there's more sophisticated approaches, but this is what I came up with.
WHERE Year([ApptDate])*100 + Month([ApptDate]) between Year(getdate()-180)*100 + Month(getdate()-180) and Year(getdate()+180)*100 + Month(getdate()+180)

Converting Milliseconds to Days, hours, minutes and seconds

i have a bigint field in Microsoft SQL Server 2008R2 filled with ticks (A single tick represents one hundred nanoseconds or one ten-millionth of a second. There are 10,000 ticks in a millisecond.)
http://msdn.microsoft.com/en-us/library/system.datetime.ticks.aspx
and i need to convert the sum of all records to Days:Hours:Minutes:Seconds:Milliseconds.
it works for a single record:
SELECT CONVERT(TIME, DATEADD(ms, duration/10000, 0)) FROM tblMediaFileProperties WHERE FileId = '6C0A849D-95B4-4755-A923-B9DD8F1AF23E'
but if a sum it up to all records using:
SELECT CONVERT(TIME, DATEADD(ms, SUM(duration/10000), 0)) FROM tblMediaFileProperties
i get a:
Arithmetic overflow error converting expression to data type int.
i know the overflow comes from the CONVERT to Data Type TIME Function...
help's appreciated, thanks!
It's too big for DATEADD which only accepts an int.
Break it into two parts: seconds, then milliseconds.
SELECT CONVERT(TIME,
DATEADD(ms, SUM(duration/10000 % 1000),
DATEADD(ss, SUM(duration/10000000), 0)))
FROM tblMediaFileProperties
And if your total duration goes above 1 day, you can use this to get the days and hr:min:sec:ms separately. It's a matter of cast and string concat if you actually want the result in textual form.
declare #duration bigint
set #duration = 1230000000
SELECT #duration/10000/1000/60/60/24 DAYS,
CONVERT(TIME,
DATEADD(ms, SUM(#duration/10000 % 1000),
DATEADD(ss, SUM(#duration/10000000), 0))) HR_MIN_SEC

SQL Server: Get data for only the past year

I am writing a query in which I have to get the data for only the last year. What is the best way to do this?
SELECT ... FROM ... WHERE date > '8/27/2007 12:00:00 AM'
The following adds -1 years to the current date:
SELECT ... From ... WHERE date > DATEADD(year,-1,GETDATE())
I found this page while looking for a solution that would help me select results from a prior calendar year. Most of the results shown above seems return items from the past 365 days, which didn't work for me.
At the same time, it did give me enough direction to solve my needs in the following code - which I'm posting here for any others who have the same need as mine and who may come across this page in searching for a solution.
SELECT .... FROM .... WHERE year(*your date column*) = year(DATEADD(year,-1,getdate()))
Thanks to those above whose solutions helped me arrive at what I needed.
Well, I think something is missing here. User wants to get data from the last year and not from the last 365 days. There is a huge diference. In my opinion, data from the last year is every data from 2007 (if I am in 2008 now). So the right answer would be:
SELECT ... FROM ... WHERE YEAR(DATE) = YEAR(GETDATE()) - 1
Then if you want to restrict this query, you can add some other filter, but always searching in the last year.
SELECT ... FROM ... WHERE YEAR(DATE) = YEAR(GETDATE()) - 1 AND DATE > '05/05/2007'
The most readable, IMO:
SELECT * FROM TABLE WHERE Date >
DATEADD(yy, -1, CONVERT(datetime, CONVERT(varchar, GETDATE(), 101)))
Which:
Gets now's datetime GETDATE() = #8/27/2008 10:23am#
Converts to a string with format 101 CONVERT(varchar, #8/27/2008 10:23am#, 101) = '8/27/2007'
Converts to a datetime CONVERT(datetime, '8/27/2007') = #8/27/2008 12:00AM#
Subtracts 1 year DATEADD(yy, -1, #8/27/2008 12:00AM#) = #8/27/2007 12:00AM#
There's variants with DATEDIFF and DATEADD to get you midnight of today, but they tend to be rather obtuse (though slightly better on performance - not that you'd notice compared to the reads required to fetch the data).
Look up dateadd in BOL
dateadd(yy,-1,getdate())
GETDATE() returns current date and time.
If last year starts in midnight of current day last year (like in original example) you should use something like:
DECLARE #start datetime
SET #start = dbo.getdatewithouttime(DATEADD(year, -1, GETDATE())) -- cut time (hours, minutes, ect.) -- getdatewithouttime() function doesn't exist in MS SQL -- you have to write one
SELECT column1, column2, ..., columnN FROM table WHERE date >= #start
I, like #D.E. White, came here for similar but different reasons than the original question. The original question asks for the last 365 days. #samjudson's answer provides that. #D.E. White's answer returns results for the prior calendar year.
My query is a bit different in that it works for the prior year up to and including the current date:
SELECT .... FROM .... WHERE year(date) > year(DATEADD(year, -2, GETDATE()))
For example, on Feb 17, 2017 this query returns results from 1/1/2016 to 2/17/2017
For some reason none of the results above worked for me.
This selects the last 365 days.
SELECT ... From ... WHERE date BETWEEN CURDATE() - INTERVAL 1 YEAR AND CURDATE()
The other suggestions are good if you have "SQL only".
However I suggest, that - if possible - you calculate the date in your program and insert it as string in the SQL query.
At least for for big tables (i.e. several million rows, maybe combined with joins) that will give you a considerable speed improvement as the optimizer can work with that much better.
argument for DATEADD function :
DATEADD (*datepart* , *number* , *date* )
datepart can be: yy, qq, mm, dy, dd, wk, dw, hh, mi, ss, ms
number is an expression that can be resolved to an int that is added to a datepart of date
date is an expression that can be resolved to a time, date, smalldatetime, datetime, datetime2, or datetimeoffset value.
declare #iMonth int
declare #sYear varchar(4)
declare #sMonth varchar(2)
set #iMonth = 0
while #iMonth > -12
begin
set #sYear = year(DATEADD(month,#iMonth,GETDATE()))
set #sMonth = right('0'+cast(month(DATEADD(month,#iMonth,GETDATE())) as varchar(2)),2)
select #sYear + #sMonth
set #iMonth = #iMonth - 1
end
I had a similar problem but the previous coder only provided the date in mm-yyyy format. My solution is simple but might prove helpful to some (I also wanted to be sure beginning and ending spaces were eliminated):
SELECT ... FROM ....WHERE
CONVERT(datetime,REPLACE(LEFT(LTRIM([MoYr]),2),'-
','')+'/01/'+RIGHT(RTRIM([MoYr]),4)) >= DATEADD(year,-1,GETDATE())
Here's my version.
YEAR(NOW())- 1
Example:
YEAR(c.contractDate) = YEAR(NOW())- 1
For me this worked well
SELECT DATE_ADD(Now(),INTERVAL -2 YEAR);
If you are trying to calculate "rolling" days, you can simplify it by using:
Select ... FROM ... WHERE [DATE] > (GETDATE()-[# of Days])