Sql 服务器转换和转换 DateTime 和 DateTime2 无情地失败

Sql Server Cast & Convert DateTime and DateTime2 Fail Relentlessly

首先,这是关于我的环境的一些信息:

我是 运行 SQL 2005 数据库 SQL 2014 实例。 COLLATION 是 SQL_Latin1_General_CP1_CI_AS。这是我的 DBCC USEROPTIONS

的结果

这是我的问题。

我将 运行 保留在以下 2 个错误中,具体取决于我何时尝试从视图中 select 以及针对类型为 DATETIME 或 DATETIME2 的已转换列使用 WHERE 子句。

(DATETIME2) 消息 241,级别 16,状态 1,第 122 行 从字符串转换日期 and/or 时间时转换失败。

还有...

(日期时间) 消息 242,级别 16,状态 3,第 122 行 将 varchar 数据类型转换为 datetime 数据类型导致值超出范围。

我的问题听起来似乎很简单,就像我一定是一个 mook 并且要么在 WHERE 中传递了格式不正确的值,要么没有考虑语言设置、排序规则和优化器优先级。

这是我的查询。

DECLARE @DATEVALUE DATETIME = '01-05-2015 14:23:05'
SELECT t.* FROM Reports.MyReportView WHERE t.MyDateColumn = @DATEVALUE

无论我怎么尝试,都惨遭失败。

我可以毫无问题地将字符串转换为 DATETIME。 喜欢...

SELECT CAST('03/28/2011 18:03:40' AS DATETIME) 

工作正常。

问题似乎只发生在我的 WHERE 子句中。我看了很多建议,尝试了很多事情,例如将语言设置更改为英国或法语,将 DATEFORMAT 更改为 ymd、dmy、mdy,尝试了我发现的其他一些解决方案,例如...

DECLARE @F7 nvarchar(10) = '01/05/2015'
DECLARE @F8 nvarchar(10) = '00:00:00'
DECLARE @NEWTIME DATETIME

DECLARE @Date VARCHAR(20)
SET @Date = RIGHT(@F7,4)+'/'+SUBSTRING(@F7,4,2)+'/'+LEFT(@F7,2)

DECLARE @time DATETIME
SET @time =  CONVERT(DATETIME, @Date + ' ' + @F8) 

...是的,输出 DATETIME 就好了。但是当我尝试在我的 WHERE 子句中使用该值时...... KAAAABOOOOOOOM!所以我没有运气解决这个问题。

我唯一能想到的是优化器在欺骗我,因为它在幕后重新安排查询,而我在转换查询顺序,因此实际上导致失败的是视图,与 SQL 服务器的日期时间转换无关。

请帮忙。

更新

确定已修复。我听从了大家的建议,将View中的日期转换重写为SubQuery/SubView,然后Join了进去。这是新的转换代码的样子...

CONVERT(DATETIME,
CASE 
    WHEN   
    (
        CASE 
            WHEN REPLACE(ISNULL(t.DayCol,''),' ','') = '' 
            THEN NULL ELSE REPLACE(ISNULL(t.DayCol,''),' ','') END
    ) IS NULL
    OR 
        (
            CASE WHEN REPLACE(ISNULL(t.MonthCol,''),' ','') = '' 
            THEN NULL ELSE REPLACE(ISNULL(t.MonthCol,''),' ','') END
        ) IS NULL
    OR 
        (
            CASE WHEN REPLACE(ISNULL(t.YearCol,''),' ','') = '' 
            OR t.YearCol = 'NoSelection' 
            THEN NULL ELSE t.YearCol END
        ) IS NULL
    OR  
    ISDATE
    (
        CONCAT
            (
                RIGHT('0' +REPLACE(MonthCol,' ',''),2),'/',
                RIGHT('0' + REPLACE(t.DayCol,' ',''), 2),'/',
                REPLACE(YearCol,' ','')
            )
    ) = 0
    THEN NULL
    ELSE 
    CONCAT
    (
        RIGHT('0' +REPLACE(MonthCol,' ',''),2),'/',
        RIGHT('0' + REPLACE(t.MonthCol,' ',''), 2),'/',
        REPLACE(YearCol,' ','')
    )
END) as 'MyDateColumn'

这是令人尴尬的代码,但它有效,解决了问题,不幸的是,它是必需的,因为有人不想将日期值作为日期列存储在古怪的数据库结构中……所以也许它会对运行的人有所帮助进入这个问题。

如果您要从中转换的数据列可能包含空值或其中可能包含无效日期数据,那么您将收到此错误。 SQL 引擎将在过滤 WHERE 子句上的数据之前尝试对所有潜在数据执行 CAST。你需要做的是做一个子查询,然后 select 并反对它:

SELECT CAST(a.StringField AS datetime) AS TheDate
FROM (
    SELECT a.* FROM MyTable WHERE StringField = '12/1/2014'
) a

或:

SELECT CAST(a.StringField AS datetime) AS TheDate
FROM (
    SELECT a.* FROM MyTable WHERE ISDATE(StringField) = 1
) a
WHERE CAST(a.StringField as datetime) = '12/1/2014'