如果不进行转换,则SQL DateTime转换失败

Ada*_*son 5 t-sql sql-server-2008-r2

我正在修改客户端的现有查询,我遇到了一个令人困惑的问题.

我们的客户端使用SQL Server 2008 R2,并且相关数据库使用户能够通过使用EAV结构为其中一个表指定自定义字段.存储在此结构中的所有值都是varchar(255),并且几个字段用于存储日期.正在修改有问题的查询以使用其中两个字段并将它们(一个是开始,另一个是结束)与当前日期进行比较,以确定哪一行是"当前".

我遇到的问题是查询的一部分是CONVERT(DateTime, eav.Value)为了varchar变成一个DateTime.转换本身都已成功,我可以将值作为SELECT子句的一部分包含在内,但问题的一部分是给我一个转换错误:

Conversion failed when converting date and/or time from character string.
Run Code Online (Sandbox Code Playgroud)

真正的踢球者是这样的:如果我将此查询的基础(获取具有两个自定义字段值的实体列表展平为单行)定义为视图并选择视图并过滤视图getdate(),则它可以正常工作,但是如果我使用视图中的一个(非日期)字段向第二个表添加连接,则会失败.我意识到这可能有点难以理解,所以如果需要我可以发布一个示例查询,但这个问题已经有点长了.

我已经尝试在另一个数据库中重新创建基本结构并包含示例数据,但新数据库的行为与预期一致,所以我在这里不知所措.

编辑如果它有用,这里是视图的声明:

create view Festival as 
select
    e.EntityId as FestivalId,
    e.LookupAs as FestivalName,
    convert(Date, nvs.Value) as ActivityStart,
    convert(Date, nve.Value) as ActivityEnd

from tblEntity e

left join CustomControl ccs on ccs.ShortName = 'Activity Start Date'
left join CustomControl cce on cce.ShortName = 'Activity End Date'
left join tblEntityNameValue nvs on nvs.CustomControlId = ccs.IdCustomControl and nvs.EntityId = e.EntityId
left join tblEntityNameValue nve on nve.CustomControlId = cce.IdCustomControl and nve.EntityId = e.EntityId

where e.EntityType = 'Festival'
Run Code Online (Sandbox Code Playgroud)

失败的查询是这样的:

select * 

from Festival f

join FestivalAttendeeAll fa on fa.FestivalId = f.FestivalId

where getdate() between f.ActivityStart and f.ActivityEnd
Run Code Online (Sandbox Code Playgroud)

但这有效:

select * 

from Festival f

where getdate() between f.ActivityStart and f.ActivityEnd
Run Code Online (Sandbox Code Playgroud)

(EntityId/ FestivalId是int列)

spe*_*593 11

之前我遇到过这种类型的错误,这是由于执行计划执行的"操作顺序".

您收到该错误消息,因为您的语句的执行计划(由优化程序生成)正在对包含无法转换为DATETIME的字符串值的行执行CONVERT()操作.

基本上,您无法控制优化程序执行转换的行.您知道您只需要在某些行上完成转换,并且您具有排除这些行的谓词(WHERE或ON子句)(将行限制为需要转换的行),但您的执行计划正在执行CONVERT()操作在行之前排除这些行.

(例如,优化程序可能会选择执行表扫描,并在应用任何谓词之前在每一行上执行该转换.)

没有特定问题和产生错误的特定SQL,我无法给出具体答案.


解决该问题的一种简单方法是使用ISDATE()函数来测试字符串值是否可以转换为日期.

也就是说,替换:

CONVERT(DATETIME,eav.Value)
Run Code Online (Sandbox Code Playgroud)

有:

CASE WHEN ISDATE(eav.Value) > 0 THEN CONVERT(DATETIME, eav.Value) ELSE NULL END
Run Code Online (Sandbox Code Playgroud)

要么:

CONVERT(DATETIME, CASE WHEN ISDATE(eav.Value) > 0 THEN eav.Value ELSE NULL END)
Run Code Online (Sandbox Code Playgroud)

请注意,ISDATE()函数受到一些重要限制,例如受会话的DATEFORMAT和LANGUAGE设置的影响.


如果eav行上有其他指示,您可以使用其他一些测试来有条件地执行转换.

CASE WHEN eav.ValueIsDateTime=1 THEN CONVERT(DATETIME, eav.Value) ELSE NULL END
Run Code Online (Sandbox Code Playgroud)

我使用的另一种方法是尝试使用内联视图或公用表表达式对优化程序的操作顺序进行一些控制,其中操作强制优化程序实现它们并应用谓词,以便在此之前发生外部查询中的任何转换.