【问题标题】:Sql Server Cast & Convert DateTime and DateTime2 Fail RelentlesslySql Server Cast & Convert DateTime 和 DateTime2 无情地失败
【发布时间】:2015-01-20 17:01:59
【问题描述】:

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

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

  • 文本大小 2147483647
  • 语言 us_english
  • 日期格式 mdy
  • 日期优先 7
  • lock_timeout -1
  • quoted_identifier 设置
  • arithabort 设置
  • ansi_null_dflt_on SET
  • ansi_warnings 设置
  • ansi_padding 设置
  • ansi_nulls 设置
  • concat_null_yields_null SET
  • 已提交隔离级别读取

这是我的问题。

我不断遇到以下 2 个错误,具体取决于我何时尝试从视图中选择以及我们针对 DATETIME 或 DATETIME2 类型的转换列使用 WHERE 子句。

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

还有……

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

我的问题听起来很简单,就像我一定是个傻瓜,要么在 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 !!!所以我没有运气解决这个问题。

我唯一能想到的是优化器在嘲笑我,因为它在幕后重新安排查询,并且在我按查询顺序转换字符串之前,我正在将 VALID 日期时间变量与无效的 VARCHAR 字符串进行比较,因此实际上是视图导致了失败,并且与 SQL Server 的日期时间转换无关。

请帮忙。

更新

确定已修复。我听从了大家的建议,将视图中的日期转换重写为子查询/子视图,然后加入它。这是新的转换代码的样子...

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'

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

【问题讨论】:

  • “这很失败”,“KABOOOM”....你能告诉我们实际发生了什么吗?
  • 我推荐的一件事是在您的代码中使用 ISO 8601 日期格式 (YYYY-MM-DD HH:MM:SS)。这将消除本地化影响事物。然后,如果您使用 ISO 日期并出现错误,则很容易追踪。至于失败,视图的代码在这里会有所帮助。
  • 如何在视图中填充日期列?
  • 另外,您可能会在 DBA.Stackexchange 上获得更多有用的帮助。这些人中的大多数都非常擅长 TSQL(我曾经是我以前工作中的一员)
  • @ChrisCampbell 服务器本地化选项确定字符串 1/12/2014 如何转换为日期时间,是 2014 年 1 月 12 日还是 2014 年 12 月 1 日(取决于国家/地区)。无论服务器本地化设置是什么,ISO 2014-01-12 总是 2014 年 1 月 12 日。那是我所引用的,而不是 CONVERT 语法。

标签: c# sql-server date datetime


【解决方案1】:

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

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'

【讨论】:

  • 我将尝试将查询重组为子查询,看看会发生什么。我仍然认为优化器可能会决定如果它确实成功了,它会再次知道最好,但在这一点上,有总比没有好。也许我应该使用 SPROC 并稍微改变架构而不是视图。
  • 是的......优化器让我很难。我重写了查询,干净地删除了显示为 ISDATE(...) = 0 的所有值,并将我的转换移到了子视图中(因为从一开始就从格式不佳的表中创建日期值是一个相当复杂的操作)。之后,引用了子视图,现在相等操作执行得很漂亮。 ARRRRRRRG 当心优化器!它比我聪明:(
猜你喜欢
  • 1970-01-01
  • 2019-10-07
  • 2010-11-22
  • 2010-12-21
  • 1970-01-01
  • 1970-01-01
  • 2016-11-30
  • 1970-01-01
相关资源
最近更新 更多