SQL Server 2019 忽略 WHERE 子句?

SQL Server 2019 ignoring WHERE clause?

当我尝试 运行 一个简单的聚合查询时出现错误。

SELECT MAX(CAST(someDate as datetime)) AS MAX_DT FROM #SomeTable WHERE ISDATE(someDate) = 1

错误:从字符串转换日期 and/or 时间时转换失败。

WHERE 子句应删除非日期条目,但这似乎并没有发生。我可以在 MAX() 中使用显式 CASE 语句来解决问题,但如果可以避免的话,我不想破坏查询。如果我使用较低的 COMPATIBILITY_LEVEL,它工作正常。如果我有少于 2^17 行,它工作正常。

-- SQLServer 15.0.4043.16
USE AdventureWorks;
GO
ALTER DATABASE AdventureWorks SET COMPATIBILITY_LEVEL = 150;
GO

-- delete temp table if exists
DROP TABLE IF EXISTS #SomeTable;
GO

-- create temp table
CREATE TABLE #SomeTable (
    someDate varchar(20) DEFAULT GETDATE()
);

-- load data, need at least 2^17 rows with at least 1 bad date value
INSERT #SomeTable DEFAULT VALUES;

DECLARE @i int = 0;

WHILE @i < 17
BEGIN

    INSERT INTO #SomeTable (someDate) SELECT someDate FROM #SomeTable
    SET @i = @i + 1;

END
GO

-- create invalid date row
WITH cteUpdate AS (SELECT TOP 1 * FROM #SomeTable)
UPDATE cteUpdate SET someDate='NOT_A_DATE'

-- error query
SELECT MAX(CAST(someDate as datetime)) AS MAX_DT
FROM #SomeTable 
WHERE ISDATE(someDate) = 1

--ERROR: Conversion failed when converting date and/or time from character string.

-- delete temp table if exists
DROP TABLE IF EXISTS #SomeTable;
GO

我会推荐 try_cast() 而不是 isdate():

SELECT MAX(TRY_CAST(someDate as datetime)) AS MAX_DT
FROM #SomeTable 

这是一种更可靠的方法:try_cast 不依赖于一些启发式猜测值是否可转换为 datetime(如 isdate() 那样),而是实际尝试进行转换,如果失败,returns null - 聚合函数 max() 很乐意忽略。

try_cast()(和姊妹函数 try_convert())是一个非常方便的函数,许多其他数据库都没有。

实际上我刚刚遇到了同样的问题(除了我确实转换为浮动)。似乎 SQL Server 2019 优化器有时(是的,它不可靠)决定在应用 WHERE 之前在 SELECT 部分执行计算。

如果您将兼容级别设置为较低版本,这也会导致使用不同的优化器(它始终使用兼容级别的优化器)。较旧的查询优化器似乎总是首先执行 WHERE 部分。

似乎降低兼容性级别已经是最好的解决方案,除非您想将所有 CAST 替换为 TRY_CAST(这也意味着您不会轻易发现实际错误,例如导致错误的 WHERE你的计算然后 return NULL 而不是正确的值)。