round GETDATE (SQL Server) - sql

I have a function which is working fine in MySQL
round((now()-ts/60) as tdiff
(round the result of subtracting the current datetime from ts (also a datetime) divided by 60)
Attempting (and failing) to convert this for SQL Server.
Tried -
round((GETDATE()-ts/60) as tdiff
but that results in round function requires 2 or 3 parameters (which to me it does), so modified to -
round((GETDATE()-ts/60,0) as tdiff
but that results in the datatypes (GETDATE and ts) are incompatible in the subtract operator.
So then I attempted to cast both GETDATE and ts as date and that made no difference.
ts is a conventional datetime i.e.
2918-04-20 11:05:09 and I assumed GETDATE returned the same format.
As an example if GETDATE is today and ts is 2018-04-20 11:05:09 then tdiff is
6850891 (round effectively removes the dashes and colons and concatenates the datetime producing 20180420110509 for 2018-04-20 11:05:09 and 20180831164000 for 2018-08-31 16:40:00 and then divides by 60 to get 6850891.
Is there a remedy for this?
Regards, Ralph

GETDATE(), as per the documentation, returns a datetime. A datetime is accurate to 1/300 of a second, and it's accuracy cannot be changed.
If you want the time accurate to a second, you need to convert to a datetime2(0):
SELECT CONVERT(datetime2(0),GETDATE());
Also, however, don't use syntax like GETDATE()-ts. use the functions DATEADD and DATEDIFF for date maths.
I've no idea what GETDATE()-ts/60 is trying to acheive. Perhaps the number of minutes between the 2? DATEDIFF counts the "ticks" between 2 dates/times, thus DATEDIFF(MINUTE,'00:00:59','00:01:00') would return 1, despite there only being 1 second between the 2 times. This is because the minute value has "ticked" once (from 0 to 1). Therefore you might want to use DATEDIFF(SECOND,'00:00:59','00:01:00') / 60. This would return 0, as 1 / 60 in integer math is 0 (as is 59 / 60).

I think you want to use the DATEDIFF function:
DATEDIFF ( datepart , startdate , enddate )
DATEDIFF ( second, ts, GETDATE())
DATEDIFF ( second, ts, GETDATE())
DATEDIFF ( minute, ts, GETDATE())
DATEDIFF ( hour, ts, GETDATE())
The first argument tells it which increment of time to return.

If you are trying to find the difference between two values, then use datediff(). For instance:
select datediff(day, getdate(), ts)
gets the difference in days.
date_diff() or a related function would also be the right approach in MySQL.

sorry, I don't know if I have understand the question, you need to do date-date/60 and round the result?
In this case you have to change the minus ("-") with the DATEDIFF("Type return example DAYS", GETDATE(), ts).
So you will have ROUND((DATEDIFF(DAY,GETDATE(),ts)/60,0)
Please try and let me know if it will works for you
Bye

Related

Rounding problems with DATETIME

The following queries:
DECLARE #__dateRange_StartDate_4 DATETIME ='2021-03-01T00:00:00.000'
DECLARE #__dateRange_EndDate_3 DATETIME ='2021-03-31T23:59:59.999'
SELECT DATEDIFF(DAY, '2021-03-01T00:00:00.000', '2021-03-31T23:59:59.999') + 1
SELECT DATEDIFF(DAY, #__dateRange_StartDate_4, #__dateRange_EndDate_3) + 1
SELECT #__dateRange_EndDate_3
Produces the following results:
31
32
2021-04-01 00:00:00.000
It appears #__dateRange_EndDate_3 is being rounded to the next day, which I don't want.
What is the correct way to have the second SELECT return 31?
Note: My queries are actually being called from Entity Framework so I may be limited to what I can do here, but I at least want to understand the issue as this was unexpected.
DATETIME in SQL Server has an accuracy of 3.33ms (0.003 seconds) - therefore, the "highest" possible value for March 31, 2021 would be 2021-03-31T23:59:59.997 - anything beyond that will be rounded up to the next day.
This is just one of the reasons why as of SQL Server 2008 the general recommendation is to use DATE for when you don't need any time portion, or DATETIME2(n) (when you need the time portion; n is the number of fractional digits after the second - can be 0 through 7) datatypes.
DATETIME2(n) offers accuracy down to 100 ns and thus 2021-03-31T23:59:59.999 will be absolutely no problem in a DATETIME2(3) column.
As an added benefit, DATETIME2(n) also doesn't have this "arbitrary" lower limits of supported dates only through 01.01.1753 - with DATETIME2(n) you can store any date, back to 1 AD
This is silly. Don't bother with trying to get the last increment before a time -- and learning that datetime is only accurate to 0.003 seconds.
Express the logic only using dates:
DECLARE #__dateRange_StartDate_4 DATE ='2021-03-01'
DECLARE #__dateRange_EndDate_3 DATE ='2021-04-01'
SELECT DATEDIFF(DAY, '2021-03-01', '2021-04-01');
SELECT DATEDIFF(DAY, #__dateRange_StartDate_4, #__dateRange_EndDate_3);
SELECT #__dateRange_EndDate_3;
Then use these with inequalities:
WHERE date >= #__dateRange_StartDate_4 AND
date < #__dateRange_EndDate_3
Inequalities -- with >= and < is the recommended way to handle date/time comparisons. Dealing with the "last increment" problem is only one of the problems it solves.
If you really are committed to figuring out the last increment before midnight, you can use DATETIME2 or .997. But I don't recommend either of those approaches. Here is a db<>fiddle.

Is there a more elegant way to compare timestamps without milliseconds?

I have a table AT_OBJECT with two TIMESTAMP nullable fields MODIFIED and CURR_MODIFIED. I need to compare them but milliseconds should not be counted. As of now I use SQL request:
SELECT
*
FROM
at_object o
WHERE
DATEDIFF(SECOND,
COALESCE(o.modified, cast('01.01.2000 00:00:00.0000' as TIMESTAMP)),
COALESCE(o.curr_modified, cast('01.01.2000 00:00:00.0000' as TIMESTAMP))) > 0
It works but looks kind of ugly. Is there a better way to compare timestamps without milliseconds?
You can use a formula like below to remove the milliseconds (actually convert all ms to zero)
DATEADD(ms, -DATEPART(ms, date), date)
So
.
.
.
WHERE
DATEADD(ms, -DATEPART(ms, date1), date1) = DATEADD(ms, -DATEPART(ms, date2), date2)
And you could clean that all up by replacing it with a user defined function (hopefully my syntax is correct I don't have a Firebird installation here)
CREATE FUNCTION removeMs
(
#inDateTime
)
RETURNS DateTime
AS
BEGIN
RETURN DATEADD(ms, -DATEPART(ms, #inDateTime), #inDateTime)
END
then nice and clean!
WHERE
removeMs(date1) = removeMs(date2)
Edit: For Firebird the basic concept should work but the functions are slightly different
DATEPART -> EXTRACT(MILLISECOND FROM date1)
DATEADD -> DATEADD(MILLISECOND, -1 * EXTRACT(MILLISECOND FROM date1), date1)
all of which can be cleaned up with a user defined function (hopefully my syntax is correct)
If you don't need the milliseconds part at all (never in your software / queries) then the easiest solution would be to store the timestamps with appropriate precision, ie without millisecons. At the DDL it would be like:
CREATE TABLE T (
Foo TIMESTAMP DEFAULT CURRENT_TIMESTAMP(0) NOT NULL
);
Note the CURRENT_TIMESTAMP(0) - this takes care of the default value inserted by engine when user doesn't supply the value. If the field(s) are sometimes filled by the user then you also have to write BEFORE INSERT OR UPDATE triggers which "delete" the milliseconds part from timestamps.
Convert to timestamp and ignore milliseconds using date_trunc function. Refer below query.
This where clause worked for me.
date_trunc('seconds', "column name"::timestamp)= date_trunc( 'seconds', '2022-07-05 19:10:29'::timestamp) ;

How to add Floating(decimal) hour in DateTime's DATEADD function?

I am trying to add some hours in SQL SERVER using DATEADD function. But when I try this,
SELECT DATEADD(Hour, 0.5, GETDATE())
It is not adding 0.5 hour. How to solve this?
You can't. It's well describer on documentation: DATEADD (Transact-SQL)
number
Is an expression that can be resolved to an int that is added
to a datepart of date. User-defined variables are valid.
If you specify a value with a decimal fraction, the fraction is truncated and
not rounded.
UPDATE
You could try that:
SELECT DATEADD(Second, 0.5 * 60 * 60, GETDATE())
Of course - you can change DATEPART and multiplier to get desired precision.
You can't add parts of hours, just full hours. Use minute for half an hour
SELECT DATEADD(minute, 30, GETDATE())
I have found another approach to this where adding a number to the date instead of using the function works for fractions. For example:
GETDATE() + n
where n is the number of days. For 1 half hour, you can use:
GETDATE() + 0.5/24

convert Excel Date Serial Number to Regular Date

I got a column called DateOfBirth in my csv file with Excel Date Serial Number Date
Example:
36464
37104
35412
When i formatted cells in excel these are converted as
36464 => 1/11/1999
37104 => 1/08/2001
35412 => 13/12/1996
I need to do this transformation in SSIS or in SQL. How can this be achieved?
In SQL:
select dateadd(d,36464,'1899-12-30')
-- or thanks to rcdmk
select CAST(36464 - 2 as SmallDateTime)
In SSIS, see here
http://msdn.microsoft.com/en-us/library/ms141719.aspx
The marked answer is not working fine, please change the date to "1899-12-30" instead of "1899-12-31".
select dateadd(d,36464,'1899-12-30')
You can cast it to a SQL SMALLDATETIME:
CAST(36464 - 2 as SMALLDATETIME)
MS SQL Server counts its dates from 01/01/1900 and Excel from 12/30/1899 = 2 days less.
tldr:
select cast(#Input - 2e as datetime)
Explanation:
Excel stores datetimes as a floating point number that represents elapsed time since the beginning of the 20th century, and SQL Server can readily cast between floats and datetimes in the same manner. The difference between Excel and SQL server's conversion of this number to datetimes is 2 days (as of 1900-03-01, that is). Using a literal of 2e for this difference informs SQL Server to implicitly convert other datatypes to floats for very input-friendly and simple queries:
select
cast('43861.875433912' - 2e as datetime) as ExcelToSql, -- even varchar works!
cast(cast('2020-01-31 21:00:37.490' as datetime) + 2e as float) as SqlToExcel
-- Results:
-- ExcelToSql SqlToExcel
-- 2020-01-31 21:00:37.490 43861.875433912
this actually worked for me
dateadd(mi,CONVERT(numeric(17,5),41869.166666666664)*1440,'1899-12-30')
(minus 1 more day in the date)
referring to the negative commented post
SSIS Solution
The DT_DATE data type is implemented using an 8-byte floating-point number. Days are represented by whole number increments, starting with 30 December 1899, and midnight as time zero. Hour values are expressed as the absolute value of the fractional part of the number. However, a floating point value cannot represent all real values; therefore, there are limits on the range of dates that can be presented in DT_DATE. Read more
From the description above you can see that you can convert these values implicitly when mapping them to a DT_DATE Column after converting it to a 8-byte floating-point number DT_R8.
Use a derived column transformation to convert this column to 8-byte floating-point number:
(DT_R8)[dateColumn]
Then map it to a DT_DATE column
Or cast it twice:
(DT_DATE)(DT_R8)[dateColumn]
You can check my full answer here:
Is there a better way to parse [Integer].[Integer] style dates in SSIS?
Found this topic helpful so much so created a quick SQL UDF for it.
CREATE FUNCTION dbo.ConvertExcelSerialDateToSQL
(
#serial INT
)
RETURNS DATETIME
AS
BEGIN
DECLARE #dt AS DATETIME
SELECT #dt =
CASE
WHEN #serial is not null THEN CAST(#serial - 2 AS DATETIME)
ELSE NULL
END
RETURN #dt
END
GO
I had to take this to the next level because my Excel dates also had times, so I had values like this:
42039.46406 --> 02/04/2015 11:08 AM
42002.37709 --> 12/29/2014 09:03 AM
42032.61869 --> 01/28/2015 02:50 PM
(also, to complicate it a little more, my numeric value with decimal was saved as an NVARCHAR)
The SQL I used to make this conversion is:
SELECT DATEADD(SECOND, (
CONVERT(FLOAT, t.ColumnName) -
FLOOR(CONVERT(FLOAT, t.ColumnName))
) * 86400,
DATEADD(DAY, CONVERT(FLOAT, t.ColumnName), '1899-12-30')
)
In postgresql, you can use the following syntax:
SELECT ((DATE('1899-12-30') + INTERVAL '1 day' * FLOOR(38242.7711805556)) + (INTERVAL '1 sec' * (38242.7711805556 - FLOOR(38242.7711805556)) * 3600 * 24)) as date
In this case, 38242.7711805556 represents 2004-09-12 18:30:30 in excel format
In addition of #Nick.McDermaid answer I would like to post this solution, which convert not only the day but also the hours, minutes and seconds:
SELECT DATEADD(s, (42948.123 - FLOOR(42948.123))*3600*24, dateadd(d, FLOOR(42948.123),'1899-12-30'))
For example
42948.123 to 2017-08-01 02:57:07.000
42818.7166666667 to 2017-03-24 17:12:00.000
You can do this if you just need to display the date in a view:
CAST will be faster than CONVERT if you have a large amount of data, also remember to subtract (2) from the excel date:
CAST(CAST(CAST([Column_With_Date]-2 AS INT)AS smalldatetime) AS DATE)
If you need to update the column to show a date you can either update through a join (self join if necessary) or simply try the following:
You may not need to cast the excel date as INT but since the table I was working with was a varchar I had to do that manipulation first. I also did not want the "time" element so I needed to remove that element with the final cast as "date."
UPDATE [Table_with_Date]
SET [Column_With_Excel_Date] = CAST(CAST(CAST([Column_With_Excel_Date]-2 AS INT)AS smalldatetime) AS DATE)
If you are unsure of what you would like to do with this test and re-test! Make a copy of your table if you need. You can always create a view!
Google BigQuery solution
Standard SQL
Select Date, DATETIME_ADD(DATETIME(xy, xm, xd, 0, 0, 0), INTERVAL xonlyseconds SECOND) xaxsa
from (
Select Date, EXTRACT(YEAR FROM xonlydate) xy, EXTRACT(MONTH FROM xonlydate) xm, EXTRACT(DAY FROM xonlydate) xd, xonlyseconds
From (
Select Date
, DATE_ADD(DATE '1899-12-30', INTERVAL cast(FLOOR(cast(Date as FLOAT64)) as INT64) DAY ) xonlydate
, cast(FLOOR( ( cast(Date as FLOAT64) - cast(FLOOR( cast(Date as FLOAT64)) as INT64) ) * 86400 ) as INT64) xonlyseconds
FROM (Select '43168.682974537034' Date) -- 09.03.2018 16:23:28
) xx1
)
For those looking how to do this in excel (outside of formatting to a date field) you can do this by using the Text function https://exceljet.net/excel-functions/excel-text-function
i.e.
A1 = 132134
=Text(A1,"MM-DD-YYYY") will result in a date
This worked for me because sometimes the field was a numeric to get the time portion.
Command:
dateadd(mi,CONVERT(numeric(17,5),41869.166666666664)*1440,'1899-12-31')

The result of [Datetime + 420./1440] (7 hours) is wrong (precision in ms is lost)

Could pls explain why I' not able to get valid result ( with precision to miliseconds ) after doing this (adding 7hrs to date):
select getdate() as dt into #t1
select dt, dt+(7.*60/1440) as dt_new from #t1
I got:
dt dt_new
2010-10-25 04:56:33.640 2010-10-25 11:56:33.580
As you can see there is difference in 60ms for dt_new - WHY?
AFAIR I WAS able to do this sort of things in Oracle 10g - even with minutes:
date + 5/1440 -to produce 5-min periods of datetime.
UPD:
My mistake - the CORRECT way of doing this is:
select getdate(), getdate()+(convert(float, 5)/1440) as dt_new
I suppose there is wrong implicit convertion choosen by Ms Sql server.
UPD2:
Thanks for replies, but my point was "arifmetic" method - because there are cases when calculating dates without dateadd really improves Query Plan - by eliminating Table scan that is the case with Dateadd and other functions.
Anyway it works - thanks!
You could also use the inbuilt functions, such as
select dt, DATEADD(hour, 7, dt) as dt_new from #t1
My guess as to the imprecision in using + is that its converting from datetime to integer, then back again.
Actually, the even correcter way of adding 7 hours to a datetime is to use DATEADD:
select getdate() as dt into #t1
select dt, dateadd(hh, 7, dt) as dt_new from #t1
gives
dt dt_new
----------------------- -----------------------
2010-10-25 13:16:35.067 2010-10-25 20:16:35.067
You could achieve better result with dateadd() function (like previous posters noted). If you want use arithmetics, use better datatype, like float - cast(7 as float) instead of 7. is working well.