sql server 错误地重写了我的查询?

标签 sql sql-server

输入中有脏数据。 我们正在尝试清理数据集,然后对清理后的数据进行一些计算。

declare @t table (str varchar(10))
insert into @t select '12345' union all select 'ABCDE' union all select '111aa'

;with prep as
(
select *, cast(substring(str, 1, 3) as int) as str_int
from @t
where isnumeric(substring(str, 1, 3)) = 1
)

select * 
from prep
where 1=1
and case when str_int > 0 then 'Y' else 'N' end = 'Y'
--and str_int > 0

最后两行正在做同样的事情。第一个可以工作,但如果取消注释第二个,它将崩溃,并显示将 varchar 值“ABC”转换为数据类型 int 时转换失败。

显然,SQL Server 正在重写将所有条件混合在一起的查询。 我猜它认为“case”是一项重要的操作,并将其作为最后一步执行。这就是案例解决方法有效的原因。

此行为是否以任何方式记录下来?或者这是一个错误?

最佳答案

这是 SQL Server 的一个已知问题,尽管用户认为这是一个错误,但 Microsoft 并不认为这是一个错误。两个查询之间的区别在于执行路径。一种是在过滤之前进行转换,另一种是在过滤之后进行转换。

SQL Server 保留重新排序处理的权利。 documentation确实将子句的逻辑处理指定为:

  1. 来自
  2. 开启
  3. 加入
  4. 哪里
  5. 分组依据
  6. 使用多维数据集或使用 ROLLUP
  7. 拥有
  8. 选择
  9. 独特
  10. 排序依据
  11. 顶部

(可能但这里没有明确记录)CTE 首先被逻辑处理。 逻辑处理是什么意思?嗯,这并不意味着运行时错误会被捕获。它真正决定了编译阶段标识符的范围。

当 SQL Server 从数据源读取数据时,它可以添加新变量。此时执行此操作很方便,因为所有内容都在内存中。但是,这可能发生在过滤之前,这就是导致错误发生的原因。

解决此问题的方法是使用 case 语句。因此,以下 CTE 通常会起作用:

with prep as (
      select *, (case when isnumeric(substring(str, 1, 3)) = 1 and str not like '%.%'
                      then cast(substring(str, 1, 3) as int)
                 end) as str_int
      from @t
      where isnumeric(substring(str, 1, 3)) = 1
     )

看起来很奇怪。我想雷德蒙德也是这么想的。 SQL Server 2012 引入了 try_convert()(请参阅 here),如果转换失败,它会返回 NULL

如果您可以指示 SQL Server 实现 CTE,也会有所帮助。这也可以解决本例中的问题。您可以投票决定向 SQL Server 添加此类选项 here .

关于sql server 错误地重写了我的查询?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24511836/

相关文章:

sql - 如何跳过子查询的匹配结果?

java - SQL语句花费大量时间,是否可以对其进行优化?

mysql - 基于连接更新 mysql 表

java - Java函数中SQL语句的执行

html - 在 SQL Server 中存储 HTML

sql-server - 就在我使用约束时,数据库中已经存在同名对象

sql - 表命名困境 : Singular vs. 复数名称

php - 在 Zend Framework 2 中生成具有多个数据库连接的 Doctrine 2 实体

sql - 连接来自 Teradata SQL 中多条记录的字符串

mysql - 从 MySQL 切换到 MSSQL for classic asp