Removing millisecond from datetime throwing error - sql

I have this code in SQL Server 2016
SELECT CONVERT (DATETIME, CONVERT(varchar(8), ExpiryDate))
I get this result:
ExpiryDate
------------------------
2020-08-03 00:00:00.000
How can I remove the .000 (the milliseconds part)?
Expected result should be:
2020-08-03 00:00:00
Please help

By not using a datetime, which is accurate to 1/300th of a second. Instead define your value as a datetime2(0), which is accurate to 1 second (due to having a precision of 0 on milliseconds):
SELECT CONVERT(datetime2(0),ExpiryDate)
FROM ...

You are confusing the internal format and the display format. If you want the value formatted in a particular way, then format it explicitly:
select CONVERT(VARCHAR(19), expirydate, 121)
You can add this into the table as a computed column:
alter table t add expirydate_display as (CONVERT(VARCHAR(19), expirydate, 121))

Related

Why isn't SQL Server letting me store '21/04/17' as a date?

I've got a table that currently has all columns stored as nvarchar(max), so I'm converting all the datatypes to be what they should be. I have a column of dates, however when I run this:
ALTER TABLE Leavers ALTER COLUMN [Actual_Termination_Date] date;
I get
"Conversion failed when converting date and/or time from character string".
This is relatively normal, so I did the following to investigate:
SELECT DISTINCT TOP 20 [Actual_Termination_Date]
FROM LEAVERS
WHERE ISDATE([Actual_Termination_Date]) = 0
which returned:
NULL
13/04/2017
14/04/2017
17/04/2017
19/04/2017
21/04/2017
23/04/2017
24/04/2017
26/04/2017
28/04/2017
29/03/2017
29/04/2017
30/04/2017
31/03/2017
42795
42797
42813
42817
42820
42825
The null and excel style date formats (e.g. 42795) are no problem, however it's the ones appearing as perfectly normal dates I'm having a problem with. I usually fix issues like this by using one of the following fixes:
SELECT cast([Actual_Termination_Date] - 2 as datetime)
FROM LEAVERS
WHERE ISDATE([Actual_Termination_Date]) = 0
or
SELECT cast(convert(nvarchar,[Actual_Termination_Date], 103) - 2 as datetime)
FROM LEAVERS
WHERE ISDATE([Actual_Termination_Date]) = 0
When these return back the dates as I would expext, I'd then do an UPDATE statement to change them in the table and then convert the column type. However I keep getting an error message telling me that various dates can't be converted such as:
Conversion failed when converting the nvarchar value '21/04/2017' to data type int.
Any thoughts? Thanks!
Probably because of your language setting. For '21/04/2017' to work, you'll need to be using the BRITISH language, or other language that uses dd/MM/yyyy. I suspect you are using ENGLISH which is actually American.
American's use MM/dd/yyyy meaning that '21/04/2017' would mean the 4th day of the 21st month in the year 2017; obviously that doesn't work.
The best method is to use an unambiguous format, regardless of language and data type. For SQL Server that's yyyyMMdd and yyyy-MM-ddThh:mm:ss.nnnnnnn (yyyy-MM-dd and yyyy-MM-dd hh:mm:ss.nnnnnnn are not unambiguous in SQL Server when using the older datetime and smalldatetime data types).
Otherwise you can use CONVERT with a style code:
SELECT CONVERT(date,'21/04/2017', 103)
The problem with your data, however, is that you have values that are in the format dd/MM/yyyy and integer values. The int (not varchar) value 42817 as a datetime in SQL Server is 2017-03-25. On the other hand, if this data came from Excel then the value is 2017-03-23. I am going to assume the data came from Excel, not SQL Server (because the ACE drivers have a habit of reading dates as numbers, because the thing they aren't is "ace").
You'll need to therefore convert the values to an unambiguous format first, so that'll be yyyyMMdd. As we have 2 different types of values, this is a little harder, but still possible:
UPDATE dbo.Leavers
SET Actual_Termination_Date = CONVERT(varchar(8), ISNULL(TRY_CONVERT(date, Actual_Termination_Date, 103), DATEADD(DAY, TRY_CONVERT(int, Actual_Termination_Date),'18991230')), 112);
Then you can alter your table:
ALTER TABLE dbo.Leavers ALTER COLUMN [Actual_Termination_Date] date;
DB<>Fiddle using MichaƂ Turczyn's DML statement.
Put the column into a canonical format first, then convert:
update leavers
set Actual_Termination_Date = try_convert(date, [Actual_Termination_Date], 103);
ALTER TABLE Leavers ALTER COLUMN [Actual_Termination_Date] date;
The update will do an implicit conversion from the date to a string. The alter should be able to "undo" that implicit conversion.
Back up the table before you do this! You are likely to discover that some dates are not valid -- that is pretty much the rule when you store dates as strings although in a small minority of cases, all date strings are actually consistently formatted.
The actual date does not matter. The error happens when you try to subtract 2 from a string:
[Actual_Termination_Date] - 2
The clue comes from the error message:
Conversion failed when converting the nvarchar value '21/04/2017' to data type int.
To fix the problem, use DATEADD after the conversion:
SELECT DATEADD(days, -2, convert(datetime, [Actual_Termination_Date], 103))
You just have inconsistent date format within your column, which is terrible.
Having wrong datatype lead to it, that's why it is so important to have proper data types on columns.
Let's investigate it a little:
-- some test data
declare #tbl table (dt varchar(20));
insert into #tbl values
(NULL),
('13/04/2017'),
('14/04/2017'),
('17/04/2017'),
('19/04/2017'),
('21/04/2017'),
('23/04/2017'),
('24/04/2017'),
('26/04/2017'),
('28/04/2017'),
('29/03/2017'),
('29/04/2017'),
('30/04/2017'),
('31/03/2017'),
('42795'),
('42797'),
('42813'),
('42817'),
('42820'),
('42825');
-- here we handle one format
select convert(date, dt, 103) from #tbl
where len(dt) > 5
or dt is null
-- here we handle excel like format
select dateadd(day, cast(dt as int), '1900-01-01') from #tbl
where len(dt) = 5
So, as you can see you have to apply to different approaches for this task. CASE WHEN statement should fit here nicely, see below SELECT:
select case when len(dt) = 5 then
dateadd(day, cast(dt as int), '1900-01-01')
else convert(date, dt, 103) end
from #tbl

Converting "42978.6736458333" varchar to datetime

I've a varchar column with a value of 42978.6736458333 that I want to convert back to a proper datetime. I've searched quite a bit and tried many of the suggestions but I cannot seem to find one that works with the data I have.
I got this value from excel when I did a CONCATENATE of all the fields in the sheet to do an insert to my DB; the output of the datetime column looks like "42978.6736458333" (the cell originally contained "2017-08-31 16:10:03"). I tried formatting and various things in excel to no avail.
Here are a few examples of what I've tried:
Select
convert(varchar(23), date, 112) DATE1,
convert(datetime, '20160805') DATE2,
convert(datetime, '2011-09-28 18:01:00', 120) DATE3,
dateadd(second, 42978.6736458333 * 24*24*60, '1899-12-31') DATE4
From
[dbo].[trainingLog]
Results:
DATE 1 = 42978.6736458333
DATE 2 = 2016-08-05 00:00:00.000
DATE 3 = 2011-09-28 18:01:00.000
DATE 4 = 1947-01-25 11:16:01.000
For every result. DATE 2/3/4 don't count up even though the original datetime varchar increments.
For example, here are more varchar values:
42981.5092361111
42982.7187615741
42983.8171527778
The above attempts return a value/date, but it's the same date even though my varchar value increments.
I expect any datetime format. I really only want the month/day/year in any format.
Try
SELECT CAST(42978.6736458333 AS DATETIME)
returns
2017-09-02 16:10:03.000
However SQL server uses 01/01/1900 as the epoch whereas your excel uses 30/12/1899 or 31/12/1899 as the epoch so it looks like you need to subtract 2 days off after you cast.
e.g.
SELECT dateadd(d, -2, CAST(42978.6736458333 AS DATETIME) )
returns
2017-08-31 16:10:03.000
From you comment I am not sure how you get your value as running
SELECT CAST(42981.5092361111 AS DATETIME)
returns for me
2017-09-05 12:13:18.000
This looks like an Excel time. So:
select dateadd(minute, 42978.6736458333 * 24*60, '1899-12-31')
If you try to use seconds, you will get an overflow error. If seconds are important, you can do:
select dateadd(day, floor(42978.6736458333), '1899-12-31') +
dateadd(second, (42978.6736458333 % 1)*24*60*60, 0)

Date is string between hyphens in SQL Server

I have date formats that are as follows:
Date_str
19-12-2007
31-7-2009
3-1-2010
31-11-2009
etc.
I can't do the following:
CONCAT(RIGHT(Date_str,4),SUBSTRING(Date_str,3,3),LEFT(2))
because as you can see above, the dates are not the same length. Is there a way in SQL Server to extract the date as datetime/date?
I also tried
Convert(datetime, Date_str)
but it just threw an error:
The conversion of a varchar data type to a datetime data type resulted
in an out-of-range value.
If 2012+, I would use Try_Convert(). This will return bogus dates as NULL.
Example
Declare #YourTable Table ([Date_str] varchar(50))
Insert Into #YourTable Values
('19-12-2007')
,('31-7-2009')
,('3-1-2010')
,('31-11-2009')
Select *
,try_convert(date,Date_Str,105)
from #YourTable
Returns
Date_str (No column name)
19-12-2007 2007-12-19
31-7-2009 2009-07-31
3-1-2010 2010-01-03
31-11-2009 NULL -- Notice 11/31 is NOT a date
See https://learn.microsoft.com/en-us/sql/t-sql/functions/cast-and-convert-transact-sql for date formats
You probably need
CONVERT(DateTime, Date_str, 105)
As I mentioned in the comments, the only realistic solution is to convert that string into a proper date-typed column. The current format doesn't allow date sorting, or search for a range of dates, eg to find entries in the last week, or between one date and the other.
Parsing with CONVERT or TRY_PARSE means that no indexes can be used to speed up queries. Each time :
WHERE CONVERT(Date, Date_str, 105) > '20170101'
is used, the server will have to scan the entire table to convert the data, then filter the rows.
If you can't change the type of the field itself, you can create a persisted computed column that returns the value as a date and add indexes to it. You'll be able to use that column for indexed querying:
alter table SomeTable add date2 as TRY_convert(Actual_Date,date_str,105) PERSISTED
create index IX_SomeTable_ActualDate on SomeTable (Actual_Date)
This will allow you to perform sorting without tricks:
SELECT *
FROM SomeTable
ORDER BY Actual_Date
Or run range queries that take advantage of the IX_SomeTable_ActualDate index:
SELECT *
FROM SomeTable
Where Actual_Date Between DATEADD(d,-7,GETDATE()) AND GETDATE()
If you have 1000 rows, you could get 1000 times better performance.
Existing applications won't even notice the change. Newer queries and applications will be able to take advantage of indexing and sorting
I had a similar problem: my column (<my_date_field>) had date values in the form of
2021-01
2021-02
2021-10
2021-12
and so on, with data type of nvarchar(4000), and I always ran into the Conversion failed when converting date and/or time from character string. error (when trying e.g. CAST(<my_date_field> AS DATE) or CAST(CAST(<my_date_field> AS VARCHAR(7)) AS DATE) etc.)
I was able to convert them to date with the following code:
SELECT
CONVERT(date, <my_date_field> + '-01') AS first_day_of_month
FROM my_table
which resulted in
2021-08-01
2021-07-01
2021-06-01
2021-05-01

SQL Server - add DATE part of DATETIME to TIME part of DATETIME

In SQL Server 2005 I am interrogating some old legacy data and I need to combine the date component of a datetime column with the time component from another column. Here's an example:
DateColumn: 2016-05-09 00:00:00.000
TimeColumn: 1899-12-30 12:26:00.000
I need the end result converted to the following DateTime:
ResultDateTime: 2016-05-09 12:26:00.000
I tried using:
CAST(DateColumn AS DATETIME) + CAST(TimeColumn AS TIME) AS ResultDateTime
But SQL Server 2005 doesn't recognize the type TIME.
Can someone please show me a way of doing that?
Many thanks!
convert the time column to string HH:MM:SS and add to the date column
ResultDatetIme = DateColumn + convert(varchar(10), TimeColumn, 108)
You can just use DATEADD and DATEDIFF, assuming that the time column's date portion is always 30/12/1899:
declare #t table (DateColumn datetime,TimeColumn datetime)
insert into #t(DateColumn,TimeColumn) values
('2016-05-09T00:00:00.000','1899-12-30T12:26:00.000')
select DATEADD(millisecond,DATEDIFF(millisecond,'18991230',TimeColumn),DateColumn)
from #t
Result:
-----------------------
2016-05-09 12:26:00.000
As you can see following data type supported in SQL 2005:
https://msdn.microsoft.com/en-us/library/ms187819(v=sql.90).aspx
Use datetime and smalldatetime, The smalldatetime data type stores dates and times of day with less precision than datetime. The Database Engine stores smalldatetime values as two 2-byte integers. The first 2 bytes store the number of days after January 1, 1900. The other 2 bytes store the number of minutes since midnight.
datetime values are rounded to increments of .000, .003, or .007 seconds, as shown in the following table.
SELECT CAST('2016-05-09 00:00:00.000' AS DATETIME) + CAST('1900-01-01 12:26:00.000' AS smalldatetime) AS ResultDateTime
Result: 2016-05-09 12:26:00.000
So you can use datetime and smalldatetime, hope fully that will work for you.
Let me know if any issue. love to resolve :)

select statement using Between with datetime type does not retrieve all fields?

I'm facing a strange query result and I want to ask you why I'm facing this issue.
I store some datetime data into TestTable as following :
creation_time
-----------------------
2010-07-10 00:01:43.000
2010-07-11 00:01:43.000
2010-07-12 00:01:43.000
This table is created and filled as following :
create table TestTable(creation_time datetime);
Insert into TestTable values('2010-07-10 00:01:43.000');
Insert into TestTable values('2010-07-11 00:01:43.000');
Insert into TestTable values('2010-07-12 00:01:43.000');
when I execute this query , I get two rows only instead of three as I expected:
SELECT * FROM TestTable
WHERE creation_time BETWEEN CONVERT(VARCHAR(10),'2010-07-10',111) -- remove time part
and CONVERT(VARCHAR(10),'2010-07-12',111) -- remove time part
Or if I execute this query , the same issue ..
SELECT * FROM TestTable
WHERE CONVERT(datetime,creation_time,111) BETWEEN CONVERT(VARCHAR(10),'2010-07-10',111) -- remove time part
and CONVERT(VARCHAR(10),'2010-07-12',111) -- remove time part
My Question :
Why the last row ('2010-07-12 00:01:43.000') does not appear in
the result even if I set the date range to cover all the day from 2010-07-10 to 2010-07-12?
I use Sql server 2005 express edition with windows xp 32-bits.
I'm trying to don't use a workaround solution such as increasing the date range to cover additional day to get the days I want.
Thanks .
You need to remove the time part from creation_time as well. Just use the same CONVERT if it works.
Currently you're asking if 2010-07-12 00:01:43.000 is less than 2010-07-12 00:00:00.000, which is not true.
it does not show the date because you have removed the time part, which would make the date equivalent to '2010-07-12 00:00:00.000' and since the last row is greater than this, so it is not displaying in the query results.
Your script should look like this:
SELECT *
FROM TestTable
WHERE creation_time BETWEEN
convert(datetime, convert(char, '2010-07-10', 106))-- remove time part
and **DATEADD**(day, 1, convert(datetime, convert(char, '2010-07-**11**', 106))) -- remove time part and add 1 day
This script will return all between 2010-07-10 00:00:00 and 2010-07-12 00:00:00. Basically this means all items created in 2 days: 2010-07-10 and 2010-07-11.
Converting columns in your table for comparison can be costly and cause indexes to not be used. If you have a million rows in your table and you have an index on creation_time, you will be doing an index scan and converting all million values to a string for comparison.
I find it better to use >= the start date and < (end date + 1 day):
SELECT *
FROM TestTable
WHERE creation_time >= '2010-07-10'
AND creation_time < dateadd(day, 1, '2010-07-12')
And the reason your second one may not work is because format 111 uses slashes ("2010/07/10"), format 120 uses dashes ("2010-07-10"). Your converts aren't doing anything to your start and end date because you are converting a string to varchar, not a date. If you did this, it might work, but I would still recommend not doing the conversion:
SELECT * FROM TestTable
WHERE CONVERT(datetime, creation_time, 111) BETWEEN
CONVERT(VARCHAR(10), CONVERT(datetime, '2010-07-10'), 111) -- remove time part
and CONVERT(VARCHAR(10), CONVERT(datetime, '2010-07-12'), 111) -- remove time part
Date/time inclusive between 7/10/2010 and 7/12/2010:
SELECT * FROM TestTable
WHERE creation_time BETWEEN
CONVERT(VARCHAR,'2010-07-10',101) -- remove time part
and CONVERT(VARCHAR,'2010-07-13',101) -- remove time part