Sql Server Cast&转换DateTime和DateTime2无情地失败

时间:2015-01-20 17:01:59

标签: c# sql-server date datetime

首先,以下是有关我的环境的一些信息:

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

  • textsize 2147483647
  • language us_english
  • dateformat mdy
  • datefirst 7
  • lock_timeout -1
  • quoted_identifier SET
  • arithabort SET
  • ansi_null_dflt_on SET
  • ansi_warnings SET
  • ansi_padding SET
  • ansi_nulls SET
  • concat_null_yields_null SET
  • 隔离级别读取已提交

这是我的问题。

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

(DATETIME2) Msg 241,Level 16,State 1,Line 122 从字符串转换日期和/或时间时转换失败。

和...

(DATETIME) Msg 242,Level 16,State 3,Line 122 将varchar数据类型转换为日期时间数据类型会导致超出范围的值。

我的问题SOUNDS看似简单,就像我必须是一个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 !!!所以我没有解决这个问题的运气。

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

请帮忙。

更新

好的。我按照每个人的建议,将View中的日期转换重写为SubQuery / SubView,然后加入它。以下是新转换代码的内容......

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'

这是令人尴尬的代码,但它起作用,解决了问题并且不幸的是必要,因为有人不想将日期值存储为古怪的数据库结构中的日期列...所以它可能会帮助遇到此问题的人问题

1 个答案:

答案 0 :(得分:1)

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

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

OR:

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'