Nested Loops join时显示no join predicate原因分析以及解决办法

时间:2022-09-04 20:27:52

本文出处:http://www.cnblogs.com/wy123/p/6238844.html

  

  最近遇到一个存储过程在某些特殊的情况下,效率极其低效(同时服务器CPU资源占用急剧上升,导致整个服务器相应缓慢)
  至于底下到什么程度我现在都没有一个确切的数据,因为预期很快就可以查询出来结果的SQL,实则半个小时都出不来,后面会有截图
  观察执行计划的时候发现中间有一步中出现一个类似如下非常规的连接提示警告,如下图

  Nested Loops join时显示no join predicate原因分析以及解决办法

  no join predicate 意思就是没有连接谓词,表之间join的时候没有指定连接谓词可以导致no join predicate,
  但是反过来也是一定成立的吗,明明写了连接条件,仍旧提示no join predicate,为什呢?
  下面先从no join predicate 入手开始,说明什么时候会出现no join predicate ,以及原因和解决办法。

1,未指定连接条件下导致的no join predicate

  两个表在没有指定连接条件的情况下,做运算的结果是计算器笛卡尔积,当然是没有连接谓词的,提示no join predicate 也很容易理解
  上一段简单的代码演示一下,如下创建两张表,#t1,#t2,至于测试数据为什么是这样子,我下面会继续做解释

create table #t1(id int,name varchar(100))
create table #t2(id int,name varchar(100)) insert into #t1 values (1,newid())
insert into #t1 values (1,newid()) insert into #t2 values (1,newid())
insert into #t2 values (1,newid())

首先看计算笛卡尔积的时候的执行计划,Nested Loops 中的红叉叉,就表明是没有连接谓词,当然这个查询SQL中也确实没有连接谓词,这种情况下也很容易理解。

Nested Loops join时显示no join predicate原因分析以及解决办法

2,指定了连接条件下的no join predicate

  这里即便是指定了连接条件,仍然提示没有连接谓词,这个原因又是为什么呢?
  此时就需要看表中的数据特点了,从上面造的测试数据可以看出,#t1表id = 1 的是两行,#t2 表的同样,id = 1的数据也是两行
  此时两张表的join,是多对多的关系,多对多的情况下就是计算笛卡尔积,这就是这种情况下提示没有连接谓词的原因。
  详细请参考:http://www.cnblogs.com/liwei225/p/5056460.html,大神早就有详细的分析,感谢liwei225大神的分享

  Nested Loops join时显示no join predicate原因分析以及解决办法

  

  不过我这里还有一个疑问,还是上述两张表,指定连接条件,但是不指定查询条件,也就是没有where a.id = 1,此时就没有提示no join predicate
  这个原因我也没弄懂,后面再想想为什么,希望路过的大神帮忙解释一下,谢谢。

  Nested Loops join时显示no join predicate原因分析以及解决办法

3,指定了连接条件的情况下,某些查询条件下会出现no join predicate

  这是一个实际业务的SQL,从存储过程中扣出来的代码,因为有比较多的查询条件,最后组装的动态SQL也不完全一样,绝大多数情况下是没有问题的,
  但是当在where 条件中添加某一个查询条件之后,效率就开始严重下降,至于下降到什么程度,截图是运行了35分钟之后取消的
  在这个SQL运行期间,服务器CPU直接飙升至100%,并且是持续性的

  Nested Loops join时显示no join predicate原因分析以及解决办法

  截图一个对比测试的,仅仅在上面的SQL中加了一个OPTION(FORCE ORDER)查询提示,强制按照书写的表的顺序驱动,结果2秒钟就出来结果了
  执行计划跟上面是不一样的,同时也没有显示no join predicate,不能说加了一个强制提示就有了连接谓词,不加强制提示就没有连接谓词吧?
  从对比情况看,可以说明,没有非常严重的外界因素干扰,比如缺少索引,统计信息有问题等等
  倘若如此,加了OPTION(FORCE ORDER)查询提示的SQL与不加OPTION(FORCE ORDER)查询提示的SQL差别不可能这么大,一定是执行计划的选择出了问题。

  Nested Loops join时显示no join predicate原因分析以及解决办法

  那么就继续分析这个执行计划。
  通常情况下,我们会首先分析执行计划,什么索引使用(被抑制)了,索引碎片了,参数嗅探了,统计信息过期了(取样不够),都一一分析过,
  这些额外因素只会在一定程度上拖慢SQL的效率,而不是拖慢到如此相差几个数量级的程度
  那么来分析,没有加OPTION(FORCE ORDER)为什么会这么慢?
  实际上,这个SQL的执行计划只能从预估执行计划来看,因为实在等不到这个SQL运行完成而看实际执行计划
  如题,预估执行计划显式,中间有一步存在一个如上所述的没有连接谓词警告

  Nested Loops join时显示no join predicate原因分析以及解决办法

  我们看一下这个Nested Loops的详细信息,确实提示没有连接谓词,并且显式的预估行数为126469000行,超过了1亿行了,
  根据具体的数据分布和查询条件分析,如果不做笛卡尔积,这个中间结果是怎么也达不到亿级别的,这个妥妥的是笛卡尔积
  如果真的要计算出来超过一亿行这么大一个结果集,代价可想而知。

  实际上1亿行的笛卡尔积,并需要太多的基数,select 10000*10000就可以达到了,也就是两个过万的结果集做笛卡尔积运算,就可以算出来一亿行的结果
  结果也证明,第一个SQL在做查询的时候CPU飙升,而并没有很高的物理IO,慢就慢在笛卡尔结果的运算上。

  Nested Loops join时显示no join predicate原因分析以及解决办法

  那么这里的笛卡尔积是怎么出现的?具体数据我不方便分析,这里做一个简单的推倒
  比如这么一个SQL:
  select * from TableA a
    inner join TableB b on a.Identifier1 = b.Identifier1
    inner join TableC c on b.Identifier2 = c.Identifier2
  where a.Column_X = ***
    and b.Column_Y = ***
    and Other Filter Condition

  连接条件都是有的,我们暂时简化问题,忽略查询条件,从逻辑上分析
  正常逻辑是A表结果驱动B表( a.Identifier1 = b.Identifier1 ),
  用A表和B表join的结果,借助B表的Identifier2 驱动C表( b.Identifier2 = c.Identifier2 ),这里的A表和C表示没有直接关系的,
  如果A表和C表结合起来,最后驱动B表,可以想象,因为A表和C表之间没有直接的关系,强制连接的话,A表和C表计算出来的结果必然是笛卡尔积
  这个笛卡尔积就类似于上面截图Nested Loops中的预估的超过一亿行数的结果集。

  为什么SQL Server会私自更改表之前的连接方式,从而导致笛卡尔积?
  执行计划的选择是一个复杂的计算过程。执行计划的生成是跟索引,统计信息,表中的数据分布,系统资源等等多种因素一并计算出来的,
  SQL Server可能是根据查询条件,选择了自己认为一种“高效”的单个表查询方式,却忽略了表之间驱动的驱动顺序(个人猜测)。
  因此才会造如上推理的类似于“A表和C表之间没有直接的关系,强制连接”造成的笛卡尔积,
  根据预估的执行计划和实际表之间的关联关系分析得到,这个执行计划在处理表之间关联的处理上,正是如此。
  同时,在强制驱动顺序之后,很快地查询出来了结果,也能说明,用类似于A驱动B,A+B的结果驱动C这种方式的效率远远高于A+C计算笛卡尔积再驱动B的

  Sometimes SQL Server can remove a join predicate from the original query.

  那么,如果避免这种情况的呢?
  已知的是,上述SQL在执行的时候提示没有连接谓词,并不是真的没有写连接谓词,
  而是SQL Server改动了表之间驱动顺序,造成了部分没有直接关系的表放在一起生成笛卡尔积的结果

  方案一:

      OPTION(FORCE ORDER)是也验证过了,通过强制驱动顺序来让查询引擎按照顺序来实现,

  方案二:

  还是上面的例子来说明:
  比如原始的SQL类似如下:
  select * from TableA a
    inner join TableB b on a.Identifier1 = b.Identifier1
    inner join TableC c on b.Identifier2 = c.Identifier2
  where a.Column_X = ***
    and b.Column_Y = ***
    and Other Filter Condition

  将这个SQL改写一下
   select * from TableA a
    inner join TableB b on a.Identifier1 = b.Identifier1
    CROSS APPLY( select * TableC c where b.Identifier2 = c.Identifier2)
  where a.Column_X = ***
    and b.Column_Y = ***
    and Other Filter Condition
  用CROSS APPLY的方式,类似于强制用B表去驱动C表,就不会出现A表和C表结合从而出现笛卡尔积的情况
  事实也证明了,在改写实际SQL的过程中,这种方式也是切实可行的,效果相当于OPTION(FORCE ORDER)。

  方案三:同样是改写SQL,实际上述的SQL并不是太复杂,但也不是那种很简单的逻辑关联,可以通过在一定接住临时表,拆分出一个中间结果集

      用中间结果集的方式去驱动另外的表,简化每一步的连接逻辑,也可以避免中间产生笛卡尔积的情况
      事实证明,这种方式也是可行的,效果稍微亚于前两种方式,
      关于借助临时表做逻辑拆分的,也需要一定的技巧,这里有案例,http://www.cnblogs.com/wy123/p/5712001.html

总结:上述通过一个实际案例,分析了什么情况下会造成no join predicate,
     以及即便是写了连接条件,仍然会出现no join predicate的原因,当面对这种情况的时候,又可以通过什么办法来解决。
   当从新手开始,不敢在SSMS查询窗口中写SELECT(怕超过三个表的就写不好,被师傅骂),怕写Update DELETE语句(怕误操作),
   到写完一个又一个的SQL,慢慢地掌握了一些基础知识和技巧,再到后面了解了索引,执行计划表,统计信息,会用几个DMV,几个系统表,会看几个性能指标,服务器资源使用等信息
   开始做性能分析,性能优化的时候,当大多数问题手到擒来的时候,我觉得自己已经无所不能了,
   现实情况屡屡告诉我,你还有很多很多未知的问题,再一次感觉到自己如此的弱逼。
   我承诺,我以后再也不敢吹牛逼了。

参考:http://www.cnblogs.com/liwei225/p/5056460.html

     http://www.scarydba.com/2009/09/15/no-join-predicate/

    http://dba.stackexchange.com/questions/35082/what-exactly-does-no-join-predicate-mean-in-sql-server

     http://www.scarydba.com/2009/09/15/no-join-predicate/

2017,SQL Server中还有很多很多未知的知识等着去学习和挑战。

Nested Loops join时显示no join predicate原因分析以及解决办法的更多相关文章

  1. Cocos2D v3.4.9粒子效果不能显示的原因分析及解决办法

    大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请多提意见,如果觉得不错请多多支持点赞.谢谢! hopy ;) 在游戏App中为了衬托气氛我们往往使用一些特殊的图形效果,粒子 ...

  2. VC++ MFC单文档应用程序SDI下调用glGenBuffersARB(1, &pbo)方法编译通过但执行时出错原因分析及解决办法:glewInit()初始化的错误

    1.问题症状 在VC++环境下,利用MFC单文档应用程序SDI下开发OpenGL程序,当调用glGenBuffersARB(1, &pbo)方法编译通过但执行时出错,出错代码如下: OpenG ...

  3. 关于join时显示no join predicate的那点事

    我们偶尔,非常偶尔的情况下会在一个查询计划中看到这样的警告: 大红叉,好吓人啊! 把鼠标放上去一看显示这样的信息 No join predicate 直译过来就是:没有连接谓词 在真实的生产环境下我们 ...

  4. postgresql 使用pg_restore时显示role "root" does not exist的解决办法

    在docker里恢复bakcup格式的数据库,结果提示role "root" does not exist 解决方法: 切换用户: su - postgres 然后再次运行命令: ...

  5. 连接SQLServer时,因启用连接池导致孤立事务的原因分析和解决办法

    本文出处:http://www.cnblogs.com/wy123/p/6110349.html 之前遇到过这么一种情况: 连接数据库的部分Session会出现不定时的阻塞,这种阻塞时长时短,有时候持 ...

  6. 点击ViewGroup时其子控件也变成pressed状态的原因分析及解决办法

    这个问题,当初在分析touch事件处理的时候按理应该分析到的,可是由于我当时觉得这块代码和touch的主题不是那么紧密, 就这么忽略掉了,直到后来在这上面遇到了问题.其实这个现象做Android开发的 ...

  7. oracle执行update语句时卡住问题分析及解决办法

    转载:http://www.jb51.net/article/125754.htm 这篇文章主要介绍了oracle执行update语句时卡住问题分析及解决办法,涉及记录锁等相关知识,具有一定参考价值, ...

  8. Vue微信自定义分享时安卓系统config:ok,ios系统config:invalid signature签名错误,或者安卓和ios二次分享时均config:ok但是分享无效的解决办法

    简述需求:要求指定页面可以进行微信自定义分享(自定义标题,描述,图片,链接),剩下的页面隐藏所有基础接口.二次分享依然可以正常使用,切换至其他页面也可以正常进行自定义分享. 这两天在做微信自定义分享的 ...

  9. 关于jquery html()方法获取带有OBJECT标签的元素内容时,出现“类型不匹配。”的解决办法

    关于jquery html()方法获取带有OBJECT标签的元素内容时,出现“类型不匹配.”的解决办法 解决办法: $("selector").clone().html()

随机推荐

  1. React入门

    一.引入Reactjs 方法一:直接下载相关js文件引入网页,其中react.js 是 React 的核心库,react-dom.js 是提供与 DOM 相关的功能,Browser.js 的作用是将 ...

  2. redis-windows免安装版本安装多个redies

    1.复制两份redis:端口分别为6369和6379 2.修改端口 6379为redis默认的端,不改; 进入6369的下面找到如下配置文件: redis.conf 修改端口 然后分别启动redis- ...

  3. Emit学习(4) - Dapper解析之数据对象映射(一)

    感觉好久没有写博客了, 这几天有点小忙, 接下来会更忙, 索性就先写一篇吧. 后面估计会有更长的一段时间不会更新博客了. 废话不多说, 先上菜. 一.示例 1. 先建类, 类的名称与读取的表名并没有什 ...

  4. osg osgDB::Options noTexturesInIVEFile ForceReadingImage dds_flip

    osgDB::writeNodeFile(node, path, new osgDB::Options("noTexturesInIVEFile")); noTexturesInI ...

  5. 参加:白帽子活动-赠三星(SAMSUNG) PRO....

    参加:白帽子活动-—赠三星(SAMSUNG) PRO.... Everybody~小i在这里提前祝大家国庆假期愉快,咱们期待已久的国庆活动终于开始拉,下面进入正题,恩,很正的题! 活动地址:http: ...

  6. 获取radio和select的值,获取select的值

    获取radio的值 var val=$('input:radio[name="_objId"]:checked').val(); jQuery中获得选中select值 第一种方法$ ...

  7. leetcode:Excel Sheet Column Number

    Given a column title as appear in an Excel sheet, return its corresponding column number. For exampl ...

  8. [转载]initwithcoder和 initwithframe

    大前提是UIViewController有一个UIView.同时,需要厘清两个概念,创建一个类和实例化一个类.在XCode中创建一个类和实例化一个类很容易区分,但是在IB(Interface Buil ...

  9. 动态WebApi

    动态WebApi实现了直接对Service的调用,其实没有跨过ApiController,只是我们自己创建出ApiController 实现主要分以下几步 一 对默认WebApi服务的替换 ApiGl ...

  10. java中间缓存变量机制

    public static void main(String[] args){ int j = 0; for(int i = 0; i < 100; i++) j = j++; System.o ...