Sql server 将月份名称和年份转换为SQL Server完成日期的正确方法

Sql server 将月份名称和年份转换为SQL Server完成日期的正确方法,sql-server,tsql,sql-server-2014,Sql Server,Tsql,Sql Server 2014,我有以下格式的数据 January 2016 August 2017 November 2018 January 2018 August 2018 November 2018 现在我想将其转换为有效的datetime值,以便在报告中使用该列作为正确的日期 我的计划是在这个月的最后一天增加第一天甚至更好。 我已经尝试了下面的代码,它似乎工作,但我想知道是否有更好的方法 DATEADD(dd, -Day(b.MonthYear) + 1, b.MonthYear) 根据上面的代码,2017年8月

我有以下格式的数据

January 2016
August 2017
November 2018
January 2018
August 2018
November 2018
现在我想将其转换为有效的datetime值,以便在报告中使用该列作为正确的日期

我的计划是在这个月的最后一天增加第一天甚至更好。 我已经尝试了下面的代码,它似乎工作,但我想知道是否有更好的方法

DATEADD(dd, -Day(b.MonthYear) + 1, b.MonthYear)

根据上面的代码,2017年8月给了我2017-08-01 00:00:00.000,但也许有更好的方法。

我想它就这么简单:

DATEADD(dd, -Day(b.MonthYear) + 1, b.MonthYear)
SELECT CAST ('January 2016' AS DATETIME)
或用于SQL2012+

SELECT TRY_CAST ('January 2016' AS DATETIME)

这里的危险源在于:你依赖于你的系统文化

文化相关日期格式很危险2017年9月10日是什么?是10月9日还是9月10日?。但更糟糕的是,在我的国家,10月份是Oktober

试试这个

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')
没有TRY_uu的可选调用不会返回NULL,而是抛出一个错误

但您正在声明[sql-server-2014]。也就是说。最大的优点是:您可以指定底层文化

SET LANGUAGE GERMAN
SELECT TRY_PARSE('January 2017' AS DATE USING 'en-us')
如果有最小的机会,您的代码可能会在国际环境中运行,那么您永远不应该依赖默认的区域性

更新性能与稳定性。。。 在一篇评论中,我回答了@SQL\M:

我知道我会选择什么。。。如果日期处理得当,你永远不会 我不得不处理这样一个问题。我们应该在中看到文本格式 仅输出。用户的输入应由应用程序处理 层因此,数据库不应该考虑这样的问题 转换。这一需求的存在清楚地表明 数据库/应用程序设计不正确。我们不应该再增加了 如果我们能避免的话

这让我好奇,我做了一些测试

我必须承认:性能差异是不可否认的,比预期的要大得多。在日期为1900年1月1日12:11AM的一百万行上,默认值为CONVERT,参数100 I get:

尝试转换大约600毫秒 试着用550ms左右的时间施放 请尝试解析大约45.000ms 因此,确实值得注意x100对性能的影响。但上述说法仍然正确

测试代码-如果有兴趣:

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;

考虑到字符串转换的风险,try\u convert或try\u cast更安全+1@JohnCappelletti你是对的,但这取决于版本。谢谢你的评论,更新了我的答案。这取决于你的系统默认语言是英语。一个非常危险的假设。。。你可能会读到我的答案……看看这个加上一个,对我的帖子有用的补充。@SQL\M老实说:在我看来,这不是对你的帖子有用的补充,而是唯一可靠的答案。您的方法可能工作得很好,通过了所有内部测试,并且可能仍然会在生产环境中出现意外、愚蠢和难以发现的错误……是的。但请记住,如果您不在国际环境中工作,这种解决方案是不可取的。你的解决方案总是有效的,但代价很高。性能受到了影响。很好的测试,我不久前自己做了一些测试,得到了类似的结果。正如你所说,差异比你预期的要大得多。