Convert float (YYYY) to datetime - sql

I'm relatively new to SQL, so please forgive me if this is a dumb question. I've been trying for too long now to get this to work.
I have a column in table A that is a float column called ConstructionYear. It is populated with a simple 4 digit year (i.e. 2010, 2005, 1972, etc.). I need to populate table B's YearBuilt datetime field using these years. I've searched and searched and tried all sorts of different combinations of convert() and cast() that I've found online, but it's not working.
What I would like to happen is this:
'2008' -> '2008-01-01 00:00:00.000'
'2005' -> '2005-01-01 00:00:00.000'
'1986' -> '1986-01-01 00:00:00.000'
Instead of what is currently happening (using CAST(ConstructionYear AS DATETIME)):
'2008' -> '1905-07-02 00:00:00.000'
'2010' -> '1905-07-04 00:00:00.000'
'1984' -> '1905-06-08 00:00:00.000'
EDIT: Solution: cast(convert(varchar,#ConstructionYear) AS DATETIME)
So my problem had 2 main causes (other than me being new to sql).
I didn't know about the 1900 epoch that SQL Server uses for datetime. I could tell something was going on because of all teh 1905 datetimes i saw, but i didn't know that it was taking my 2005 year and counting it as days from 1900.
The year 1753. Why is 1753 the earliest year we can use? I probably had the right syntax at some point before i posted my question here, but it didn't run because my data had some years predating 1753. I assumed the error was with my code.

Check this example:
DECLARE #ConstructionYearas FLOAT
SET #ConstructionYear = 2012
SELECT FloatToDatetime = CAST(convert(varchar(4),#ConstructionYear) as datetime)
It will output:
2012-01-01 00:00:00.000
Basically use:
CAST(convert(varchar(4),#ConstructionYear) as datetime)
where #ConstructionYear is your Float variable
Hope it helps!

Besides #Ghost's answer, if you are using SQL SERVER 2012
You can use
DATEFROMPARTS(ConstructionYear, 1, 1)
reference: http://msdn.microsoft.com/en-us/library/hh213228.aspx

This should WORK but there's probably a better way
CAST(CAST(ConstructionYear as nvarchar(4)) + '-01-01' as datetime)
What your query is doing is taking the number of days you are providing and adding it to SQL servers epoch, January 1st, 1900, which gives the results you saw

DECLARE #y FLOAT;
SELECT #y = 2012;
SELECT DATEADD(YEAR, #y-1900, 0);
Why did you think the numeric value 2012 translated directly to a number of years? When in fact in SQL Server a numeric value translates to the number of days since 1900-01-01. So the following should yield the same results:
SELECT CONVERT(DATETIME, 2012);
SELECT DATEADD(DAY, 2012, '19000101');
As others have suggested, I strongly suggest you to store this data correctly. While you could use something a little safer like SMALLINT, why not use DATE? This comes with built-in validation, ability to use all kinds of date/time functionality directly without conversion, and doesn't have any of the inherent rounding problems you might experience with an approximate data type like FLOAT. If you're storing a date, use a date data type.

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.

Using REPLACE to change datetime with SET

I haven't used this website before and am very new to SQL so please excuse any errors or if this question has been raised incorrectly - I have been asked to do something that is well outside of my remit and skill level!
I have many rows of data with a particular column - COLUMN - which has a DATETIME datatype, in a particular table - let's call it _data, and the data within it looks like this: 2019-11-12 17:00:00.000
I want to replace the YY-MM-DD portion of all of the columns in this table with today's date, 2019-11-28.
I am trying this code:
update DATABASE.dbo._data
set COLUMN = REPLACE(COLUMN,'2019-11-13','2019-11-28')
Which executes and informs me that every row in DATABASE has changed, but when I look at the data with a SELECT statement, it is the same as it was before. Why is this, do I need to do something different because the datatype is DATETIME? Google did not elucidate me and neither did w3schools or the other questions I searched for on here, but that's probably because I have no idea what I am doing.
I am using SQL SERVER 2012 on a Windows Server 2012 R2 box and I am running this code with SQL Server Management Studio v18.4.
DATETIMEFROMPARTS is your best friend here.
DECALRE #now DATETIME = GETDATE();
UPDATE DATABASE.dbo._data
SET COLUMN = DATETIMEFROMPARTS(
YEAR(#now),
MONTH(#now),
DAY(#now),
DATEPART(hour, COLUMN),
DATEPART(minute, COLUMN),
DATEPART(second, COLUMN),
DATEPART(millisecond, COLUMN));
This seems like a really add logic, but seems like the "easiest" method would be to use DATEADD and DATEDIFF:
UPDATE dbo.YourTable
SET DateTimeColumn = DATEADD(DAY, DATEDIFF(DAY, DateTimeColumn, GETDATE()), DateTimeColumn);

Function get the last day of month in sql

I need to get the last day of month with input of month and year. For example, with input 06/2016 it will return 30. I use SQL Server 2005. Thanks for any help.
Suppose your input is VARCHAR in the form of MM/YYYY.
Use RIGHT and LEFT to get the year and month respectively. Then use DATEFROMPARTS to generate the starting date. Next, use EOMONTH to get the last day of the month. Finally use DAY to extract the day part.
DECLARE #input VARCHAR(7) = '06/2016'
SELECT
DAY(
EOMONTH(
DATEFROMPARTS(CAST(RIGHT(#input,4) AS INT),CAST(LEFT(#input, 2) AS INT),1)
)
)
The above only works for SQL Server 2012+.
For SQL Server 2005, you can use DATEADD to generate the dates:
SELECT
DAY( -- Day part
DATEADD(DAY, -1, -- Last day of the month
DATEADD(MONTH, CAST(LEFT(#input, 2) AS INT), -- Start of next month
DATEADD(YEAR, CAST(RIGHT(#input, 4) AS INT) - 1900, 0) -- Start of the year
)
)
)
Reference:
Some Common Date Routines
You would do something like this:
select eomonth(getdate(), 0);
If you want it formatted as MM/YYYY then you'd do this:
select format(eomonth(getdate(), 0), 'MM/yyyy');
Pardon me for tossing-in a response that is not specific to "SQL Server," nor thence to "2005," but the generalized way to compute the answer that you seek is as follows:
Break down the input that you have, e.g. 06/2016, into two parts. Call 'em #MONTH and #YEAR. Define a third value, #DAY, equal to 1.
Typecast this into a date-value ... "June 1, 2016."
Now, using the date-handling functions that you're sure to have, "first add one month, then subtract one day."
One thing that you must be very careful of, when designing code like this, is to be certain(!) that your code for decoding 06/2016 works for every(!) value that actually occurs in that database, or that it can be relied upon to fail.
try this,
declare #input varchar(20)='06/2016'
set #input=#input+'/01'
declare #dtinput datetime=#input
select dateadd(day,-1,dateadd(month,datediff(month,0,#dtinput)+1,0))
--OR in sql server 2012
select eomonth(#dtinput)

How do you extract just date from datetime in T-Sql?

I am running a select against a datetime column in SQL Server 2005. I can select only the date from this datetime column?
Best way is:
SELECT DATEADD(day, DATEDIFF(Day, 0, #ADate), 0)
This is because internally, SQL Server stores all dates as two integers, of which the first one is the ****number of days*** since 1 Jan 1900. (the second one is the time portion, stored as the number of seconds since Midnight. (seconds for SmallDateTimes, or milleseconds for DateTimes)
Using the above expression is better because it avoids all conversions, directly reading and accessing that first integer in a dates internal representation without having to perform any processing... the two zeroes in the above expression (which represent 1 Jan 1900), are also directly utilized w/o processing or conversion, because they match the SQL server internal representation of the date 1 jan 1900 exactly as presented (as an integer)..
*NOTE. Actually, the number of date boundaries (midnights) you have to cross to get from the one date to the other.
Yes, by using the convert function. For example:
select getdate(), convert(varchar(10),getdate(),120)
RESULTS:
----------------------- ----------
2010-05-21 13:43:23.117 2010-05-21
You can use the functions:
day(date)
month(date)
year(date)
Also the Datepart() function might be of some use:
http://msdn.microsoft.com/en-us/library/ms174420(SQL.90).aspx
DECLARE #dToday DATETIME
SET #dToday = CONVERT(nvarchar(20), GETDATE(), 101)
SELECT #dToday AS Today
This returns today's date at 12:00am : '2010-05-21 00:00:00.000'
Then you can use the #dToday variable in a query as needed
CONVERT (date, GETUTCDATE())
CONVERT (date, GETDATE())
CONVERT (date, '2022-18-01')
I don't know why the others recommend it with varchar(x) tbh.
https://learn.microsoft.com/de-de/sql/t-sql/functions/getdate-transact-sql

Best approach to remove time part of datetime in SQL Server

Which method provides the best performance when removing the time portion from a datetime field in SQL Server?
a) select DATEADD(dd, DATEDIFF(dd, 0, getdate()), 0)
or
b) select cast(convert(char(11), getdate(), 113) as datetime)
The second method does send a few more bytes either way but that might not be as important as the speed of the conversion.
Both also appear to be very fast, but there might be a difference in speed when dealing with hundreds-of-thousands or more rows?
Also, is it possible that there are even better methods to get rid of the time portion of a datetime in SQL?
Strictly, method a is the least resource intensive:
a) select DATEADD(dd, DATEDIFF(dd, 0, getdate()), 0)
Proven less CPU intensive for the same total duration a million rows by someone with way too much time on their hands: Most efficient way in SQL Server to get a date from date+time?
I saw a similar test elsewhere with similar results too.
I prefer the DATEADD/DATEDIFF because:
varchar is subject to language/dateformat issues
Example: Why is my CASE expression non-deterministic?
float relies on internal storage
it extends to work out first day of month, tomorrow, etc by changing "0" base
Edit, Oct 2011
For SQL Server 2008+, you can CAST to date i.e. CAST(getdate() AS date). Or just use date datatype so no time to remove.
Edit, Jan 2012
A worked example of how flexible this is: Need to calculate by rounded time or date figure in sql server
Edit, May 2012
Do not use this in WHERE clauses and the like without thinking: adding a function or CAST to a column invalidates index usage. See number 2 here Common SQL Programming Mistakes
Now, this does have an example of later SQL Server optimiser versions managing CAST to date correctly, but generally it will be a bad idea ...
Edit, Sep 2018, for datetime2
DECLARE #datetime2value datetime2 = '02180912 11:45' --this is deliberately within datetime2, year 0218
DECLARE #datetime2epoch datetime2 = '19000101'
select DATEADD(dd, DATEDIFF(dd, #datetime2epoch, #datetime2value), #datetime2epoch)
In SQL Server 2008, you can use:
CONVERT(DATE, getdate(), 101)
Of-course this is an old thread but to make it complete.
From SQL 2008 you can use DATE datatype so you can simply do:
SELECT CONVERT(DATE,GETDATE())
In SQL Server 2008, there is a DATE datetype (also a TIME datatype).
CAST(GetDate() as DATE)
or
declare #Dt as DATE = GetDate()
SELECT CAST(FLOOR(CAST(getdate() AS FLOAT)) AS DATETIME)
...is not a good solution, per the comments below.
I would delete this answer, but I'll leave it here as a counter-example since I think the commenters' explanation of why it's not a good idea is still useful.
Here's yet another answer, from another duplicate question:
SELECT CAST(CAST(getutcdate() - 0.50000004 AS int) AS datetime)
This magic number method performs slightly faster than the DATEADD method. (It looks like ~10%)
The CPU Time on several rounds of a million records:
DATEADD MAGIC FLOAT
500 453
453 360
375 375
406 360
But note that these numbers are possibly irrelevant because they are already VERY fast. Unless I had record sets of 100,000 or more, I couldn't even get the CPU Time to read above zero.
Considering the fact that DateAdd is meant for this purpose and is more robust, I'd say use DateAdd.
SELECT CAST(CAST(GETDATE() AS DATE) AS DATETIME)
I really like:
[date] = CONVERT(VARCHAR(10), GETDATE(), 120)
The 120 format code will coerce the date into the ISO 8601 standard:
'YYYY-MM-DD' or '2017-01-09'
Super easy to use in dplyr (R) and pandas (Python)!
BEWARE!
Method a) and b) does NOT always have the same output!
select DATEADD(dd, DATEDIFF(dd, 0, '2013-12-31 23:59:59.999'), 0)
Output: 2014-01-01 00:00:00.000
select cast(convert(char(11), '2013-12-31 23:59:59.999', 113) as datetime)
Output: 2013-12-31 00:00:00.000
(Tested on MS SQL Server 2005 and 2008 R2)
EDIT: According to Adam's comment, this cannot happen if you read the date value from the table, but it can happen if you provide your date value as a literal (example: as a parameter of a stored procedure called via ADO.NET).
See this question:
How can I truncate a datetime in SQL Server?
Whatever you do, don't use the string method. That's about the worst way you could do it.
Already answered but ill throw this out there too...
this suposedly also preforms well but it works by throwing away the decimal (which stores time) from the float and returning only whole part (which is date)
CAST(
FLOOR( CAST( GETDATE() AS FLOAT ) )
AS DATETIME
)
second time I found this solution... i grabbed this code off
CAST(round(cast(getdate()as real),0,1) AS datetime)
This method does not use string function. Date is basically a real datatype with digits before decimal are fraction of a day.
this I guess will be faster than a lot.
For me the code below is always a winner:
SELECT CONVERT(DATETIME, FLOOR(CONVERT(FLOAT,GETDATE())));
select CONVERT(char(10), GetDate(),126)
Strip time on inserts/updates in the first place. As for on-the-fly conversion, nothing can beat a user-defined function maintanability-wise:
select date_only(dd)
The implementation of date_only can be anything you like - now it's abstracted away and calling code is much much cleaner.
I think you mean
cast(floor(cast(getdate()as float))as datetime)
real is only 32-bits, and could lose some information
This is fastest
cast(cast(getdate()+x-0.5 as int)as datetime)
...though only about 10% faster(about 0.49 microseconds CPU vs. 0.58)
This was recommended, and takes the same time in my test just now:
DATEADD(dd, DATEDIFF(dd, 0, getdate()), 0)
In SQL 2008, the SQL CLR function is about 5 times faster than using a SQL function would be, at 1.35 microseconds versus 6.5 microsections, indicating much lower function-call overhead for a SQL CLR function versus a simple SQL UDF.
In SQL 2005, the SQL CLR function is 16 times faster, per my testing, versus this slow function:
create function dateonly ( #dt datetime )
returns datetime
as
begin
return cast(floor(cast(#dt as float))as int)
end
How about select cast(cast my_datetime_field as date) as datetime)? This results in the same date, with the time set to 00:00, but avoids any conversion to text and also avoids any explicit numeric rounding.
I think that if you stick strictly with TSQL that this is the fastest way to truncate the time:
select convert(datetime,convert(int,convert(float,[Modified])))
I found this truncation method to be about 5% faster than the DateAdd method. And this can be easily modified to round to the nearest day like this:
select convert(datetime,ROUND(convert(float,[Modified]),0))
Here I made a function to remove some parts of a datetime for SQL Server. Usage:
First param is the datetime to be stripped off.
Second param is a char:
s: rounds to seconds; removes milliseconds
m: rounds to minutes; removes seconds and milliseconds
h: rounds to hours; removes minutes, seconds and milliseconds.
d: rounds to days; removes hours, minutes, seconds and milliseconds.
Returns the new datetime
create function dbo.uf_RoundDateTime(#dt as datetime, #part as char)
returns datetime
as
begin
if CHARINDEX( #part, 'smhd',0) = 0 return #dt;
return cast(
Case #part
when 's' then convert(varchar(19), #dt, 126)
when 'm' then convert(varchar(17), #dt, 126) + '00'
when 'h' then convert(varchar(14), #dt, 126) + '00:00'
when 'd' then convert(varchar(14), #dt, 112)
end as datetime )
end
Just in case anyone is looking in here for a Sybase version since several of the versions above didn't work
CAST(CONVERT(DATE,GETDATE(),103) AS DATETIME)
Tested in I SQL v11 running on Adaptive Server 15.7
If possible, for special things like this, I like to use CLR functions.
In this case:
[Microsoft.SqlServer.Server.SqlFunction]
public static SqlDateTime DateOnly(SqlDateTime input)
{
if (!input.IsNull)
{
SqlDateTime dt = new SqlDateTime(input.Value.Year, input.Value.Month, input.Value.Day, 0, 0, 0);
return dt;
}
else
return SqlDateTime.Null;
}
I, personally, almost always use User Defined functions for this if dealing with SQL Server 2005 (or lower version), however, it should be noted that there are specific drawbacks to using UDF's, especially if applying them to WHERE clauses (see below and the comments on this answer for further details). If using SQL Server 2008 (or higher) - see below.
In fact, for most databases that I create, I add these UDF's in right near the start since I know there's a 99% chance I'm going to need them sooner or later.
I create one for "date only" & "time only" (although the "date only" one is by far the most used of the two).
Here's some links to a variety of date-related UDF's:
Essential SQL Server Date, Time and DateTime Functions
Get Date Only Function
That last link shows no less than 3 different ways to getting the date only part of a datetime field and mentions some pros and cons of each approach.
If using a UDF, it should be noted that you should try to avoid using the UDF as part of a WHERE clause in a query as this will greatly hinder performance of the query. The main reason for this is that using a UDF in a WHERE clause renders that clause as non-sargable, which means that SQL Server can no longer use an index with that clause in order to improve the speed of query execution. With reference to my own usage of UDF's, I'll frequently use the "raw" date column within the WHERE clause, but apply the UDF to the SELECTed column. In this way, the UDF is only applied to the filtered result-set and not every row of the table as part of the filter.
Of course, the absolute best approach for this is to use SQL Server 2008 (or higher) and separate out your dates and times, as the SQL Server database engine is then natively providing the individual date and time components, and can efficiently query these independently without the need for a UDF or other mechanism to extract either the date or time part from a composite datetime type.
I would use:
CAST
(
CAST(YEAR(DATEFIELD) as varchar(4)) + '/' CAST(MM(DATEFIELD) as varchar(2)) + '/' CAST(DD(DATEFIELD) as varchar(2)) as datetime
)
Thus effectively creating a new field from the date field you already have.