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)提示来强制使用散列连接。
适用情况:
- 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)来强制使用排序合并连接.
适用情况:
- RBO模式
- 不等价关联(>,<,>=,<=,<>)
- HASH_JOIN_ENABLED=false
- 用在没有索引,并且数据已经排序的情况.
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)
所以添加索引
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