如何停止 SQL 服务器交换月份和日期?

How do I stop SQL Server swapping the month and day?

我有以下查询:

DECLARE @someDateTime DATETIME = '2019-12-01 00:00:00.000'; -- 1st December 2019
SELECT @someDateTime;

结果

2019-01-12 00:00:00.000

我预计结果是 2019-12-01 00:00:00.000(1st 2019 年 12 月)- 月份和日期因某种未知原因互换。

直到最近,我从未遇到过这种格式的问题。

如何以 "YYYY-MM-DD HH:mm:ss.000" 格式输入日期,并在分配给 select 中的 variable/displayed 后保持该格式?
什么设置决定了这种可能已经改变的格式?

可能有用的信息

dbcc useroptions

结果:

Set Option | Value
----------   -----
...
language   | British
dateformat | dmy
...

我尝试过的事情:

查询 1

将日期更改为交换后无效的日期

DECLARE @someDateTime DATETIME = '2019-12-20 00:00:00.000';
SELECT @someDateTime;

结果:

Msg 242, Level 16, State 3, Line 4
The conversion of a varchar data type to a datetime data type resulted in an out-of-range value.

查询 2

声明后设置变量

DECLARE @someDateTime DATETIME;
SET @someDateTime = '2019-12-01 00:00:00.000';
SELECT @someDateTime;

结果 - 不受欢迎:

2019-01-12 00:00:00.000

查询 3

将变量插入 table 变量

DECLARE @someDateTime DATETIME = '2019-12-01 00:00:00.000';
DECLARE @someTable TABLE (someDateTimeColumn DATETIME);
INSERT @someTable VALUES (@someDateTime);
SELECT * FROM @someTable

结果 - 不受欢迎:

someDateTimeColumn
------------------
2019-01-12 00:00:00.000

查询 4

将数据直接插入 table 变量

DECLARE @someTable TABLE (someDateTimeColumn DATETIME);
INSERT @someTable VALUES ('2019-12-01 00:00:00.000');
SELECT * FROM @someTable

结果:不希望的

someDateTimeColumn
------------------
2019-01-12 00:00:00.000

查询 5

正在更改输入字符串的格式

DECLARE @someDateTime DATETIME = '01/12/2019';
SELECT @someDateTime;

结果 - 期望

2019-12-01 00:00:00.000

查询 6

正在更改输入字符串的格式

DECLARE @someDateTime DATETIME = '2019-12-01T00:00:00.00';
SELECT @someDateTime;

结果 - 期望:

2019-12-01 00:00:00.000

查询 7

正在更改输入字符串的格式

DECLARE @someDateTime DATETIME = '2019-12-01';
SELECT @someDateTime;

结果 - 不受欢迎:

2019-01-12 00:00:00.000

查询 8

使用SET DATEFORMAT

SET DATEFORMAT ymd
DECLARE @someDateTime DATETIME = '2019-12-01';
SELECT @someDateTime;

结果 - 期望

2019-12-01 00:00:00.000

您可以使用文字格式 YYYYMMDD,它始终被解释为年-月-日,无论您的 SQL 服务器的区域设置如何:

DECLARE @someDateTime DATETIME = '20191201 00:00:00.000';

一种解决方案是使用完整的 ISO8601 格式,即 2019-12-01T00:00:00.000,另一种是使用未分隔的日期格式,即 20191201 00:00:00.000。更好的解决方案是切换到 datetime2.

datetime2 是在 15 年前(或者是 11 年前)引入的,目的是摆脱 datetime 的怪癖,比如毫秒不准确、任意精度、怪异的算术和......解析特质.例如,datetime2对ISO8601格式的解析不受DATEFORMAT影响:

SET DATEFORMAT ydm
DECLARE @someDateTime DATETIME2(0) = '2019-12-01 00:00:00.000'
select @someDateTime
------
2019-12-01 00:00:00

这可以保护您的代码免受不幸的服务器设置修改

阅读来自@SMor 评论的 this 文章、我的同事通过 google 搜索发表的评论,并想起几个月前我做的事情后,我想我已经弄明白了什么是发生了什么以及发生了什么变化。

默认情况下,我的数据库用户设置为:

language   | us_english
dateformat | mdy

所以当读取字符串 '2019-12-01' SQL 服务器期望 mm-dd-yyyy.

SQL 服务器虽然很聪明。它看到第一部分 2019 并意识到它实际上是年份,因此它将年份移到最后,然后再次尝试。*

它现在 12-01-2019 符合它期望的格式。

当您在 SSMS 中打开帐户属性时,语言下拉列表默认为列表中的第一种语言 (Arabic)。
几个月前我碰巧在那儿做一些不相关的事情并决定,为了确保我没有不小心将它设置为 Arabic,我也会更改它。

我选了British.

我的数据库用户现在设置为:

language   | British
dateformat | dmy

所以当读取字符串 '2019-12-01' SQL 服务器期望 dd-mm-yyyy.

一旦它改变了年份,它就变成了 12-01-2019,它被解释为 dd-mm-yyyy 从而给我留下 January 12th 2019 而不是 12 月 1 日st 2019

今天是漫长的一天。

*请注意,这是对我如何理解这个问题的一个相当简单的解释。这可能不是它在现实中的实际运作方式,但可以满足我对这个问题的好奇心。