sql - SELECT 中的 MSSQL cast( [varcharColumn] to int) 在 WHERE 子句过滤掉错误值之前执行

标签 sql sql-server sql-server-2008

假设以下架构和查询:

请忽略在 varchar 列中包含我们期望为整数的值的明显设计问题。

create table dbo.Parent (
    Id bigint NOT NULL,
    TypeId int NOT NULL
)

create table dbo.Child (
    Id bigint NOT NULL,
    ParentId bigint NOT NULL,
    TypeId int NOT NULL,
    varcharColumn varchar(300) NULL
)

select cast(c.varcharColumn as int)
from dbo.Parent p (nolock)
    inner join dbo.Child c (nolock)
        on p.Id = c.ParentId
            and c.TypeId = 2
where p.TypeId = 13

休息:

由于无法转换为 int 的值,我们遇到了强制转换中断。在本例中:“123-1”。奇怪的是,正在转换的值被从最终结果集中过滤掉。

例如,这将返回零结果

select c.varcharColumn
from dbo.Parent p (nolock)
    inner join dbo.Child c (nolock)
        on p.Id = c.ParentId
            and c.TypeId = 2
where p.TypeId = 13
    and c.varcharColumn = '123-1'

查询计划最终会查看 Child 表并在 where 子句之前实际应用强制转换函数。

我们能够通过在子表上创建新索引来解决此问题(它正在执行 PK 扫描)

create index [NCIDX_dbo_Child__TypeId] on dbo.Child (
    TypeId
)
include (
    ParentId,
    varcharColumn
)

现在它首先过滤父表的 where 子句。

有没有办法在不使用额外索引的情况下解决这个问题?再次强调,请不要提出任何与修复我们的架构相关的建议。在这种情况下,这绝对是正确的解决方案。

我最感兴趣的是理解为什么它在过滤结果集之前应用强制转换。

谢谢

编辑 - 答案:

非常感谢亚伦和戈登。如果我获得超过 15 个代表点,我会回来并更新您的两条回复。

我们最终需要戈登的答案,因为我们想在 View 中使用此查询。办公室里的一些人对使用 case 语句持谨慎态度,因为他们更喜欢有更多的控制权来确保我们首先拥有较小的结果集(亚伦的回答),但这一切都归结为查看查询计划并检查您的阅读计数。

再次感谢您的所有回复!

最佳答案

首先,这不是一个“明显的设计问题”。 SQL 是输出的描述性语言,而不是指定如何完成处理的过程语言。一般来说,不能保证处理的顺序,这是一个优点。我可能会说存在设计问题,但它是围绕 SQL 语句中异常的一般处理的。

根据 SQL Server 文档 (http://msdn.microsoft.com/en-us/library/ms181765.aspx),您可以根据标量表达式的 CASE 语句的计算顺序嗯>。因此,以下内容应该有效:

select (case when isnumeric(c.varcharColumn) = 1 then cast(c.varcharColumn as int) end)

或者,更接近“int”表达式:

select (case when isnumeric(c.varcharColumn) = 1 and c.varcharColumn not like '%.%' and c.varcharColumn not like '%e%'
             then cast(c.varcharColumn as int)
        end)

至少您的代码正在执行显式 CAST。当强制转换是隐式的(并且有数百列)时,这种情况会更加糟糕。

关于sql - SELECT 中的 MSSQL cast( [varcharColumn] to int) 在 WHERE 子句过滤掉错误值之前执行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12305823/

相关文章:

php - 返回的数组让我很困惑

c# - 使用类从 asp.net 中的数据库填充下拉列表的方法是什么?

sql-server - SQL AlwaysOn - 如果您不将其用作集群/故障转移怎么办?

java - CallableStatement getResultSet 使用输出参数时返回 null

sql-server - 数据库 'tempdb' 中的 SHOWPLAN 权限被拒绝。在 SQL Server 2008 中

sql - 如何在SQL Server 2008中将整数(时间)转换为HH:MM:SS::00?

sql - EF Core 检查约束

sql - 使用两个 que 向 SQL 结果集添加附加列

sql - 从大数据集上昂贵的其他表更新值

sql-server - ON 是语法的一部分