join时显示nojoinpredicate原因分析以及解决办法

专注,勤学,慎思。戒骄戒躁,谦虚谨慎 专注,勤学,慎思。戒骄戒躁,谦虚谨慎     2022-08-20     558

关键词:

 

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

  

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

  

 

  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中也确实没有连接谓词,这种情况下也很容易理解。

 

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

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

  

  

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

  

 

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

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

  

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

  

 

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

  

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

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

  

 

  那么这里的笛卡尔积是怎么出现的?具体数据我不方便分析,这里做一个简单的推倒
  比如这么一个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中还有很多很多未知的知识等着去学习和挑战。

 

DataTable:如何隐藏分页并仅在需要时显示?

】DataTable:如何隐藏分页并仅在需要时显示?【英文标题】:DataTable:Howtohidethepaginationandonlyshowitasneed?【发布时间】:2015-04-2001:24:23【问题描述】:我有2个表正在使用DataTablejQuery插件。我想知道是否有办法在表格的右下角隐藏我... 查看详情

加载 Webview 时显示图像

】加载Webview时显示图像【英文标题】:ShowImageWhileLoadingWebview【发布时间】:2019-04-1519:20:09【问题描述】:我试图在加载WKWebView时显示图像(徽标)。所以,查看其他帖子,我会在ViewDidLoad()中将图像放在屏幕上,并在didFinish方法... 查看详情

求助:ansys做齿轮的模态分析,求解时显示出错:modalanalysishasnomass.请问是数目原因?谢谢

参考技术AANSYS做模态分析时,给定材料性质时,必须要给定弹性模量(某种形式的刚度)与密度(或某种形式的质量)!modalanalysishasnomass:指说明没有给定材料的密度. 参考技术B2楼正解!没有定义材料密度。 参考技术C2 查看详情

找回华为账号密码时为啥存在安全风险找回华为账号密码时显示存在安全风险的原因

参考技术A1、原因是短时间内频繁找回密码界面。应该24小时之后再找回,显示存在风险是找回密码的时候点击过于频繁。2、短时间内频繁进入找回密码界面操作帐号,系统检测帐号存在安全风险,便会出现此风险提示。3、华为... 查看详情

每次应用启动时显示初始视图

】每次应用启动时显示初始视图【英文标题】:Showinitialvieweverytimetheapplaunches【发布时间】:2012-05-0819:13:11【问题描述】:出于安全原因,我需要用户在每次打开我的应用程序时登录。我想做的是当应用程序即将关闭时将其重置... 查看详情

ListView 为空时显示空视图

】ListView为空时显示空视图【英文标题】:ShowingemptyviewwhenListViewisempty【发布时间】:2011-04-1520:30:05【问题描述】:由于某种原因,即使ListView不为空,空视图(在这种情况下为TextView)也会始终出现。我认为ListView会自动检测何... 查看详情

用eclipse导入maven工程时显示少包如何解决?

-11上午09时57分45秒:Missingartifactcommons-dbcp:commons-dbcp:jar:1.3:compile15-9-11上午09时57分45秒:Missingartifactmysql:mysql-connector-java:jar:5.1.23:compile15-9-11上午09时57分45秒:Missingartifactcom.oracle:ojdbc:jar:jdk15:11.2.0.3:compile15-9-11上午09时57分45秒:Missingartifac... 查看详情

wx小程序只有一张图时显示大图,多张图时显示一排三张,如何实现?

参考技术Awx小程序只有一张图时显示大图,多张图时显示一排三张,可以试试重新排序。追问上代码 参考技术B最佳原因:Word中插入背景后,如页面增加,背景则增加;且如放大/缩小页面,背景也随之改动。 参考技术C这个应该... 查看详情

extjs 组合框在单击时显示空白列表

】extjs组合框在单击时显示空白列表【英文标题】:extjscomboboxshowsblanklistwhenclicked【发布时间】:2021-06-0818:24:46【问题描述】:我是extjs的新手,我试图从API调用返回的数据显示到组合框中,但由于某种原因,组合框没有显示“fir... 查看详情

请问致远a8oa协同办公软件打开文件时显示:被迫下线与服务器失去联系,是啥原因?

参考技术A是有用户数限制的,或者服务器端没有安装好 参考技术B打开ie浏览器的internet选项页面,在“高级”栏位中点击“重置”按钮可以解决被迫下线问题 参考技术C是不是加密狗松动了,找不到企业信息了 查看详情

在配置单元中执行查询时显示 Tez 顶点错误

】在配置单元中执行查询时显示Tez顶点错误【英文标题】:Tezvertexerrorshownwhileexecutionofqueryinhive【发布时间】:2017-12-1411:08:32【问题描述】:谁能解释一下使用Tez执行引擎时Hive中的VERTEX_FAILURE错误是什么?还有它的根本原因是什... 查看详情

geany写c语言,printf打印中文时显示乱码

原因geany设置了编码格式为utf8运行时显示出的cmd窗口编码格式为GBK解决方法打开cmd窗口,使用“chcp65001”命令,临时设置cmd窗口显示为utf编码格式,然后手工运行程序即可正常显示。永久修改cmd窗口显示为utf8编码格式。参考:wi... 查看详情

图像仅在给定链接时显示,而不是路径 [重复]

】图像仅在给定链接时显示,而不是路径[重复]【英文标题】:ImageOnlyShowsWhenGivenaLink,NotaPath[duplicate]【发布时间】:2020-12-2100:56:58【问题描述】:我正试图在我的网站上展示一张图片,但遇到的麻烦比我想象的要多。我已经多次... 查看详情

向下滚动之前,在页面加载时显示粘滞的“返回顶部”按钮

】向下滚动之前,在页面加载时显示粘滞的“返回顶部”按钮【英文标题】:Sticky"backtotop"buttonshowingonpageload,beforescrollingdown【发布时间】:2014-04-1704:48:23【问题描述】:我按照教程获得了一个粘性“返回顶部”按钮,一... 查看详情

accesssql语句运行时显示无效的sql语句是啥原因

altertable语句提示无效1、首先我们打开电脑里的Access2010软件,软件会默认开启一个表名为【表1】的空白表单。2、将空白表单表名修改为【测试表】,添加字段和几行测试数据。3、默认软件工具栏是【开始】工具栏,我们点击【... 查看详情

在 Android 中显示对话框时显示 AppBar

】在Android中显示对话框时显示AppBar【英文标题】:ShowAppBarwhenshowingdialoginAndroid【发布时间】:2019-11-2621:29:07【问题描述】:我有一个自定义的Dialog,它可以正常工作,但是有没有办法让对话框适应屏幕?所以可以看到AppBar(下... 查看详情

包含 php-mysql 连接文件时显示空白页

】包含php-mysql连接文件时显示空白页【英文标题】:Displayingblankpagewhenincludingphp-mysqlconnectionfile【发布时间】:2015-12-1219:08:17【问题描述】:我正在使用Ubuntu14.04并且我正在使用灯泡。我有一个更复杂的问题,但我找出了导致错误... 查看详情

Kendo UI tabstrip - 切换时显示选项卡的延迟

】KendoUItabstrip-切换时显示选项卡的延迟【英文标题】:KendoUItabstrip-delayindisplayingthetabwhenswtiching【发布时间】:2017-12-0110:27:44【问题描述】:我正在使用带有3个选项卡和KendoGrids的KendoUI选项卡,所有3个选项卡都没有分页。我的标... 查看详情