一尘不染

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

sql

假定以下架构和查询:

请忽略在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'

查询计划最终查看了子表,并在where子句之前实际应用了强制转换功能。

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

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

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

没有额外的索引,有什么方法可以解决此问题?同样,请避免提出任何与修复我们的架构有关的建议。在这种情况下,这绝对是正确的解决方法。

我最想了解的是为什么它在过滤结果集之前应用了强制类型转换。

谢谢

编辑-答案:

非常感谢亚伦和戈登。如果我获得超过15个代表点,我会回头再谈您的两个回答。

我们最终需要Gordon的答案,因为我们想在视图中使用此查询。办公室中的一些人对使用案例声明感到谨慎,因为他们希望对确保我们首先拥有较小的结果集(Aaron的答案)有更多的控制权,但是这全都归结为查看查询计划并检查您的阅读结果计数。

再次感谢您的所有答复!


阅读 165

收藏
2021-03-10

共1个答案

一尘不染

首先,这不是一个“明显的设计问题”。SQL是输出的描述性语言,而不是指定如何进行练习的过程性语言。通常,不能保证处理顺序,这是一个优势。我可能会说存在一个设计问题,但这是有关SQL语句中异常的一般处理。

根据SQL Server文档(http://msdn.microsoft.com/zh-cn/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。当强制转换是隐式的(并且有数百列)时,这种情况更加糟糕。

2021-03-10