奇怪的 SQL 语句行为

Ahm*_*yed 3 sql-server sql-server-2008-r2

我有一个简单的 SQL 语句(在从 2000 升级到 2008 R2 的 SQL Server 上执行):

SELECT TOP (1)
    DocNox = CONVERT(integer, SUBSTRING(DocNo, 2, LEN(DocNo)-1)
FROM Tbl1
WHERE
    Fld1 = 19 
    AND Fld2 = 1
    AND Fld3 = 1 
    AND DocNo NOT LIKE '%-%'
ORDER BY
    CONVERT(integer, SUBSTRING(DocNo, 2, LEN(DocNo)-1) DESC;
Run Code Online (Sandbox Code Playgroud)

示例数据(Serial 是一IDENTITY列)

Serial   DocNo    Fld1   Fld2   Fld3
211      A1       19     1      1
212      A2       19     1      1
213      A003     19     1      1
214      X1-C1-1  20     1      1
Run Code Online (Sandbox Code Playgroud)

执行查询会产生以下错误消息:

Error converting nvarchar value '1-C1-1' to a column of data type int.

这是出乎意料的,因为包含该值的行应该WHERESELECT和/或ORDER BY子句中的转换之前被子句过滤掉。

我注意到以下更改可以防止错误发生:

  • 改变条件 WHERE Fld1 = 19
  • 将数据复制到另一个具有相同结构的表
  • 删除 TOP (1)
  • 删除 ORDER BY
  • 更改ORDER BY为不同的列(例如串行)

但是在以下情况下仍然会出现错误:

  • 将整个表导入另一个数据库
  • 对具有相同数据的不同数据库运行相同的语句

我还对防止错误的统计数据进行了更改,但我不记得我做了什么。

任何人都可以解释这里发生了什么吗?

usr*_*usr 5

这是正常的。T-SQL 通常不指定评估顺序。直觉上,您会首先评估 where 子句,但情况并非必须如此。SQL Server 决定反对完全有效的那个。

目前,SQL Server 没有提供 100% 确定永远不会遇到此问题的构造。不过有一个 99% 确定的技术:在任何危险的东西周围包裹一个 case 表达式:

case when DonNo like '%-%' then (/* convert */) end
Run Code Online (Sandbox Code Playgroud)

除了在 Microsoft Connect 上记录的极少数极端情况外,这几乎总是有效。我现在手头没有它们。