假设以下架构和查询:
请忽略在 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/