I have a date in Datetime2 format and it is coming up as yyyy-mm-dd. Is there a way to reformat it so it is mm-dd-yyyy?
CASE
WHEN CAST(ai.[Due Date] AS DATETIME2) < GETDATE() THEN '[due]' + LEFT((ai.[Due Date]),10)
WHEN CAST(ai.[Due Date] AS DATETIME2) IS NULL THEN ' '
ELSE LEFT((ai.[Due Date]),10)
END AS [TD]
The traditional way is to use convert():
convert(varchar(10), ai.[Due Date], 110)
A more versatile method uses format():
select format(ai.[Due Date], 'dd-MM-yyyy')
You misunderstand how date values work in a database. There is no human-readable format. When you see DateTime or DateTime2 values formatted as yyyy-mm-dd what you're seeing is something shown by your debugger or query tool for convenience; the actual value used in the database is binary, not human readable, and is intended to be efficient for storage, indexing, and date arithmetic.
If you need to see a value in a specific format, you must convert() or format() it to a type in the varchar family as part of the query. Or, even better, let your application code/reporting tool do this for you, and just return the original value.
I also see indication these dates are potentially stored originally in a varchar, or nvarchar column. If so, it is a major flaw in the schema design. You will get significant performance benefits and save yourself some big headaches down the road if you can start storing these values using a type from the DateTime family in the first place.
With this in mind, and because it's not clear what you're starting from, let's look at five scenarios, in order of preference:
The column already uses a type from the DateTime family, and you can let your application/reporting tool handle the format
Good for you using a real DateTime value in the schema. That's what we expect to see. Even better, suddenly everything gets really simple in your SQL and the entire snippet in the question reduces to just this:
ai.[Due Date] AS [TD]
The column already uses a type from the DateTime family, but the client system can't format
This is still pretty good. The schema is still okay, and in this case we can still simplify the original code somewhat:
COALESCE(
CASE WHEN ai.[Due Date] < GETDATE() THEN '[due] ' ELSE '' END
+ FORMAT(ai.[Due Date], 'MM-dd-yyyy')
, ' ') AS [TD]
The column uses the a type from varchar family, but you can fix it to use DateTime2
I say "fix" here, because now the schema really is broken as is. But that's okay: you can fix it. Do that. Then use the code from a previous scenario.
The column uses the a type from varchar family and you can't fix it, but at least the raw data always uses a semantic 'yyyy-MM-dd` format
Bummer. You're stuck with a broken schema. But we can at least take advantage of the well-formatted data to make things much more efficient by using cast/convert on the get_date() expression to match the column, rather than vice versa as it is now, like this:
WHEN ai.[Due Date] < CONVERT(varchar, GETDATE(), 120)
Now we're doing a string comparison instead of a date comparison, which is generally slower and, well, just wrong. But we can get away with it because of the nice format in the data, and the saving grace is we only need to cast the one get_date() value, rather than every single row we have. Moreover, this way any index on the column would still be valid. The code snippet on the question would be unable to use any index on the [Due Date] column. I know this is a SELECT clause, but this is worth remembering for the general case.
The full solution for this scenario now looks like this:
COALESCE(
CASE WHEN ai.[Due Date] < CONVERT(varchar, GETDATE(), 120) THEN '[due] ' ELSE '' END
+ FORMAT(CAST(ai.[Due Date]) AS Date), 'MM-dd-yyyy')
, ' ') AS [TD]
Again, only do this if you can't get your raw column data into a DateTime format. That is what you really want here.
The column uses the a type from varchar family, you can't fix it, and the format is not semantic or not consistent
Oh boy. This is where you really don't want to be. If you can do nothing else, at least see if you can start getting consistent and semantic values into your column. At this point, we are stuck with doing extra work on every row we have (possibly more than once) for pretty much every query. Here we go:
COALESCE(
CASE WHEN CAST(ai.[Due Date] AS DATETIME2) < GETDATE() THEN '[due] ' ELSE '' END
+ FORMAT(CAST(ai.[Due Date] AS DATETIME2), 'MM-dd-yyyy')
, ' ') AS [TD]
The code doesn't look much different than other options, but the performance characteristics will be extremely different... potentially multiple orders of magnitude worse.
Remember: because of internationalization and time zone issues, converting between strings and dates is surprisingly slow and expensive. Avoid doing that whenever possible in all your queries.
Related
I am not sure if this has already been answered but I could not find anything.
I am trying to convert the column of dates from MM/DD/YYYY to DD/MM/YYYY.
I must confess that I don't even know where to begin on this. I was thinking I might try an update statement but I am unsure as to how to format it. Any help would be appreciated.
You need to do two level of conversation :
select convert(varchar(12), convert(date, datecol, 101), 103)
In general, you need to fix the data-type (if that is in your hand) instead of doing conversation in SQL. Date formatting should be done at the presentation layer.
You can try this by fixing the data type in the actual table. For displaying purpose either on a webpage, reports or whatever.
SQL Server provides a number of options you can use to format a date/time string. In select, you can try one of the suggested methods as here.
For all these conversions you need to pass the date values in proper data type which may be the date or date-time.
Here is one of the examples of your illustration.
declare #DateInString varchar(20) = Cast(getdate() as Varchar(20))
select convert(varchar(12), convert(date, #DateInString, 101), 103)
You could also do Select Format(datecol, 'dd/MM/yyyy')
This will return your datetime field as a varchar - you should then be able to insert that into your target.
I should preface my question by saying I am very new to SQL (or any programming involving databases). I started learning SQL a couple of weeks ago when I decided to take on a data project.
I have been using SSMS in wrangling large tables in comma-separated text file format. I need to be able to sort by dates, and the current format is mmddyyyy, but when I try to sort by date it does not work.
I know that converting dates is something that gets asked a lot around here, but I haven't found any solutions that explain things for a newb like myself.
So far my guesses for a solution are to use the CONVERT or CAST solutions, but I'm not sure if that is the right approach. I have found several CAST/CONVERT posts but none have really applied to my situation.
I hate to have this as my first question, but I'd thought I'd take some down vote bullets if it means I could figure this out. Thank you.
Sample of what I'm trying to do:
SELECT *
FROM [databasename].[dbo].[table1]
WHERE [ column1] > 01012017;
I get the entire table back, unsorted.
Since your:
SELECT *
FROM [databasename].[dbo].[table1]
WHERE [ column1] > 01012017;
does not error, we could say that the [column1]'s datatype is either a character type (like VARCHAR, char), or datetime.
Since you are getting back all the data and I would think you don't have data in the future, it should be a varchar (or char) - with datetime that means 1900-01-01 + 1012017 days.
To make it a datetime you need to 'cast' your column1 which is in mmddyyyy form by first converting it to yyyymmdd style (which would work under any date and language setting):
cast(right([column1],4)+substring([column1],1,2)+substring([column1],3,2) as datetime)
and you would write that 01012017 as a string (quotes around) and also again in yyyymmdd format (it would be implicitly casted to datetime):
'20170101'
So your SQL becomes:
SELECT *
FROM [databasename].[dbo].[table1]
WHERE cast(right([column1],4) +
substring([column1],1,2) +
substring([column1],3,2) as datetime) > '20170101';
Having a date\datetime column as varchar and using like this would render the ability to use simple indexes but that is another matter. For now, this would return the data you want.
Assuming your column's datatype is [Date], try something similar to:
SELECT *
FROM [databasename].[dbo].[table1]
WHERE FORMAT([column1],'dd/MM/yyyy') >'01012017'
If it's string format, you'll have to use CONVERT() to convert the column to Date with a query like
SELECT *
FROM [databasename].[dbo].[table1]
WHERE CONVERT(NVARCHAR(10), [Column1], 112) >'01012017'
Refer to this W3Schools article if you need more help with the CONVERT clause
The goal is to, by default, populate all the input fields of a query with a '%' and let the user put in real values wherever they want. However, this is causing a problem for the DATE/TIME field. I get the error: Conversion failed when converting date and/or time from character string. How can I achieve the equivalent of '%' but for dates?
DECLARE #trans_date datetime = '%'
SELECT
t.lp_num,
t.trans_date
FROM ISW_LPTrans AS t
WHERE t.trans_date = #trans_date
Edit: - Removed the LIKE and replaced with = because people kept focusing too much on that and it's not the focus of this question.
This is too long for a comment. Your query doesn't make sense. LIKE is for strings and dates are not strings.
If you like, you can convert the date to a canonical string format and then use that.
However, date pickers are the more typical solution for choosing dates, and the resulting code does not use LIKE.
You cannot use LIKE keywords directly on dates. LIKE is mostly used for string values.
However you can use the CONVERT function to cast your date as varchar and then apply the LIKE keyword. For example:
CONVERT(VARCHAR(25), trans_date, 126) LIKE '2016-10-01%'
The goal is to, by default, populate all the input fields of a query
with a '%' and let the user put in real values wherever they want.
If you want that user should be allowed to enter a date then you can simply provide NULL value to your date column instead of populating it with a % value(which is absolutely vague for a date column)
I need to get a SQL query to output the date from a datetime field in the format Mmm dd yyyy hh:mm AM/PM. The best aproach I've been able to come up with so far is:
SELECT Left(
Convert(
nvarchar(30),
SWITCHOFFSET(
CAST(datetime1 as datetimeoffset),
'-05:00'
),
0
),
LEN(
Convert(
nvarchar(30),
SWITCHOFFSET(
CAST(datetime1 as datetimeoffset),
'-05:00'
)
)
)-11
)
However, a) it's ugly! I feel like this should be simpler than that; and b) I think going to have to change my query when Daylight savings time comes back.
The source data is a sharepoint calendar, so I can't simply change the datatype to datetimeoffset.
Any ideas?
Thanks,
Steve
As a general principal you should not be using SQL to format data into something presentable for the front end. You should be getting a DateTime type back, and using code on the format to change it. What if a future requirement comes in to support DD/MM/YYY? You'll need a separate query. It's better to let the front end format it for that.
With that in mind, store 2 pieces of data in the database. 1) DateTime as a UTC value 2) The current user's timezone (not offset)
The reason you store timezone and not offset is because of all the rule involved with DST. For example, the days that DST starts end aren't fixed in stone. They are set for a country each year, but that schedule can change, and that's a bad reason to need to update your code (unless you're writing a timezone library.)
Then once you have these two pieces of data, you retrieve the date, and the timezone, and construct a new object in the server that allows you to convert the time in the DB to the local time.
Am I missing something, or is it simply:
Convert(nvarchar(30), DATEADD(Hour, -5, datetime1), 100)
That should add -5 hours to datetime1, then convert it into the format you were specifying.
I do agree that you should try to deal with timezones instead of offsets whenever possible.
Consider a varchar field (ShipDate) that gets date-like strings written to it. These strings come from multiple third-party systems in various formats (over which I, apparently, have no control =/).
I decided to create a view that converts this varchar field to DATE so that I can query it easily (and filter out some other records / fields that I don't care about).
So far I see two formats coming in: YYYYMMDD (which is fine, I can just a a straight CONVERT) and MM/DD/YYYY, which causes an error:
Conversion failed when converting date and/or time from character string.
This changes my conversion from a simple CONVERT(DATE, ShipDate, 1) to:
CONVERT (DATE,
(CASE
WHEN ShipDate LIKE '_/__/____' THEN SUBSTRING(ShipDate, 6, 4) + '0' + SUBSTRING(ShipDate, 1, 1) + SUBSTRING(ShipDate, 3, 2)--M/DD/YYYY
WHEN ShipDate LIKE '__/_/____' THEN SUBSTRING(ShipDate, 6, 4) + SUBSTRING(ShipDate, 1, 2) + '0' + SUBSTRING(ShipDate, 4, 1)--MM/D/YYYY
WHEN ShipDate LIKE '_/_/____' THEN SUBSTRING(ShipDate, 5, 4) + '0' + SUBSTRING(ShipDate, 1, 1) + '0' + SUBSTRING(ShipDate, 3, 1)--M/D/YYYY
ELSE ShipDate --For the YYYYMMDD dates
END), 1) --End of CONVERT
Is there a better way to do the above SQL statement? I could potentially get even more date-like string formats as time goes on, so the above example could get pretty awful (I tagged this question with regex in case that could reduce the size of the case statement).
Or, is there a way to handle this problem as the records come in, avoiding the view altogether? I'm not too familiar with Triggers / SP's, but if that's a good option I'm willing to go that route =)
Or, some other method that is commonly used to solve this problem? Just curious at this point. I'm a .NET programmer, but end up helping out with SQL work because I have some experience, so I'm pretty new to anything even kind of advanced in SQL.
Don't use the date_style parameter for CONVERT. That's really for converting in the other direction. You should be able to just use: CAST(some_string AS DATE).
You might have some problems if you start getting dates in the DD/MM/YYYY format though. Of course, if they're being all mixed together then there's no way to solve that issue anyway, since even you can't know whether 4/1/2011 is April 1st or January 4th.
If the known formats are always M then D, and the separators are always /, why not just parse for the slashes? Also, why are you using ,1) in your CONVERT? All of the above formats seemed to convert fine for me without it:
WITH x(ShipDate) AS
(
SELECT '5/12/2011'
UNION ALL SELECT '05/5/2012'
UNION ALL SELECT '05/05/2012'
)
SELECT CONVERT (DATE, ShipDate) FROM x;
You say you can work with YYYYMMDD?
But MM/DD/YYYY is giving you problems. Then perhaps you can do this:
CONVERT(varchar(8),CAST('MM/DD/YYYY' as datetime),112) = YYYYMMDD
my reaction would be to add a proper date column, then implement a trigger that does the conversion into that date column.
you could then manually fix up any that failed to convert, and those records would still have values, unlike the view solution.