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

jed*_*ley 9 sql-server type-conversion datetime

我正在尝试运行一个简单的查询来获取在 11 月创建的所有行:

SELECT COUNT(*)
FROM dbo.profile 
WHERE [Created] BETWEEN '2014-11-01 00:00:00.000' 
AND '2014-11-30 23:59:59.997';
Run Code Online (Sandbox Code Playgroud)

SMSS 返回:

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

我不明白为什么当“创建”设置为日期时间时数据从 varchar 转换为日期时间:

列 我需要告诉服务器“创建”是日期时间吗?如果没有,为什么我会收到此 varchar 消息?

编辑:数据库中的值为YYYY-MM-DD. 下面来自@SqlZim 的回复说我需要使用 convert() 来告诉 sql 数据库中的日期格式 - 并用字母 T 替换空格字符:

select count(*) 
from dbo.profile 
where [created] between convert(datetime,'2014-11-01T00:00:00.000') 
and convert(datetime,'2014-11-30T23:59:59.997');`
Run Code Online (Sandbox Code Playgroud)

Sql*_*Zim 8

我查看了您的个人资料,发现您在英国。如果您的 sql server 设置为使用 dateformat dmy 那么这就解释了您的问题。如果不使用 'T' 而不是日期时间字符串中的空格,Sql Server 将不会将其识别为 ISO8601 格式。

尝试这个:

select count(*) 
  from dbo.profile 
  where [created] between convert(datetime,'2014-11-01T00:00:00.000') 
                      and convert(datetime,'2014-11-30T23:59:59.997');
Run Code Online (Sandbox Code Playgroud)

使用日期和/或日期时间查询可能会很棘手,为了确保您得到所需的内容,我建议您阅读:

编辑:要澄清错误消息中的超出范围值,可以将月份解释为 30,将日期解释为 11。


Pau*_*ite 8

我不明白为什么当“创建”设置为日期时间时数据从 varchar 转换为日期时间

您提供的用于与Created列进行比较的文字是字符串。为了将这些文字与datetime列进行比较,SQL Server 尝试datetime根据数据类型优先级的规则将字符串转换为类型。在没有关于字符串格式的明确信息的情况下,SQL Server 遵循其复杂的规则将字符串解释为日期时间。

在我看来,避免这些类型问题的最好方法是明确类型。SQL Server 提供了用于此目的的CAST and CONVERT函数。当使用字符串和日期/时间类型时,CONVERT是首选,因为它提供了一个样式参数来显式定义字符串格式。

该问题使用 ODBC 规范(以毫秒为单位)格式(样式 121)中的字符串。明确数据类型和字符串样式会导致以下结果:

SELECT COUNT(*)
FROM dbo.profile 
WHERE [Created] BETWEEN 
    CONVERT(datetime, '2014-11-01 00:00:00.000', 121)
    AND 
    CONVERT(datetime, '2014-11-30 23:59:59.997', 121);
Run Code Online (Sandbox Code Playgroud)

也就是说,有充分的理由(正如 Aaron 在他的回答中指出的那样)使用半开范围而不是BETWEEN(我使用下面的样式 120 只是为了变化):

SELECT COUNT(*)
FROM dbo.profile 
WHERE
    [Created] >= CONVERT(datetime, '2014-11-01 00:00:00', 120)
    AND [Created] < CONVERT(datetime, '2014-12-01 00:00:00', 120);
Run Code Online (Sandbox Code Playgroud)

明确类型是一个非常好的习惯,尤其是在处理日期和时间时。


Aar*_*and 7

由于BETWEEN四舍五入不同的日期/时间类型和其他问题而非常成问题,并且由于YYYY-MM-DD不是没有尴尬的安全格式,因此T使用没有分隔符的ISO 标准完整日期的开放式范围是一个更好的方法:

WHERE Created >= '20141101' AND Created < '20141201';
Run Code Online (Sandbox Code Playgroud)