|
本帖最后由 newkid 于 2021-7-18 03:30 编辑
我的思路是构造等值连接条件,这样就可以用HASH JOIN, 原有的比较条件可以作为附加的 filter
下面是刘兄提供的测试用例:
create table range_a(id number);
create table range_b(start_id number,end_id integer,type number);
insert into range_a
select round(dbms_random.value(1,25000000)) from dual connect by level<=200000;
insert into range_b
select 10000*(level-1)+3000 as start_id, 10000*level as end_id , level as type
from dual connect by level<=2000
order by dbms_random.value;
commit;
create index idx_range_b_0 on range_b(start_id,end_id);
create index idx_range_b_1 on range_b(end_id);
create table range_result (id number, type number);
create table range_result2 (id number, type number);
--原sql:
insert into range_result
SELECT ID, TYPE
FROM range_a A,range_b B
WHERE A.ID>=B.START_ID AND A.ID<=B.END_ID;
-------- 刘兄写法:
--12c+版本
insert into range_result
select *
from
(
select id,
(select case when start_id<=a.id then type else null end as type
from
(select /*+ index(range_b idx_range_b_1) */ type,start_id
from range_b
where end_id>=a.id
order by end_id
) where rownum=1
) as type
from range_a a
) where type is not null;
--- 111824 rows created.
--- Elapsed: 00:00:01.23
按我的思路构造的写法:分析B表数据特点,范围以千计, 所以把 id/10000 进行等值比较。范围除以10000之后可能有多个值,比如从13000—20000这个返回,除以10000之后会映射到1,2两个值,所以一行B表数据会变成多行,我用了12C的LATERAL方法来复制B表数据,在12C之前可以用传统的CONNECT BY方法。
insert into range_result2
with b as (
select b.*,trunc(start_id/10000)+n-1 as compare_id ------- 构造一个等值比较的 ID
from range_b b
,lateral(select level n from dual connect by level<=trunc(end_id/10000)-trunc(start_id/10000)+1)
)
select a.id,b.type from range_a a,b
where trunc(a.id/10000)=b.compare_id -------- 等值比较
and a.id between b.start_id and b.end_id ---------- 附加filter
;
111824 rows created.
Elapsed: 00:00:00.12
从执行计划可以看到,等值比较如愿以偿用上了HASH JOIN:
Execution Plan
----------------------------------------------------------
Plan hash value: 2108890611
----------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------------------
| 0 | INSERT STATEMENT | | 960K| 29M| 4124 (1)| 00:00:01 |
| 1 | LOAD TABLE CONVENTIONAL | RANGE_RESULT2 | | | | |
|* 2 | HASH JOIN | | 960K| 29M| 4124 (1)| 00:00:01 |
| 3 | NESTED LOOPS | | 2000 | 52000 | 4004 (1)| 00:00:01 |
| 4 | TABLE ACCESS FULL | RANGE_B | 2000 | 26000 | 3 (0)| 00:00:01 |
| 5 | VIEW | VW_LAT_535DE542 | 1 | 13 | 2 (0)| 00:00:01 |
|* 6 | CONNECT BY WITHOUT FILTERING| | | | | |
| 7 | FAST DUAL | | 1 | | 2 (0)| 00:00:01 |
| 8 | TABLE ACCESS FULL | RANGE_A | 200K| 1171K| 103 (1)| 00:00:01 |
----------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access(TRUNC("A"."ID"/10000)=TRUNC("START_ID"/10000)+"N"-1)
filter("A"."ID"<="B"."END_ID" AND "A"."ID">="B"."START_ID")
6 - filter(LEVEL<=TRUNC("END_ID"/10000)-TRUNC("START_ID"/10000)+1)
|
|