将 varchar 数据类型转换为 datetime 数据类型导致值超出范围

AA.*_*.SC 3 sql-server

我的数据库中有两个用户。当我在用户下执行以下查询时,ONE它成功执行;但是,当我在用户下执行它时,TWO它会生成一条错误消息:

询问:

select cast ('2015-04-28 16:00:00.000' as datetime)
Run Code Online (Sandbox Code Playgroud)

错误消息:

消息 242,级别 16,状态 3,第 11 行
varchar 数据类型到 datetime 数据类型的转换导致值超出范围。

用户ONE将英语作为默认语言,其简单的服务器登录
用户TWO将英国英语作为默认语言,也用作应用程序用户

数据库属性

在此处输入图片说明

因为在我的真实查询中,列是 inWhere子句来获取指定日期范围的结果集:

    CAST(CONVERT(VARCHAR(10), Createdate, 101) AS DATETIME) >= CAST(CONVERT(VARCHAR(10), StartDate, 101) AS DATETIME) 
    AND 
    CAST(CONVERT(VARCHAR(10), Createdate, 101) AS DATETIME) <= CAST(CONVERT(VARCHAR(10), EndDate, 101) AS DATETIME)
Run Code Online (Sandbox Code Playgroud)

我验证了 columns CreatedateStartDate并且在数据库中EndDate有一个DateTime数据类型。

到目前为止我尝试过的:

  1. 删除了铸造cast(columnname as datetime)及其工作正常。

    CONVERT(VARCHAR(10), Createdate, 101)  >= CONVERT(VARCHAR(10), StartDate, 101)  
    AND 
    CONVERT(VARCHAR(10), Createdate, 101) <= CONVERT(VARCHAR(10), EndDate, 101) 
    
    Run Code Online (Sandbox Code Playgroud)

    这不是有 3000 多个 SP 并且我们不能一次单击全部更改的解决方案吗?还有其他解决方案吗?

  2. 我按照以下步骤Security->logins->User'two'->Default Language将用户“Two”的语言更改为英语,以验证这是唯一的原因,但在 Casting 上查询仍然失败

知道我们缺少哪些设置吗?我是否必须验证两个用户的日期格式?

如果删除强制转换或使用T是唯一的解决方案,那么它需要时间进行修改,然后进行 QA,我们是否有任何临时解决方案,因此对我们的截止日期没有任何影响,并且我们有足够的时间根据最佳实践更改 SP?

ype*_*eᵀᴹ 15

导致错误的条件:

CAST(CONVERT(VARCHAR(10), createdate, 101) AS DATETIME) >= 
CAST(CONVERT(VARCHAR(10), STARTDATE, 101) AS DATETIME) 
AND 
CAST(CONVERT(VARCHAR(10), createdate, 101) AS DATETIME) <= 
CAST(CONVERT(VARCHAR(10), ENDDATE, 101) AS DATETIME)
Run Code Online (Sandbox Code Playgroud)

正在尝试进行此比较:

CAST(createdate AS DATE) >= CAST(STARTDATE AS DATE)
AND 
CAST(createdate AS DATE) <= CAST(ENDDATE AS DATE)
Run Code Online (Sandbox Code Playgroud)

但使用101样式代码 (US, mm/dd/yyyy)首先转换datetimevarchar(10),有效地切断时间部分,然后转换回datetime- 但没有明确指定*第二次转换的样式代码

坦率地说,我很惊讶它真的有效。根据服务器或客户端设置,使用此类转换的各种查询和过程很可能会产生错误的结果。(即,将'2015-12-08 16:00:00'(2015-Dec-08) 转换'12/08/2015''2015-08-12 00:00:00'(2015-Aug-12) ,然后转换为(2015-Aug-12))。

您可以做的是将这些转换更改为不依赖于客户端设置。最简单的方法是删除第二个演员。但这也需要您更改第一个演员表以使用合理的格式(以及响应样式代码)。“Sane”表示保留日期顺序的格式,即样式代码 102、112、120、126。(不要使用不保留顺序的样式 101 或 103。这会使问题变得更糟。)代码示例使用:

-- style code 120 (ODBC canonical):   yyyy-mm-dd hh:mi:ss(24h)
CONVERT(VARCHAR(10), createdate, 120) >= 
CONVERT(VARCHAR(10), STARTDATE,  120) 
AND 
CONVERT(VARCHAR(10), createdate, 120) <= 
CONVERT(VARCHAR(10), ENDDATE,    120)


-- style code 126 (ISO8601):   yyyy-mm-ddThh:mi:ss.mmm (no spaces)
CONVERT(VARCHAR(10), createdate, 126) >= 
CONVERT(VARCHAR(10), STARTDATE,  126) 
AND 
CONVERT(VARCHAR(10), createdate, 126) <= 
CONVERT(VARCHAR(10), ENDDATE,    126)


-- style code 112 (ISO):   yyyymmdd
CONVERT(VARCHAR(8), createdate, 112) >= 
CONVERT(VARCHAR(8), STARTDATE,  112) 
AND 
CONVERT(VARCHAR(8), createdate, 112) <= 
CONVERT(VARCHAR(8), ENDDATE,    112)
Run Code Online (Sandbox Code Playgroud)

但我认为最好的方法是根本不要转换为varchar并使用另一种方式删除时间部分。如果您使用的是 2005 并且没有DATE可用的类型,您可以使用此方法从 a 中删除时间DATETIME

DATEADD(day, DATEDIFF(day, 0, createdate), 0) 
  >= DATEADD(day, DATEDIFF(day, 0, STARTDATE), 0) 
AND 
DATEADD(day, DATEDIFF(day, 0, createdate), 0) 
  <= DATEADD(day, DATEDIFF(day, 0, ENDDATE), 0)
Run Code Online (Sandbox Code Playgroud)

或简化为(可能使用索引):

createdate >= DATEADD(day, DATEDIFF(day, 0, STARTDATE), 0) 
AND 
DATEADD(day, DATEDIFF(day, 0, createdate), 0) <= ENDDATE
Run Code Online (Sandbox Code Playgroud)

最后最好的(并假设所有这些列都是DATETIME)并且您的 SQL Server 版本是 2008 或更高版本(并且DATE类型也是如此)是将这些转换更改为强制转换为,DATE因此它们的意图不言自明,为:

CAST(createdate AS DATE) >= CAST(STARTDATE AS DATE)
AND 
CAST(createdate AS DATE) <= CAST(ENDDATE AS DATE)
Run Code Online (Sandbox Code Playgroud)

甚至更好,简化为(可能使用索引):

createdate >= CAST(STARTDATE AS DATE)
AND 
CAST(createdate AS DATE) <= ENDDATE 
Run Code Online (Sandbox Code Playgroud)

当 a charorvarchar值隐式转换为日期/时间类型时会发生什么,Aaron Bertrand在博客文章Bad习惯踢:错误处理日期/范围查询中很好地解释了:

..,SQL Server 中日期/时间文字的唯一真正安全的格式,至少对于DATETIMESMALLDATETIME,是:

YYYYMMDD
YYYY-MM-DDThh:mm:ss[.nnn]
Run Code Online (Sandbox Code Playgroud)

关于这个问题,为什么一个用户收到这些错误而另一个用户没有,这个(谢谢Paul White)技术网页面:使用日期和时间数据,解释说:

使用日期和时间格式

字符串文字格式会影响应用程序中向用户显示的数据,但不会影响 SQL Server 中的底层整数存储格式。但是,SQL Server 可能会将应用程序或用户输入用于存储或输入到日期函数的字符串文字格式的日期值解释为不同的日期。解释依赖于字符串文字格式,数据类型和运行时的组合SET DATEFORMATSET LANGUAGE并且默认语言选项设置