假设以下模式和查询:
请看一下在我们期望为整数的 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 子句之前实际应用 cast 函数。
我们能够通过在子表上创建一个新索引来解决这个问题(它正在执行 PK扫描)
create index [NCIDX_dbo_Child__TypeId] on dbo.Child (
TypeId
)
include (
ParentId,
varcharColumn
)
它现在首先过滤父表的 where 子句。
有没有办法在没有额外索引的情况下解决这个问题?同样,请不要提出任何与修复我们的架构相关的建议。在这种情况下,这绝对是正确的解决方法。
我最感兴趣的是了解为什么它在过滤结果集之前应用了演员表。
谢谢
编辑 - 答案:
非常感谢 Aaron 和 Gordon。如果我得到超过 15 个代表点,我会回来并回复您的两个回复。
我们最终需要 Gordon 的答案,因为我们想在视图中使用此查询。办公室里的一些人对使用 case 语句持谨慎态度,因为他们更喜欢有更多的控制权来确保我们首先有一个较小的结果集(Aaron 的回答),但这一切都归结为查看查询计划并检查您的阅读计数。
再次感谢所有回复!