数据库中多表连接的原理实现

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据库中多表连接的原理实现相关的知识,希望对你有一定的参考价值。

参考技术A 多变关联的实现方式有hash join,merge join,nested loop join 方式,具体使用那种内型的连接,主要依据:

1.当前的优化器模式(all_rows和rule)

2.取决于表的大小

3.取决于关联字段是否有索性

4.取决于关联字段是否排序

Hash  join散列连接,优化器选择较小的表(数据量少的表)利用连接键(join key)在内存中建立散列表,将数据存储到hash列表中,然后扫描较大的表

select A.*,B.* from A left join B on a.id=b.id。

先是从A表读取一条记录,用on条件匹配B表的记录,行成n行(包括重复行)如果B表没有与匹配的数据,则select中B表的字段显示为空,接着读取A表的下一条记录,right join类似。

left join基本是A表全部扫描,在表关键中不建议使用子查询作为副表,比如select A.*,B.*from A left join (select * from b where b.type=1 )这样A表是全表扫描,B表也是全表扫描。若果查询慢,可以考虑关联的字段都建索引,将不必要的排序去掉,排序会导致运行慢很多。

主副表条件过滤:

table a(id, type):

id    type

----------------------------------

1      1       

2      1         

3      2   

表b结构和数据

table b(id, class):

id    class

---------------------------------

1      1

2      2

Sql语句1: select a.*, b.* from a left join b on a.id = b.id and a.type = 1;

执行结果为:

a.id    a.type    b.id    b.class

----------------------------------------

1        1            1        1

2        1            2        2

3        2

a.type=1没有起作用

sql语句2:

select a.*, b.* from a left join b on a.id = b.id where a.type = 1;

执行结果为:

a.id    a.type    b.id    b.class

----------------------------------------

1        1            1        1

2        1            2        2

sql语句3:

select a.*, b.* from a left join b on a.id = b.id and b.class = 1;

执行结果为:

a.id    a.type    b.id    b.class

----------------------------------------

1        1            1        1

2        1           

3        2

b.class=1条件过滤成功。

结论:left join中,左表(主表)的过滤条件在on后不起作用,需要在where中添加。右表(副表)的过滤条件在on后面起作用。

mysql join原理:

Mysql join采用了Nested Loop join的算法,

###坐车 回去补充。

SQL Server中多表连接时驱动顺序对性能的影响

原文:SQL Server中多表连接时驱动顺序对性能的影响

 

本文出处:http://www.cnblogs.com/wy123/p/7106861.html 
(保留出处并非什么原创作品权利,本人拙作还远远达不到,仅仅是为了链接到原文,因为后续对可能存在的一些错误进行修正或补充,无他)

 

最近在SQL Server中多次遇到开发人员提交过来的有性能问题的SQL,其表面的原因是表之间去的驱动顺序造成的性能问题,
具体表现在(已排除其他因素影响的情况下),存储过程偶发性的执行时间超出预期,甚至在调试的时候,直接在存储过程的SQL语句中植入某些具体的参数,在性能上仍达不到预期的响应时间。
此类问题在排除了服务器资源因素,索引,锁,parameter sniff等常见问题之后,确认识是表之间的驱动顺序造成的,因为在尝试sql语句的末尾加上option(force order)之后,性能迅速提升。
通常情况下,表之间连接的时候是采用“小表驱动大表”是一种相对比较高效的方式,也即在loop join的时候,先循环小表,通过循环驱动大表,然后产生查询结果集。
该性能表面上看,是表之间的驱动顺序顺序造成的,在强制一个驱动顺序之后,性能有非常明显的提升,
但是再进一步思考,为什么默认情况下,SQL Server没有选择一个合理的驱动顺序?
因此本文就简单阐述这两个问题:
1)为什么表之间的驱动顺序会影响性能?
2)为什么SQL Server在某些情况下没有选择出正确的驱动顺序?

 

为什么表之间的驱动顺序会影响性能?

首先演示一下表在连接的时候,驱动顺序对性能的影响,其中test_smalltable插入1W行数据,test_bigtable插入10W行测试数据,依次来代表小表与大表

create table test_smalltable
(
    id int identity(1,1) primary key,
    otherColumns char(500)
)

create table test_bigtable
(
    id int identity(1,1) primary key,
    otherColumns char(500)
)


declare @i int = 0
while @i<100000
begin
    if @i<10000
    begin
        insert into test_smalltable values (NEWID())
    end
    insert into test_bigtable values (NEWID())
    set @i = @i + 1
end

在测试表写入数据完成之后,使用一下两个SQL,通过强制使用loop join的驱动顺序的方式来观察其IO情况

select * from test_smalltable a inner loop join test_bigtable b on a.id = b.id option(force order)
GO

select * from test_bigtable a inner loop join test_smalltable b on a.id = b.id option(force order)
GO

 如图,是两个SQL执行之后产生的IO信息,可以发现,因为两个表的驱动顺序不一致,导致的逻辑IO几乎差了一个数量级。

技术分享技术分享

造成此问题的原因,可能有一些难以理解,双循环嵌套,谁在外谁在内还有差别,表面上看不都是一样的?其实不然。
loop join是采用的类似如下双循环嵌套的方式来执行的,直至外层的表循环结束,循环(查询)完成
foreache(outer_row in outer_table)
{
  foreache(internal_row in internal_table)
  {
    if (outer_row.key = internal_row.key)
    {
      --输出结果
    }
  }
}

以上述测试为例,做一个粗略的对比统计
如果外层是小表(1W行),外层循环1W次,分别对内层的大表(10W行)查询,然后结束查询,相当于循环1W次,分别用Id查询内层表,
可以粗略地认为整体的代价是:1W+1W*10W = 11W,这里先忽略具体代价的单位
如果外层是大表(10W行),外层循环10W次,分别对内层的小表(1W行)查询,然后结束查询,相当于循环10W次,分别用Id查询内层表,
可以粗略地认为整体的代价是:10W+10W*1W = 20W,同理,这里也先忽略代价的单位
现在就很清楚了,前者(小表驱动大表)的代价是11W,后者(大表驱动小表)的代价是20W,因此,通常来说,小表驱动大表是一种相对较为高效的方式。

但是要注意这里的大表与小表,不仅仅是“表”级别的概念,因为实际中SQL并没有这么简单,还可以是根据筛选条件过滤之后的结果的概念,这也是引出第二个问题的关键点。

 

为什么SQL Server在某些情况下没有选择出正确的驱动顺序

  在上述的测试中,如果不加查询提示,执行计划的生成是跟表书写的顺序没有关系的,一下截图可以看到,书写顺序不一样,执行计划仍旧是一样的。
  也就是说,在书写SQL语句的时候,大表在前或者在后,正常情况下是不影响执行计划的生成的。

  技术分享

那么为什么,一开始提到的问题,为什么SQL Server在某些情况下没有选择出正确的驱动顺序还会出现?
实际情况中,SQL的写法很少有这么简单的,更多的时候是在表连接之后,有各种各样的where条件。
上面说了,大表与小表的概念,不仅仅是“表”级别的概念,更多的是根据筛选条件过滤之后的结果(行数,或者大小)的概念,
比如,如下SQL,在where条件上可能加上各种筛选条件,比如可能是类似于type类型的,可能是时间范围的,还有可能两个表上都有某些筛选条件。

select * from test_smalltable a 
inner join test_bigtable b on a.id = b.id 
where a.otherColumns = ‘‘ and b.otherColumns = ‘‘ and other filter condition

那么此时,在面对复杂的查询的时候,SQL Server如何评估每个表经过各种条件筛选后的结果集的大小?
当然是依据where 后面的筛选条件(或者是on 后面的加的筛选条件),问题就来了,where 后面或者on后面的筛选条件,如何又依据什么来提供一个大概的筛选后的结果集?
没错,又是统计信息!


现在问题就清晰了起来,SQL Server依据统计信息,在经过各种(或许是比较复杂)的筛选条件过滤之后,得到一个“它自己认为的预估大小的结果集”,然后依据这个结果集来决定驱动顺序。
SQL Server在“它自己认为的预估大小的结果集”的基础上进行类似于“小表驱动大表”的方式进行运算(当然不仅仅是loop join,这里暂不说其他的join方式),
一旦这个预估的结果集的大小有较大的误差,即便是误差不大,但是足以改变真正的“小表驱动大表”的方式进行运算,第二个问题就出现了。
因此,总的来说,错误的驱动顺序,本质上在利用统计信息进行预估的时候,因为统计信息不足够准确或者预估算法自己的问题。   
参考:http://www.cnblogs.com/wy123/tag/%E7%BB%9F%E8%AE%A1%E4%BF%A1%E6%81%AF%20Statistics/
导致SQL Server错误地用大表驱动的方式来执行运算,类似问题就出现了。


鉴于该问题的特殊性,很难造case,就不造case演示了,截两个实际遇到的对比结果。实际情况中,驱动顺序对性能产生的影响,可能是从0.5秒到10秒的差别,也可能是1分钟到10分钟的差别

  技术分享技术分享

  当然,加option(force order)的时候,要注意写法本身的是不是将小表放在了最前面,
  在复杂的情况下,虽然是驱动顺序造成的问题,但是加option(force order)并不一定好使,因为多表连接的时候,按照书写的方式强制驱动,也不一定刚好就是一个合理的驱动顺序
  甚至有更严重的问题出现,参考:http://www.cnblogs.com/wy123/p/6238844.html,因此不建议乱用option(force order)

 

 总结:

  面对较为复杂的查询和筛选条件的时候,尤其是在表中的数据较大的情况下,统计信息生成的预估,以及预估产生的表之间的驱动顺序,会对性能产生较大的影响。
  面对类似问题,要确实直接原因是什么,根本原因是什么,如何快速确认问题,又要如何解决和避免,都是值得思考的,也是做性能优化的时候要考虑的问题之一。












































以上是关于数据库中多表连接的原理实现的主要内容,如果未能解决你的问题,请参考以下文章

SQL Server中多表连接时驱动顺序对性能的影响

SQL Server中多表连接时驱动顺序对性能的影响

mysql中多表连接(不使用join )为啥 超过三张表就报错?

数据库原理-数据多表查询

Oracle中 多表连接到底有哪几种方式

数据库多表连接查询的实现方式