sql-server - Correct way to convert Month Name and Year to complete date SQL Server
问题描述
I have data in the format below.
January 2016
August 2017
November 2018
January 2018
August 2018
November 2018
I now want to convert this to a valid datetime value so i use the column as a proper date in reports.
My plan is to just add the first (or even better) the last day of the month. I have tried the below code, it seems to work but i'm wondering if there could be a better way.
DATEADD(dd, -Day(b.MonthYear) + 1, b.MonthYear)
With the above code, August 20117
gives me 2017-08-01 00:00:00.000
but maybe there could be a better way.
解决方案
The source of danger here his: You are relying on your system's culture.
Culture-dependant date formats are dangerous (What is 10/09/2017
? Is it 9th of Oct or 10th of Sep?). But even worse are language dependant formats (October is Oktober in my country).
Try this
SET LANGUAGE ENGLISH;
SELECT TRY_CAST('January 2017' AS DATE),TRY_CONVERT(DATE,'January 2017')
SET LANGUAGE GERMAN;
SELECT TRY_CAST('January 2017' AS DATE),TRY_CONVERT(DATE,'January 2017')
The alternative calls without the TRY_
would not return NULL
but throw an error.
But you are stating [sql-server-2014]
. That means you can use TRY_PARSE()
(v2012+). The big advantage: You can specify the underlying culture.
SET LANGUAGE GERMAN
SELECT TRY_PARSE('January 2017' AS DATE USING 'en-us')
If there is the smallest chance, that you code might ever run in international environments you should never ever rely on the default culture...
UPDATE performance vs. stability...
In a comment I answered @SQL_M:
I know what I'd choose... If dates are properly handled, you'd never have to deal with such a question. We should see textual formats in output only. The user's input should be handled by the application layer. So the database should never have to think about such conversions. The fact, that this need exists, shows clearly, that the database/application was not designed properly. We should not add more weakness if we can avoid it
This made me curious and I did some tests.
I must admit: The performance difference is undeniable and much bigger than expected. On a million rows with dates like Jan 1 1900 12:11AM
(default with CONVERT
, param 100) I get:
- TRY_CONVERT about 600ms
- TRY_CAST about 550ms
- TRY_PARSE about 45.000ms
So yes, it is really worth to be aware of the performance impact (~x100). But the statement above still remains true.
The test code - if interested:
USE master;
GO
CREATE DATABASE testDB
GO
USE testDB
GO
CREATE TABLE testTbl(ID INT IDENTITY,DateString VARCHAR(100));
GO
WITH Tally(Nmbr) AS (SELECT TOP 1000000 ROW_NUMBER() OVER(ORDER BY(SELECT NULL)) FROM master..spt_values v1 CROSS JOIN master..spt_values v2 CROSS JOIN master..spt_values)
INSERT INTO testTbl(DateString)
SELECT CONVERT(DATETIME,DATEADD(MINUTE,t.Nmbr,'19000101'),100)
FROM Tally t;
GO
CHECKPOINT;
GO
DBCC DROPCLEANBUFFERS
GO
DECLARE @d DATETIME2=SYSUTCDATETIME();
SELECT TRY_CONVERT(DATETIME,DateString) AS d INTO t1 FROM testTbl;
SELECT 'TRY_CONVERT', DATEDIFF(MILLISECOND,@d,SYSUTCDATETIME());
GO
CHECKPOINT;
GO
DBCC DROPCLEANBUFFERS
GO
DECLARE @d DATETIME2=SYSUTCDATETIME();
SELECT TRY_CONVERT(DATETIME,DateString,100) AS d INTO t2 FROM testTbl;
SELECT 'TRY_CONVERT with 3rd param', DATEDIFF(MILLISECOND,@d,SYSUTCDATETIME());
GO
CHECKPOINT;
GO
DBCC DROPCLEANBUFFERS
GO
DECLARE @d DATETIME2=SYSUTCDATETIME();
SELECT TRY_PARSE(DateString AS DATETIME) AS d INTO t3 FROM testTbl;
SELECT 'TRY PARSE',DATEDIFF(MILLISECOND,@d,SYSUTCDATETIME());
GO
CHECKPOINT;
GO
DBCC DROPCLEANBUFFERS
GO
DECLARE @d DATETIME2=SYSUTCDATETIME();
SELECT TRY_PARSE(DateString AS DATETIME USING 'de-de') AS d INTO t4 FROM testTbl;
SELECT 'TRY PARSE with culture',DATEDIFF(MILLISECOND,@d,SYSUTCDATETIME());
GO
CHECKPOINT;
GO
DBCC DROPCLEANBUFFERS
GO
DECLARE @d DATETIME2=SYSUTCDATETIME();
SELECT TRY_CAST(DateString AS DATETIME) AS d INTO t5 FROM testTbl;
SELECT 'TRY_CAST',DATEDIFF(MILLISECOND,@d,SYSUTCDATETIME());
GO
USE master;
GO
DROP DATABASE testDB;
推荐阅读
- angularjs - TypeError:无法在 angular.min.js:62 处读取 f (angular.min.js:62) 处未定义的属性 'childNodes'"
- angular - 在拦截器上获取令牌时出错 - 键入“Promise”
>>' 不可分配给类型 'Observable >' - com - 在 VSTO 中获取图片和内容占位符中的 SelectedShape 类型失败
- javascript - 检查每个 id 是否在数组中都有一个现有对象
- c# - Entity Framework 6 连接字符串问题
- php - 为什么我的大字节字符串会覆盖文件顶部的文本?
- mysql - 如何在 MySQL 中获取具有特定行/值的每个人的 ID
- lua - 这个lua代码可以写得更好吗?
- php - 如何找到加载某些元素的文件?
- akka - akka 聚类中的错误