标量子查询SQL改写

时间:2023-03-09 19:37:52
标量子查询SQL改写

一网友说下面sql跑的好慢,让我看看

sql代码:

select er,
cid,
pid,
tbl,
zs,
sy,
(select count(sr.mobile_tele_no)
from tbl_sp_sales_records sr
where sr.task_id = tid
and sr.channel_id = cid
and sr.is_conn = '1'
and sr.sales_time >='2017-10-01 00:00:00'
and sr.sales_time <='2017-10-27 00:00:00'
) hc1,
(select count(sr.mobile_tele_no)
from tbl_sp_sales_records sr
where sr.task_id = tid
and sr.channel_id = cid
and sr.is_conn = '0'
and sr.sales_time >='2017-10-01 00:00:00'
and sr.sales_time <='2017-10-27 00:00:00'
) hc2,
(select count(1)
from tbl_disturb_customer_records cr
where cr.target_name = tbl
and cr.disturb_type in ('98', '99')) gz,
(select count(1)
from tbl_disturb_customer_records cr
where cr.target_name = tbl
and cr.disturb_type not in ('98', '99')) mr
from (select c.creator er,
tt.target_data tbl,
t.channel_id cid,
c.create_time ctime,
t.task_id tid,
c.campaign_id pid,
count_table_num_by_channelid(tt.target_data, t.channel_id) zs,
count_table_num(tt.target_data) sy
from tbl_sp_campaign c,
tbl_sp_task t,
tbl_task_targetdata tt
where c.campaign_id = t.campaign_id
and t.task_id = tt.task_id
and c.creator in ('fuzhou',
'lingde',
'longyan',
'nanping',
'putian',
'quanzhou',
'sanming',
'xiamen',
'zhangzhou')
and c.create_time >= '2017-10-01 00:00:00'
and c.create_time <= '2017-10-27 00:00:00')

执行计划

PLAN_TABLE_OUTPUT
Plan hash value: 2087309529 --------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 5 | 670 | 14 (8)| 00:00:01 |
| 1 | SORT AGGREGATE | | 1 | 74 | | |
|* 2 | TABLE ACCESS BY INDEX ROWID | TBL_SP_SALES_RECORDS | 1 | 74 | 9 (0)| 00:00:01 |
|* 3 | INDEX RANGE SCAN | IDX_SSR_STAREA | 7 | | 4 (0)| 00:00:01 |
| 4 | SORT AGGREGATE | | 1 | 74 | | |
|* 5 | TABLE ACCESS BY INDEX ROWID | TBL_SP_SALES_RECORDS | 1 | 74 | 9 (0)| 00:00:01 |
|* 6 | INDEX RANGE SCAN | IDX_SSR_STAREA | 7 | | 4 (0)| 00:00:01 |
| 7 | SORT AGGREGATE | | 1 | 26 | | |
|* 8 | TABLE ACCESS BY INDEX ROWID | TBL_DISTURB_CUSTOMER_RECORDS | 289 | 7514 | 82 (0)| 00:00:01 |
|* 9 | INDEX RANGE SCAN | IDX_TARGET_NAME | 2993 | | 20 (0)| 00:00:01 |
| 10 | SORT AGGREGATE | | 1 | 26 | | |
|* 11 | TABLE ACCESS BY INDEX ROWID | TBL_DISTURB_CUSTOMER_RECORDS | 4058 | 103K| 82 (0)| 00:00:01 |
|* 12 | INDEX RANGE SCAN | IDX_TARGET_NAME | 2993 | | 20 (0)| 00:00:01 |
|* 13 | HASH JOIN | | 5 | 670 | 14 (8)| 00:00:01 |
|* 14 | HASH JOIN | | 5 | 450 | 11 (10)| 00:00:01 |
|* 15 | TABLE ACCESS BY INDEX ROWID| TBL_SP_CAMPAIGN | 5 | 225 | 7 (0)| 00:00:01 |
|* 16 | INDEX RANGE SCAN | IDX_P_CREATE_TIME | 6 | | 2 (0)| 00:00:01 |
| 17 | TABLE ACCESS FULL | TBL_SP_TASK | 112 | 5040 | 3 (0)| 00:00:01 |
| 18 | TABLE ACCESS FULL | TBL_TASK_TARGETDATA | 112 | 4928 | 3 (0)| 00:00:01 |
-------------------------------------------------------------------------------------------------------------- Predicate Information (identified by operation id):
--------------------------------------------------- 2 - filter("SR"."TASK_ID"=:B1 AND "SR"."CHANNEL_ID"=:B2 AND "SR"."IS_CONN"='1')
3 - access("SR"."SALES_TIME">='2017-10-01 00:00:00' AND "SR"."SALES_TIME"<='2017-10-27 00:00:00')
filter(SUBSTR("SALES_TIME",1,10)>='2017-10-01' AND SUBSTR("SALES_TIME",1,10)<='2017-10-27')
5 - filter("SR"."TASK_ID"=:B1 AND "SR"."CHANNEL_ID"=:B2 AND "SR"."IS_CONN"='0')
6 - access("SR"."SALES_TIME">='2017-10-01 00:00:00' AND "SR"."SALES_TIME"<='2017-10-27 00:00:00')
filter(SUBSTR("SALES_TIME",1,10)>='2017-10-01' AND SUBSTR("SALES_TIME",1,10)<='2017-10-27')
8 - filter("CR"."DISTURB_TYPE"='98' OR "CR"."DISTURB_TYPE"='99')
9 - access("CR"."TARGET_NAME"=:B1)
11 - filter("CR"."DISTURB_TYPE"<>'98' AND "CR"."DISTURB_TYPE"<>'99')
12 - access("CR"."TARGET_NAME"=:B1)
13 - access("T"."TASK_ID"="TT"."TASK_ID")
14 - access("C"."CAMPAIGN_ID"="T"."CAMPAIGN_ID")
15 - filter("C"."CREATOR"='fuzhou' OR "C"."CREATOR"='lingde' OR "C"."CREATOR"='longyan' OR
"C"."CREATOR"='nanping' OR "C"."CREATOR"='putian' OR "C"."CREATOR"='quanzhou' OR
"C"."CREATOR"='sanming' OR "C"."CREATOR"='xiamen' OR "C"."CREATOR"='zhangzhou')
16 - access("C"."CREATE_TIME">='2017-10-01 00:00:00' AND "C"."CREATE_TIME"<='2017-10-27 00:00:00')

分析

我跟网友说:让他去掉sql里的标量,运行一次,他说很快

性能瓶颈在于标量子查询上,大家都知道,标量子查询可以改写成left join

改写后代码

select er,
cid,
pid,
tbl,
zs,
sy,
p. hc1,
p. hc2,
p2.gz,
p2. mr
from (select c.creator er,
tt.target_data tbl,
t.channel_id cid,
c.create_time ctime,
t.task_id tid,
c.campaign_id pid,
count_table_num_by_channelid(tt.target_data, t.channel_id) zs,
count_table_num(tt.target_data) sy
from tbl_sp_campaign c, tbl_sp_task t, tbl_task_targetdata tt
where c.campaign_id = t.campaign_id
and t.task_id = tt.task_id
and c.creator in ('fuzhou',
'lingde',
'longyan',
'nanping',
'putian',
'quanzhou',
'sanming',
'xiamen',
'zhangzhou')
and c.create_time >= '2017-10-01 00:00:00'
and c.create_time <= '2017-10-27 00:00:00') c
left join (select
sr.task_id,
sr.channel_id,
count(decode(sr.is_conn,1,sr.mobile_tele_no)) hc1,
count(decode(sr.is_conn,0,sr.mobile_tele_no)) hc2,
from tbl_sp_sales_records sr
where sr.sales_time >='2017-10-01 00:00:00'
and sr.sales_time <='2017-10-27 00:00:00'
group by sr.task_id,sr.channel_id)p
on (p.task_id = c.tid and p.channel_id = c.cid)
left join (select
count(case when disturb_type in ('98', '99') then 1 end )gz,
count(case when disturb_type not in ('98', '99') then 1 end )mr,
target_name
from tbl_disturb_customer_records
group by target_name) p2
on (p2.target_name = c.tbl)

执行计划



PLAN_TABLE_OUTPUT
Plan hash value: 4214787203 -----------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 5 | 1160 | 435 (1)| 00:00:06 |
| 1 | NESTED LOOPS OUTER | | 5 | 1160 | 435 (1)| 00:00:06 |
|* 2 | HASH JOIN | | 5 | 1020 | 25 (12)| 00:00:01 |
|* 3 | HASH JOIN OUTER | | 5 | 800 | 21 (10)| 00:00:01 |
|* 4 | HASH JOIN | | 5 | 450 | 11 (10)| 00:00:01 |
|* 5 | TABLE ACCESS BY INDEX ROWID | TBL_SP_CAMPAIGN | 5 | 225 | 7 (0)| 00:00:01 |
|* 6 | INDEX RANGE SCAN | IDX_P_CREATE_TIME | 6 | | 2 (0)| 00:00:01 |
| 7 | TABLE ACCESS FULL | TBL_SP_TASK | 112 | 5040 | 3 (0)| 00:00:01 |
| 8 | VIEW | | 7 | 490 | 10 (10)| 00:00:01 |
| 9 | HASH GROUP BY | | 7 | 518 | 10 (10)| 00:00:01 |
| 10 | TABLE ACCESS BY INDEX ROWID| TBL_SP_SALES_RECORDS | 7 | 518 | 9 (0)| 00:00:01 |
|* 11 | INDEX RANGE SCAN | IDX_SSR_STAREA | 7 | | 4 (0)| 00:00:01 |
| 12 | TABLE ACCESS FULL | TBL_TASK_TARGETDATA | 112 | 4928 | 3 (0)| 00:00:01 |
| 13 | VIEW PUSHED PREDICATE | | 1 | 28 | 82 (0)| 00:00:01 |
| 14 | SORT GROUP BY | | 1 | 26 | 82 (0)| 00:00:01 |
| 15 | TABLE ACCESS BY INDEX ROWID | TBL_DISTURB_CUSTOMER_RECORDS | 4342 | 110K| 82 (0)| 00:00:01 |
|* 16 | INDEX RANGE SCAN | IDX_TARGET_NAME | 2993 | | 20 (0)| 00:00:01 |
----------------------------------------------------------------------------------------------------------------- Predicate Information (identified by operation id):
--------------------------------------------------- 2 - access("T"."TASK_ID"="TT"."TASK_ID")
3 - access("P"."CHANNEL_ID"(+)="T"."CHANNEL_ID" AND "P"."TASK_ID"(+)="T"."TASK_ID")
4 - access("C"."CAMPAIGN_ID"="T"."CAMPAIGN_ID")
5 - filter("C"."CREATOR"='fuzhou' OR "C"."CREATOR"='lingde' OR "C"."CREATOR"='longyan' OR
"C"."CREATOR"='nanping' OR "C"."CREATOR"='putian' OR "C"."CREATOR"='quanzhou' OR "C"."CREATOR"='sanming'
OR "C"."CREATOR"='xiamen' OR "C"."CREATOR"='zhangzhou')
6 - access("C"."CREATE_TIME">='2017-10-01 00:00:00' AND "C"."CREATE_TIME"<='2017-10-27 00:00:00')
11 - access("SR"."SALES_TIME">='2017-10-01 00:00:00' AND "SR"."SALES_TIME"<='2017-10-27 00:00:00')
filter(SUBSTR("SALES_TIME",1,10)>='2017-10-01' AND SUBSTR("SALES_TIME",1,10)<='2017-10-27')
16 - access("TARGET_NAME"="TT"."TARGET_DATA")

如果大家有兴趣,可以拿着以下sql代码进行测试。

改写前的:

select d.department_id,
d.department_name,
d.location_id,
NVL((select SUM(e.salary)
from employees e
where e.department_id = d.department_id
and e.job_id = 'IT_PROG'),
0) IT_SAL,
NVL((select SUM(e.salary)
from employees e
where e.department_id = d.department_id
and e.job_id = 'AD_VP'),
0) VP_SAL,
NVL((select SUM(e.salary)
from employees e
where e.department_id = d.department_id
and e.job_id = 'FI_ACCOUNT'),
0) FI_SAL,
NVL((select SUM(e.salary)
from employees e
where e.department_id = d.department_id
and e.job_id = 'PU_CLERK'),
0) PU_SAL
from departments d

改写后的:

select d.department_id,
d.department_name,
d.location_id,
nvl(c.it_sal1,0) it_sal ,
nvl(c.vp_sal1,0) vp_sal ,
nvl(c.fi_sal1,0) fi_sal ,
nvl(c.pu_sal1,0) pu_sal
from departments d
left join (select sum(case when e.job_id='IT_PROG' then e.salary end) it_sal1 ,
sum(case when e.job_id='AD_VP' then e.salary end) vp_sal1 ,
sum(case when e.job_id='FI_ACCOUNT' then e.salary end) fi_sal1 ,
sum(case when e.job_id='PU_CLERK' then e.salary end) pu_sal1,
e.department_id
from employees e group by e.department_id) c
on d.department_id=c.department_id ;