我正在修改客户端的现有查询,我遇到了一个令人困惑的问题。
我们的客户使用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.
真正的问题是:如果我将此查询的基础(将两个自定义字段值的实体列表展平为一行)定义为视图并选择视图并按{{过滤视图1}},然后它可以正常工作,但如果我使用视图中的一个(非日期)字段向第二个表添加连接,它将失败。我意识到这可能有点难以理解,所以我可以根据需要发布一个示例查询,但这个问题已经有点长了。
我尝试在另一个数据库中重新创建基本结构并包含示例数据,但新数据库的行为与预期一致,所以我在这里不知所措。
编辑如果它有用,这里是视图的声明:
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'
然而这有效:
select *
from Festival f
join FestivalAttendeeAll fa on fa.FestivalId = f.FestivalId
where getdate() between f.ActivityStart and f.ActivityEnd
(select *
from Festival f
where getdate() between f.ActivityStart and f.ActivityEnd
/ EntityId
是int列)
答案 0 :(得分:11)
之前我遇到过这种类型的错误,这是由于执行计划执行的“操作顺序”。
您收到该错误消息,因为您的语句的执行计划(由优化程序生成)正在对包含无法转换为DATETIME的字符串值的行执行CONVERT()操作。
基本上,您无法控制优化程序执行转换的行。您知道您只需要在某些行上完成转换,并且您具有排除这些行的谓词(WHERE或ON子句)(将行限制为需要转换的行),但您的执行计划正在执行CONVERT()操作排除这些行之前的行。
(例如,优化器可能会选择执行表扫描,并在应用任何谓词之前在每一行上执行该转换。)
如果没有生成错误的特定问题和特定SQL,我无法给出具体答案。
解决问题的一种简单方法是使用ISDATE()函数来测试字符串值是否可以转换为日期。
即,替换:
CONVERT(DATETIME,eav.Value)
使用:
CASE WHEN ISDATE(eav.Value) > 0 THEN CONVERT(DATETIME, eav.Value) ELSE NULL END
或:
CONVERT(DATETIME, CASE WHEN ISDATE(eav.Value) > 0 THEN eav.Value ELSE NULL END)
请注意,ISDATE()函数会受到一些重要限制,例如受会话的DATEFORMAT和LANGUAGE设置的影响。
如果在eav行上有其他指示,您可以使用其他一些测试来有条件地执行转换。
CASE WHEN eav.ValueIsDateTime=1 THEN CONVERT(DATETIME, eav.Value) ELSE NULL END
我使用的另一种方法是尝试使用内联视图或公用表表达式对优化程序的操作顺序进行一些控制,操作强制优化器实现它们并应用谓词,所以在外部查询中的任何转换之前发生。