多表连接的三种方式详解hash join、merge join、nested loop

2022-01-17 08:02:02 浏览数 (2)

Oracle

多表连接的三种方式详解 hash join、merge join、 nested loop

构造测试数据

代码语言:javascript复制
DROP TABLE COUNTRY;
CREATE TABLE COUNTRY (
COUNTRY_ID SMALLINT NOT NULL,
COUNTRY_NAME VARCHAR(50) NOT NULL
);

DROP TABLE CITY;
CREATE TABLE CITY (
CITY_ID VARCHAR(50) NOT NULL,
CITY_NAME VARCHAR(50) NOT NULL,
COUNTRY_ID SMALLINT NOT NULL
);

BEGIN
FOR I IN 1 .. 10 LOOP
INSERT INTO COUNTRY VALUES(I,'country'||I);
END LOOP;
COMMIT;
END;

BEGIN
FOR I IN 1 .. 10000 LOOP
INSERT INTO CITY VALUES(I,'city'||I,CEIL(I/1000));
END LOOP;
COMMIT;
END;
Hash join:散列连接
代码语言:javascript复制
select c.city_name,t.country_name from CITY c
inner join COUNTRY t
on c.country_id=t.country_id

​ Hash join散列连接是CBO 做大数据集连接时的常用方式,优化器使用两个表中较小的表(通常是小一点的那个表或数据源)利用连接键(JOIN KEY)在内存中建立散列表,将列数据存储到hash列表中,然后扫描较大的表

​ 这种方式适用于较小的表完全可以放于内存中的情况,这样总成本就是访问两个表的成本之和 总 COST = COST(CITY) COST(COUNTRY) HASH TABLE 结果是 15=3 11 1 ​ 可以用USE_HASH(table_name1 table_name2)提示来强制使用散列连接。

适用情况:

  1. Hash join在两个表的数据量差别很大的时候.
SORT MERGE JOIN:排序合并连接
代码语言:javascript复制
SELECT /*  use_merge(C T) */C.CITY_NAME,
       T.COUNTRY_NAME
FROM CITY C
INNER JOIN COUNTRY T
ON C.COUNTRY_ID = T.COUNTRY_ID

​ Merge Join 是先将关联表的关联列各自做排序,然后从各自的排序表中抽取数据,到另一个排序表中做匹配。 ​ 因为merge join需要做更多的排序,所以消耗的资源更多。 总 COST = COST(CITY) COST(COUNTRY) SORT(CITY) SORT(COUNTRY) 结果是 17=3 11 1 2 ​ 通常来讲,能够使用merge join的地方,hash join都可以发挥更好的性能,即散列连接的效果都比排序合并连接要好。然而如果行源已经被排过序,在执行排序合并连接时不需要再排序了,这时排序合并连接的性能会优于散列连接。 ​ 可以使用USE_MERGE(table_name1 table_name2)来强制使用排序合并连接.

适用情况:

  1. RBO模式
  2. 不等价关联(>,<,>=,<=,<>)
  3. HASH_JOIN_ENABLED=false
  4. 用在没有索引,并且数据已经排序的情况.
NESTED LOOP:嵌套循环连接
代码语言:javascript复制
SELECT /*  use_nl(C T) */C.CITY_NAME,
       T.COUNTRY_NAME
FROM CITY C
INNER JOIN COUNTRY T
ON C.COUNTRY_ID = T.COUNTRY_ID

​ Nested loops 工作方式是循环从一张表中读取数据(驱动表outer table),然后访问另一张表(被查找表 inner table,通常有索引)。驱动表中的每一行与inner表中的相应记录JOIN。类似一个嵌套的循环。

耗费总 COST(CITY) rows(CITY)*COST(COUNTRY)

结果是 103=3 10*10

因为在rows不变的情况下 我们需要使总cost变小 可以尽可能的减少COST(COUNTRY)所以添加索引

代码语言:javascript复制
CREATE INDEX COUNTRY_ID_INDEX ON CITY(COUNTRY_ID)

再次查看执行任务

因为走了索引COST(COUNTRY)变成了6

总耗费63=3 6*10

​ 对于被连接的数据子集较小的情况,嵌套循环连接是个较好的选择。在嵌套循环中,内表被外表驱动,外表返回的每一行都要在内表中检索找到与它匹配的行,因此整个查询返回的结果集不能太大(大于1 万不适合),要把返回子集较小表的作为外表(CBO 默认外表是驱动表),而且在内表的连接字段上一定要有索引。当然也可以用ORDERED 提示来改变CBO默认的驱动表。

​ 使用USE_NL(table_name1 table_name2)可是强制CBO 执行嵌套循环连接。

适用情况:

适用于驱动表的记录集比较小(<10000)而且inner表需要有有效的访问方法(Index),并且索引选择性较好的时候.

JOIN的顺序很重要,驱动表的记录集一定要小,返回结果集的响应时间是最快的。

本站文章除注明转载/出处外,均为本站原创,转载前请务必署名,转载请标明出处 最后编辑时间为: 2021/12/01 11:36:39

0 人点赞