注意:这不是一个简单的问题;视图中的转换使操作变得很复杂。
在查询几层视图的select语句上出现以下错误。
将varchar数据类型转换为datetime数据类型 值超出范围。
通常,这将是一个非常简单的问题,可以通过查找错误数据并将其修复,或者在选择中添加一些其他逻辑来解决。那没有用。
通过侦查,我确定这是导致错误的代码:
,case when ( (hm.Status >= 100 and hm.Status < 200)
or (hm.Status >= 300 and hm.Status < 400 and hm.StatusEff > dateadd(day, -30, GETDATE()))
)
and coalesce(mb.isMortBilled, 0) = 0
and hm.recurring = 1
then '1' else '0'
end as isRecurable
所以我怀疑StatusEff里面有垃圾,所以我添加了一个检查,确认它是一个日期,当日期不包含日期时,该日期可能会阻止以后的转换运行:
,case when ( (hm.Status >= 100 and hm.Status < 200)
or (hm.Status >= 300 and hm.Status < 400 and ISDATE(hm.Statuseff) = 1 and convert(datetime, hm.StatusEff) > dateadd(day, -30, GETDATE()))
)
and coalesce(mb.isMortBilled, 0) = 0
and hm.recurring = 1
then '1' else '0'
end as isRecurable
我仍然收到错误消息。
如果我自己运行视图,它将运行正常。
我怀疑,当优化器必须处理几层代码时,它会拧紧并尝试在hm.StatusEff上进行转换,而不执行评估顺序和检查ISDATE()
的保护。
这是我们正在更新的旧版sql服务器;但我现在无法更新。
我认为也许可以使用CASE语句更明确地强制评估顺序。
另一个想法是:SQL是否具有评估顺序的概念?
[编辑]进行澄清:hm.StatusEff varchar始终包含一个日期,当状态在指定范围内时,该日期可以转换为日期时间。根据设计,它会包含状态以外的日期以外的日期。我认为问题在于,正在该varchar上尝试进行转换,而没有首先检查hm.Status的保护。这似乎违反了运营商的优先顺序。
[EDIT]使用@Used_by_Already的建议,我做到了,并且有效:
,case when hm.Status >= 100
and hm.Status < 200
and coalesce(mb.isMortBilled, 0) = 0
and hm.recurring = 1
then '1'
when ISDATE(hm.Statuseff) = 0 -- force this to happen before convert(datetime) below.
then '0'
when hm.Status >= 300 and hm.Status < 400 and convert(datetime, hm.StatusEff) > dateadd(day, -30, GETDATE())
and coalesce(mb.isMortBilled, 0) = 0
and hm.recurring = 1
then '1'
else '0'
end as isRecurable
答案 0 :(得分:2)
您可以使用前面的when
例如,指定该列不是日期时会发生什么情况。
,case when ISDATE(hm.StatusEff) = 0 then '0'
when ( (hm.Status >= 100 and hm.Status < 200)
or (hm.Status >= 300 and hm.Status < 400 and convert(datetime, hm.StatusEff) > dateadd(day, -30, GETDATE()))
)
and coalesce(mb.isMortBilled, 0) = 0
and hm.recurring = 1
then '1' else '0'
end as isRecurable
这应该减少转换失败的可能性,但是,如果该列是试图存储日期字符串的varchar,则始终存在潜在的问题。将该列作为真正的时态数据类型会更好。
还要注意,这是一个“隐式转换”,因为一个字符串被强制放入datetime中以便与dateadd()函数进行比较。我认为应避免隐式转换。
答案 1 :(得分:0)
在WHERE
中,引擎可以并且将通过{想要的谓词“(如果在优化方面看起来更有希望),将AND
谓词通勤。但是我也不知道CASE
是否也适用。
但是执行顺序并不一定是造成这种情况的原因。
请注意,日期时间数据的范围是1753-01-01至9999-12-31,而日期数据的范围是0001-01-01至9999-12-31。
例如,如果其中有一个“ 0001-01-01”,则isdate()
接受,它是一个有效日期。但是转换为datetime
将会失败。因此,您还应该检查hm.statuseff
是在1753-01-01之后还是在1753-01-01。
isdate(hm.statuseff) = 1
AND convert(date, hm.statuseff) >= convert(date, '1753-01-01')
AND convert(datetime, hm.statuseff) > dateadd(day, -30, getdate())
当然最好是为该列更正数据和正确的数据类型。