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


8

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

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

SMSS返回:

从varchar数据类型到datetime数据类型的转换导致值超出范围。

我不明白为什么将“ Created”设置为datetime时将数据从varchar转换为datetime:

列 我需要告诉服务器“创建”是日期时间吗?如果没有,为什么我会收到此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');`

Answers:


8

我检查了您的个人资料,发现您在英国。如果您的SQL Server设置为使用dateformat dmy,则说明了您的问题。如果不使用'T'代替datetime字符串中的空格,则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');

使用日期和/或日期时间进行查询可能会很棘手,为确保获得所需的信息,我建议阅读:

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


8

我不明白为什么将“ Created”设置为datetime时将数据从varchar转换为datetime

您提供的用于与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);

就是说,有充分的理由(如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);

明确声明类型是一种很好的习惯,尤其是在处理日期和时间时。



3

另一种选择是,我建议使用ODBC datetime文字。尽管有它们的名称,但它们不需要您通过ODBC连接。它们绕过SQL Server中通常的转换规则,并始终解释为datetime

SELECT COUNT(*)
FROM dbo.profile 
WHERE [Created] BETWEEN 
    {TS '2014-11-01 00:00:00.000'}
    AND 
    {TS '2014-11-30 23:59:59.997'};


其他支持ODBC datetime文字是DT作为记录在这里联机丛书。两者都返回datetime(不是datetime),但是语法仍然紧凑且明确。字符串的固定格式为:

ODBC字符串格式

例:

SELECT TOP (1)
    D = {D '2014-12-27'},
    T = {T '14:49:23.789'},
    TS = {TS '2014-12-27 14:49:23.789'};

T变种返回指定的时间在当天,由内部使用,只报道{fn getdateODBC()}

执行计划


1
好吧,CONVERT(DATE, '20141201')如果您需要明确地覆盖其他所有条件,我可能会这样做。再说一次,如果基础列是日期/时间类型,那么这实际上不是必需的。您是否说WHERE Active = CONVERT(BIT, 1)避免WHERE Active = 1被解释为INT
亚伦·伯特兰

3
@AaronBertrand实际上,众所周知,我确实做到这一点:) 这是一个为什么的例子
保罗·怀特9

-1

下面的代码,获取当前会话dateformat,转换为datetime时出错,然后将dateformat设置为ymd ,最后但并非最不重要的是再次测试转换(广播),并且可以正常工作

-- set the dateformat for the current session
-- if you use this date format you get the following error message:
--Msg 242, Level 16, State 3, Line 9
--The conversion of a varchar data type to a datetime data type resulted in an out-of-range value.
set dateformat dmy


-- set the dateformat for the current session
--this one does not give an error message
set dateformat ymd

-- The conversion of a varchar data type 
-- to a datetime data type resulted in an out-of-range value.
select cast('2017-08-13 16:31:31'  as datetime)

-- get the current session date_format
select date_format
from sys.dm_exec_sessions
where session_id = @@spid

-- set the dateformat for the current session
set dateformat ymd

-- this should work
select cast('2017-08-13 16:31:31'  as datetime)



select @@version

Microsoft SQL Server 2016(SP1)(KB3182545)-13.0.4001.0(X64)2016年10月28日18:17:30版权所有(c)Microsoft Corporation企业版:Windows Server 2012 R2 Datacenter 6.3上的基于核心的许可(64位) (内部版本9600 :)(管理程序)


关心解释否决票吗?,代码在这里工作正常!
Marcello Miorelli,
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.