SqlServer 的提示符(Option/With等提示符)不是什么时候都可以用的

我们在做SqlServer的查询调优的时候,经常会在语句末尾用到option(loop/merge/hash join)或在join语句前直接声明loop/merge/hash,来强制SqlServer使用某一特定类型的join方式。但是有些时候经过查询优化器优化后的执行计划可能会和你声明的join方式产生冲突,导致执行计划生成失败,我们来看一下下面这个典型案例。

 

declare @id1 int=1,@id2 int=1

select * 
from [dbo].[T_People] a 
 inner join [dbo].[T_People_II] b on a.id=b.id
where
a.id=@id1 and b.id=@id2
option(hash join)

我们在上面的查询语句中使用了option提示符强制让SqlServer使用Hash Join来做join查询,在执行该语句时我们会得到如下错误信息:

消息 8622,级别 16,状态 1,第 3 行
由于此查询中定义了提示,查询处理器未能生成查询计划。请重新提交查询,并且不要在查询中指定任何提示,也不要使用 SET FORCEPLAN。

 

为什么SqlServer会产生这么一个信息呢?

主要是因为查询优化器在分析上面这个语句的时候发现了inner join的条件是

a.id=b.id

然后又在where条件中发现了

a.id=@id1 and b.id=@id2

 

那么在查询优化器看来,@id1和@id2可能为任何int类型的值,甚至有可能@id1=@id2,比如@id1=@id2=1

那么这个时候查询优化器会认为inner join完全是多余的,所以上面的语句就被查询优化器优化为了cross join的方式,如下方式,然后在这个结果上再做了a.id=1 and b.id=1的过滤

select * 
from [dbo].[T_Peopl] a 
 cross join [dbo].[T_People_II] b

这个时候你去观察上面这个语句的查询计划,发现在Nested Loops有一个红色的小叉,表示这个join是没有Predicate的,也就是没有join条件的(Cross Join就没有条件),术语叫非equijoin,实际上下面这个执行计划中join两边的索引,只要有一边没有Predicate,那么该join都是非equijoin。

 

而非equijoin是不能使用Merge/Hash join的,换句话说cross join是不能用Merge/Hash join的,只能用Loop join,所以最上面的那条语句会报错,要求去掉option提示符。所以SqlServer的提示符不是什么时候都可以用的,甚至with(index(...))这种强制使用索引的提示符也不能随便乱用,因为执行计划中的某些步骤可能会和你声明的提示符产生冲突,从而导致执行计划生成失败而报错,要根据具体情况具体分析合理使用各种SqlServer的提示符。

 

转载于:https://www.cnblogs.com/OpenCoder/p/5800598.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值