SQL Server查询的奇怪行为

时间:2017-12-13 19:40:35

标签: sql sql-server-2008 tsql

我在验证存储过程中有一个查询。它是这样的:

SELECT *  
FROM Table1  
WHERE batchID IN (SELECT id FROM #tempIds) 
  AND CAST(field1 AS DATE) > CAST(field2 AS DATE)

field1field2都有有效日期,即在field1 / 2上执行IdDate会返回1.

#tempIds表只有一列ID,只包含一行。

当我运行上述查询时,我收到此错误:

  

无法将varchar转换为日期

但是,如果我从同一个临时表中放入硬编码的ID,它不会从临时表中选择批处理ID。

任何想法可能是什么问题?

1 个答案:

答案 0 :(得分:2)

问题是您使用varchar来存储date(或datetime)个值。
为列选择正确的数据类型可以避免许多问题,包括这个问题。有关详细信息,请阅读Aaron Bertrand的Bad habits to kick : choosing the wrong data type

现在,要在评论中解决我们的对话 - SQL Server不保证评估where子句中条件的顺序。这意味着即使所有"日期& #34;您的列中的字符串都可以转换为特定batchID的日期值,您只需要在其中一列中输入一个错误的值来提升"无法将varchar转换为日期"错误。

这也意味着即使你在哪里写你的查询,拉里B在他的回答中建议的方式(现在删除 - 所以为了未来的读者 - 这是他的建议:)

SELECT *  
FROM Table1  
WHERE batchID IN (SELECT id FROM #tempIds) 
AND ISDATE(field1) = 1 
AND ISDATE(field2) = 1 
AND CAST(field1 AS DATE) > CAST(field2 AS DATE)

无法保证最后一个条件(cast(field1 as date) > cast(field2 as date))会在<{strong> isdate(field1)=1条件后进行评估。

正确的做法是解决问题 - 将列的数据类型更改为正确的数据类型。

假设无法完成(例如,如果您无法控制数据库的结构) - 您可以做以下几件事:

  1. 找到field1field2中的值无法转换为日期并修复它们的所有位置。考虑添加一个检查约束来验证这些列的值是否可以转换为日期(假设您可以)。

  2. 将您的查询分为两部分:

  3. ;With cte as
    (
        SELECT *  
        FROM Table1  
        WHERE batchID IN (SELECT id FROM #tempIds) 
        AND ISDATE(field1) = 1 
        AND ISDATE(field2) = 1 
    )
    SELECT *
    FROM cte
    WHERE CAST(field1 AS DATE) > CAST(field2 AS DATE)
    

    这将消除错误,因为您只是在ISDATE函数已经返回1的情况下转换值,但如果值为field1或{{{},则可能不返回您要返回的某些行1}}这些行是错误的。