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 而不是正确的值)。
当我尝试 运行 一个简单的聚合查询时出现错误。
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 而不是正确的值)。