full join优化改写经验
1、问题
在项目中遇到这样的一个需求,从设备信息表和设备使用记录表中统计每个单位有效的设备信息和未被使用过的设备数量,开发商做成了full join方式,下面模拟一下场景,t1表是设备信息表,t2表是设备使用记录表,code是单位code。
with t as(select tt1.code,accountTotal,notused_cntfrom (select count(1) accountTotal,code from t1 group by code) tt1full join (select count(1) notused_cnt,t1.codefrom t1,t2where t1.id=t2.d1(+)and t2.d1 is nullgroup by t1.code) tt2on tt1.code=tt2.code)
select * from t;
计划如下:
1   #NSET2: [1046, 10->10, 100] 
2     #PRJT2: [1046, 10->10, 100]; exp_num(3), is_atom(FALSE) 
3       #HASH FULL JOIN2: [1046, 10->10, 100]; key_num(1); col_num(3); mix_aagr(0);mix_dist(0); MEM_USED(1617KB), DISK_USED(0KB) KEY(TT1.CODE=TT2.CODE)
4         #PRJT2: [368, 10->10, 48]; exp_num(2), is_atom(FALSE) 
5           #HAGR2: [368, 10->10, 48]; grp_num(1), sfun_num(1), MEM_USED(1653KB), DISK_USED(0KB), distinct_flag[0]; slave_empty(0) keys(T1.CODE)
6             #PARALLEL: [229, 2000000->2000000, 48]; scan_type(FULL) range_sfun_opt(0)
7               #CSCN2: [229, 2000000->2000000, 48]; INDEX33557263(T1); btr_scan(1)
8         #PRJT2: [676, 10->10, 52]; exp_num(2), is_atom(FALSE) 
9           #HAGR2: [676, 10->10, 52]; grp_num(1), sfun_num(1), MEM_USED(1653KB), DISK_USED(0KB), distinct_flag[0]; slave_empty(0) keys(T1.CODE)
10            #SLCT2: [672, 50000->1999000, 52]; T2.D1 IS NULL
11              #HASH RIGHT JOIN2: [606, 2000000->2999000, 52]; key_num(1); col_num(2); MEM_USED(44450KB), DISK_USED(0KB) KEY(T2.D1=T1.ID)
12                #PARALLEL: [105, 1000000->1000000, 4]; scan_type(FULL) range_sfun_opt(0)
13                  #SSCN: [105, 1000000->1000000, 4]; IDX_DM_T2(T2); btr_scan(1); is_global(1)
14                #PARALLEL: [231, 2000000->2000000, 52]; scan_type(FULL) range_sfun_opt(0)
15                  #CSCN2: [231, 2000000->2000000, 52]; INDEX33557263(T1); btr_scan(1)Statistics
-----------------------------------------------------------------0           data pages changed0           undo pages changed5331        logical reads0           physical reads0           redo size595         bytes sent to client534         bytes received from client1           roundtrips to/from client0           sorts (memory)0           sorts (disk)0           rows processed0           io wait time(ms)1219        exec time(ms)used time: 00:00:01.220. Execute id is 804
语句中可以分析到t2表存储着使用记录,使用一次就记录一次,t1表是有效的设备信息,再根据业务需求分析,这里主要是要从有效的信息中获取未使用的设备信息,t1表总数是有效的,T2表中没有t1表使用记录的设备数据就是未使用的。那么整个需求主要依赖于t1表,t2表作用是获取未使用的记录。所以根据需求描述,可以使用left join去实现。
Left join: 返回左表中所有的记录以及右表中连接字段相等的记录,右表不满足的记录记为null。
根据left join的了解,右表null的数据就是未使用的数据。那么整体上就可以优化改写了
2、改写
select code, count(1) accountTotal, sum(case when tt.d1 is null then 1 else 0 end) as notused_cntfrom (select t2.d1, t1.code from t1 left join (select distinct d1 from t2) t2on t1.id=t2.d1) tt
group by code
计划如下:
1   #NSET2: [683, 10->10, 56] 
2     #PRJT2: [683, 10->10, 56]; exp_num(3), is_atom(FALSE) 
3       #HAGR2: [683, 10->10, 56]; grp_num(1), sfun_num(2), MEM_USED(1653KB), DISK_USED(0KB), distinct_flag[0,0]; slave_empty(0) keys(DMTEMPVIEW_889194854.TMPCOL0)
4         #PRJT2: [542, 2000000->2000000, 56]; exp_num(2), is_atom(FALSE) 
5           #HASH RIGHT JOIN2: [542, 2000000->2000000, 56]; key_num(1); col_num(2); MEM_USED(1105KB), DISK_USED(0KB) KEY(T2.D1=T1.ID)
6             #PRJT2: [170, 1000->1000, 4]; exp_num(1), is_atom(FALSE) 
7               #DISTINCT: [170, 1000->1000, 4], MEM_USED(871KB), DISK_USED(0KB)
8                 #PARALLEL: [105, 1000000->1000000, 4]; scan_type(FULL) range_sfun_opt(0)
9                   #SSCN: [105, 1000000->1000000, 4]; IDX_DM_T2(T2); btr_scan(1); is_global(1)
10            #PARALLEL: [231, 2000000->2000000, 52]; scan_type(FULL) range_sfun_opt(0)
11              #CSCN2: [231, 2000000->2000000, 52]; INDEX33557263(T1); btr_scan(1)Statistics
-----------------------------------------------------------------0           data pages changed0           undo pages changed3223        logical reads0           physical reads0           redo size595         bytes sent to client359         bytes received from client1           roundtrips to/from client0           sorts (memory)0           sorts (disk)0           rows processed0           io wait time(ms)667         exec time(ms)used time: 667.566(ms). Execute id is 805.
改写后提交了1倍的性能。
3、小结
改写要从实际需求出发,了解其真正的需求,思考从更高效的方法去获取,这里主要思路是减少计算次数,提高效率,能一次性处理的就要一次性处理。
