主T-SQL WHERE函数似乎被错误地应用于子查询

时间:2015-01-16 14:20:40

标签: sql tsql

此查询从tblValue返回一组日期,其FieldValue类型为nvarchar(4000)

SELECT t1.FieldValue FROM (SELECT FieldValue
FROM tblValue
WHERE FieldID = 4) t1
WHERE DateAdd(day, -90, t1.FieldValue) <= GETDATE()

这样做有效,但我不想对4的FieldID进行硬编码,而是希望得到所有类型为&#34; Expiration&#34;的FieldValues。 此查询返回4.

SELECT FieldID FROM tblField WHERE FieldType = 'Expiration'

所以,我希望这个查询的最里面的子查询返回4,然后将DateAdd仅应用于从最外面的子查询中的t1产生的那些Expiration值,这是在第一个例子中发生的情况

SELECT t1.FieldValue FROM (SELECT FieldValue
FROM tblValue
WHERE FieldID = (SELECT FieldID FROM tblField WHERE FieldType = 'Expiration')) t1
WHERE DateAdd(day, -90, t1.FieldValue) <= GETDATE()

但是我收到了错误

  

&#34;从字符串转换日期和/或时间时转换失败。&#34;

对我来说,建议将DateAdd应用于tblValue的所有值,而不仅仅应用于返回t1的子查询所产生的值。这可能是技术上的原因,但它对我来说似乎并不合适。出于某种原因

WHERE FieldID = 4) t1

不等于

WHERE FieldID = (SELECT FieldID FROM tblField WHERE FieldType = 'Expiration')) t1

碰巧的是,如果我放弃错误查询的最后WHERE子句,我会得到与工作查询中相同的日期集。所以t1不应该呈现DateAdd应该有问题的任何值。但它确实如此。我很困惑为什么。

3 个答案:

答案 0 :(得分:3)

这是因为优化程序生成的特定执行计划。根据它选择如何组合各个子句的比较和过滤操作,它可以先做一个或另一个。

在这种情况下,它会在应用FieldType过滤器之前尝试执行日期转换和比较。

这是一个众所周知的问题,但SQL优化器的行为是固有的 - 这是一个类似的问题,具有不同的数据类型:https://connect.microsoft.com/SQLServer/feedback/details/333312/error-8114-converting-data-type-varchar-to-numeric

有很多方法,但它们并不总是直截了当,通常要求你强制执行特定的执行顺序。

以下对我有用,虽然我知道CASE技术并不总是100%有效。来自this fiddle

SELECT t1.FieldValue FROM (SELECT FieldValue
FROM tblValue
WHERE FieldID = (SELECT FieldID FROM tblField WHERE FieldType = 'Expiration')) t1
WHERE CASE WHEN ISDATE(t1.FieldValue) = 1 THEN DateAdd(day, -90, t1.FieldValue) ELSE '1/1/2900' END <= GETDATE()

答案 1 :(得分:0)

我想你想要这个?

SELECT * FROM tblValue v
JOIN tblField f ON v.FieldID = f.FieldID
WHERE f.FieldType = 'Expiration' AND DateAdd(day, -90, v.FieldValue) <= GETDATE()

答案 2 :(得分:0)

将此分类为错误应用不公平 您无法控制TSQL将评估哪些行 有了一个很难4,优化器首先做了 没有硬4,查询优化器必须为任何事情做好准备并将其移至后期 查询优化器甚至考虑派生的表公平游戏来优化
如果您只是查看查询计划,就可以看到订单

尝试

SELECT * 
  FROM tblValue v
  JOIN tblField f 
    ON v.FieldID = f.FieldID
   AND f.FieldType = 'Expiration' 
   AND DateAdd(day, -90, v.FieldValue) <= GETDATE()