CONVERT vs CAST when exporting datetime as dates from sql server - sql-server

I help out a collegue with exporting data from SQL Server 2012. I type manual queries and then copy/paste the results to an Excel sheet that I then share with him. The extract I make will be an excel file that will be manually analysed. No need for automation.
What I try to do is settling for best practice when exporting order statistics grouped by day, trying to learn SQL better. I have a field of type datetime, and I want to convert this to some date format. In my locale, the typical date format is YYYY-MM-DD. I do sometimes share my script with others, that might have another locale. I have found three statements that seem to yield the same values for me.
select top 1
createdat
, CAST(createdat as date) as A
, CONVERT(char(10), createdat,126) as B
, CONVERT(char(10), createdat,127) as C
from dbo.[Order]
resulting in
createdat |A |B |C
2012-12-27 08:23:32.397 |2012-12-27 |2012-12-27 |2012-12-27
From the TSQL MSDN reference (link) I understand that:
A is handled by SQL as type Date, whereas B and C are chars.
B and C should differ by their time zone handling.
But I dont understand:
HOW does B and C handle time zones?
What is the practical difference when copy/pasting to Excel?
Is there practical difference if I share this script with collegues using another locale I should consider?
Should one or the other be preferred?

To answer your questions sequentially:
126 uses the ISO 8601 date standard, which signifies the Year aspect to be the full 4 characters long, rather than just the last two. 127 uses the same date standard, but in Time Zone Zulu, which is a military time zone (4 hours ahead of EST)
There essentially is no difference when copy/pasting to Excel. When opening an Excel doc, the default cell formatting is "General". When you paste any of these date types into Excel, it will register option A as a number (in this case 41270) and based on the pre-existing format from your query will convert it to Date format. Options B and C will first register as text, but since they are in the format of a Date (i.e. they have the "/" marks), Excel can register these as dates as well and change the formatting accordingly.
As long as the person you are sharing your script with uses T-SQL this shouldn't cause problems. MySQL or other variations could start to cause issues.
CAST(createdat as date) is the best option (IMO)
Sources:
SQL Conversion Types
ISO 8601 Details
Zulu Time Zone

Related

SQL Server query that returns data between two date times with format 01/07/2020 01:01:01 a. m

I've been having problems with a query that returns data between two date times, the query that I'm trying to fix is this one
pay.date BETWEEN '01/06/2020 00:28:46 a. m.' AND '01/06/2020 10:38:45 a. m.'
That query does not detect the a. m. part and if I have a payment at 10 am and 10 pm it will detect both payments as the t. t. part is not detected, I've been searching for a while now with no luck, thanks in advance :)
Do the filtering by an actual datetime type:
cast(replace(replace(pay.date, ' a. m.', 'am'), ' p. m.', 'pm') as datetime)
It might be better to use convert() so you can specify the proper format. If you can't supply the date literals in a readily convertible format then do a similar replace and cast on those too.
Use a literal format that is unambiguous and not dependent on runtime or connection settings. More info in Tibor's discussion.
In this case:
where pay.date between '20200601 00:28:46' and '20200601 10:38:45'
Notice that I assume June, not January - adjust as needed. Between is inclusive and be certain that you understand the limitations of the datatype for pay.date. If datetime, the values are accurate to 3ms. Verify that your data is consistent with your assumption about accuracy to seconds.

SQL Server Convert datetime to mm/dd/yyyy format but still be a datetime datatype

I would like to keep my dates as datetime datatype by also be in MM/DD/YYYY format. I know how to do this by converting them to a varchar, but want to keep the datetime format. Can anyone help with this?
Currently I have tried
SELECT CONVERT(datetime, GETDATE(), 101)
which is not working...
There is a basic misunderstanding in your question. Repeat after me: Datetimes don't have a format.
It helps if you think of them as just an array of seven integers (year, month, day, hours, minutes, seconds, milliseconds) with certain constraints. That's not in any way accurate, but it helps to get the notion out of your head that something akin to 12/31/2015 is stored in your database.
Datetimes only get a format when (implicitly or explicitly) being converted to strings. You already know how to set the format when explicitly converting to string, now all that is left to do is to find the implicit conversion that is obviously bothering you and replace it with an explicit one.
Date and datetime Values stored in the database are NOT in any recognizable format. They are stored in binary (1s and 0s) in a proprietary format where one part represents the number of days since a defined reference date (1 jan 1900) in SQL server). and the other part represents the time portion of the value. (in sql server, its the number of 1/300ths of a second since midnight.)
ALL formatting of dates and date times, no matter what format you wish for, is done only after the values have been extracted from the database, before you see them on screen, in whatever application you are using.
You can find all the formats that the SQL Server convert function can use on this MSDN Convert Link

Independent format for a string representation of date/time value, for MS SQL server?

I have a question about MS SQL Server string-to-datetime implicit conversion.
Specifically, can I be sure that a string in the 'yyyy-mm-dd hh:mm:ss' (e.g '2011-04-28 13:01:45') format, inserted into the datetime column, will always be automatically converted to a datetime type, no matter what regional or language settings are used on the server?
If no, does there exist such an independent string format for date time?
Does it depend on MSSQL server version and how?
thank you in advance
No.
If you're using a datetime column (as opposed to the newer types introduced in 2008), the safe format includes the letter T between the date and time components, e.g. '2011-04-28T13:01:45'.
For an ambiguous date (where the day <= 12), SQL Server can confuse day and month:
set language british
select MONTH(CONVERT(datetime,'2011-04-05 13:01:45'))
----
5
set language british
select MONTH(CONVERT(datetime,'2011-04-05T13:01:45'))
----
4
More generally, however, you should find a way to avoid treating datetime values as strings in the first place, e.g. if you're passing this value from some other (non-SQL) code, then use whatever facilities are available in your data access library (e.g. ADO.Net) to pass the value across as a datetime value - let the library deal with any necessary translations.

Saving Dates in SQLServer

I have a legacy application where the input is a date string, i.e.:
06/12/2009
The format of the input is always a string, and is consistent, it's always dd/mm/yyyy
At the moment the legacy app just INSERTS this in a DateTime fields. Obviously if the Localization Culture settings of the Server change, we have a bug.
Two questions:
One:
Whats the safest way to store Dates in SQLServer in this situation?
Is there a format that will always be correctly interpreted regardless of the order of day and month?
Two:
What settings exactly determines the culture of a SQLServer DB, is it an OS setting, or a setting of that DB, or what?
cheers
Format YYYY-MM-DD is unambiguous, meaning that SQL Server won't confuse the month
and day when converting a string value to DATETIME. (I've never experienced a problem with an implicit conversion using that format using the four digit year.)
The "safest" (and most convenient) way to store date values in SQL Server is to use DATETIME datatype.
Use the CONVERT function to explicitly specify the input and output formats when converting between DATETIME and strings.
SQL Server 2005 Documentation on CONVERT style argument values:
http://msdn.microsoft.com/en-us/library/ms187928(SQL.90).aspx
To convert a string representation to DATETIME datatype:
select CONVERT(datetime, '2009-06-03', 20)
The first argument is datatype to convert to, the second argument is the expression to be converted, the third argument is the style.
(style 20 is ODBC Canonical format = 'YYYY-MM-DD HH:MI:SS' (24 hour clock)
[FOLLOWUP]
To convert a DATETIME expression (e.g. getdate() to VARCHAR in 'YYYY-MM-DD' format:
select CONVERT(varchar(10), getdate(), 20)
Note that specifying varchar(10) gets you just the first 10 characters of the etnire 'YYYY-MM-DD HH:MM:SS' format.
[/FOLLOWUP]
As to what determines the default formats, that's going to be research. We avoid the issues caused by default formats by specifying the formats.
I would recommend storing all dates in UTC time when they are placed into the database. It will be consistent that way.
Storing dates like this seems to work well...
YYYY-MM-DD
See SET DATEFORMAT. The SQL 'culture' is set by SET LANGUAGE at a session level. SQL Server has its own date format settings, independent of the hosting OS. This is for several reasons: ANSI compliance, to prevent OS changes from affecting applications using the database hosted on that host and not least is compatibility, the SQL long predates the OS is currently running on.
Keep in mind that DATA is not its PRESENTATION. In this case that DATA is a DATE or DATETIME, regardless of how you show them.
As for inserting/updating/comparing datetime values, I quote the BOL:
When specifying dates in comparisons
or for input to INSERT or UPDATE
statements, use constants that are
interpreted the same for all language
settings: ADO, OLE DB, and ODBC
applications should use the ODBC
timestamp, date, and time escape
clauses of:
{ ts 'yyyy-mm-dd
hh:mm:ss[.fff] '} such as: { ts
'1998-09-24 10:02:20' }
{ d 'yyyy-mm-dd'} such as: { d '1998-09-24' }
{ t 'hh:mm:ss'} such as: { t '10:02:20'}
I can assure you that, if you use this formats they will always work, regardless of the locale of you server
I'm a bit conservative in these matters, but I prefer to use separate Year / Month / Day fields in the table, rather than a Date field that uses a DBMS-specific data type. It certainly takes more space, but the lack of ambiguity and increased portability is worth it to me.
The price you pay is that you don't get free date/time arithmetic and sorting, but it's easy enough to do yourself or by a slightly more complex "ORDER BY" clause.
I agree with the advice from spencer7593, but please be aware that using cast or convert without a format can give unexpected results. This T-SQL query returns 12, not 1.
set language British
select month(CAST('2016-01-12' AS datetime))
Normally I prefer to insert as
insert into tbl values('yyyyMMdd')
Then, itll be inserted in proper format based on db.

Validating Date Parameter in SQL Server Stored Procedure

I've written a stored procedure that takes in a date parameter. My concern is that there will be confusion between American and British date formats. What is the best way to ensure that there is no ambiguity between dates such as 02/12/2008. One possibility would be for users to enter a date in a format such as 20081202 (yyyymmdd). Is there any way to validate that without using sub strings? Alternatively dates could be entered as 02-Dec-2008(dd-mmm-yyyy), but again verification is not trivial and there are potential issues with users who do not use English.
Further to the first three answers . . . One issue is that I'm expecting this stored proc to be called directly without a front end so validation ouside of the proc is not an option. Is it a good idea to take the day, month and year as separate parameters?
You won't have any problems whatsoever if you'd use parameters in your sproc:
create proc dbo.Sproc
#date datetime
as
...
If you declare the parameter as being of type DATETIME or one of the other typed date/time types in SQL Server, which you should, then there is no ambiguity; it represents a particular date and time. The type of validation you're talking about should happen outside the stored procedure, not inside.
OK from your comments and edit, it appears the issue is with the way people call the SP rather than actually within it. To that end, you simply need to train your users to use sortable date format, i.e.
yyyy-MM-dd HH:mm:ss
And then there is no ambiguity. Anybody who is allowed near a database should be aware of localisation issues and should always be using a non-ambiguous format like this one when entering dates.
I've ended up taking a string paramater for the date and require users to enter the month as a word. I check the input is a valid date by converting it to date. To ensure the month is entered as a word, I use the like comparator to compare the input string with "%Jan%" or "%Feb%" or "%Mar%" etc.
If your proc accepts the date as a datetime parameter then there is little you can do to validate that the desired format is ddmmyyyy and not mmddyyyy. It all depends on how the user entered the date and how it was passed to SQL.
For example: On a web page i could add a parameter like this
command.Parameters.AddWithValue("#mydate",mydateVar.ToString("dd/MM/yyyy"));
OR
command.Parameters.AddWithValue("#mydate",mydateVar.ToString("MM/dd/yyyy"));
And SQL will just insert what its given as long as the string can be cast to a date correctly. It wont know the format you want to use so it will try to cast to its system default format.
A solution i use although it may not be applicable in your situation is to have users enter all dates in whatever frontend you have in the dd-MMM-yyyy format. I can then be sure of the format before i insert into the DB. I use that format everywhere to keep it all the same throughout the app.
You said that you are expecting this stored proc to be called directly without a front end and validation ouside of the proc is not an option.
In that case the users will be inserting data directly, I also believe that in this case it is for internal use only (as the stored proc is going to be called directly)
So I think you have 2 options
if you have disciplined users you can agree on one of the safe formats: ISO yyyyddmm, or ISO8601 yyyy-mm-dd Thh:mm:ss:mmm if you need a time part as well
otherwise take 3 parameters: year, month, year and perform some validation inside the stored procedure
I say take a datetime and train them to use the ODBC canonical form of a date as in this example:
EXECUTE uspMyProc {d '2009-02-11'}
If you take a date that you have to parse, whether it be a string or the year, month and day as separate integer arguments, then you have to deal with days out of range for the month and year. Some functions that take those automatically advance or move backwards the day on you. Thus the trick of sending 0 for the day and getting the last day of the previous month. Others return an error. But handling that stuff yourself is probably not worth reinventing the wheel.
If you absolutely have to because novices will be running it directly (why would novices be running stored procedures directly?), I'd take three separate arguments and pass the concatenated date as a string in the format YYYY-MM-DD through ISDATE to verify the parameters and exit if it isn't valid.

Resources