merge into sql优化

今天网友说他的merge into sql跑了15分钟了还没有跑出数据,问我能不能优化一下,我让他把sql和sql的执行计划发过来

merge into F_Sal_P_Camp_Samp_Cust_Data A
using (select CUST_ID,
CUST_MNGR_ID,
ORGCODE From (
SELECT t.CUST_ID,
t.CUST_MNGR_ID,
t.ORGCODE,
ROW_NUMBER() OVER(PARTITION BY t.cust_id ORDER BY t.BELG_RELA_TYPE asc,t.open_percent DESC) AS RN
From F_Sal_P_Camp_Samp_Cust_Data a
INNER JOIN (
SELECT T.CUST_ID,T.CUST_MNGR_ID,T.BELG_RELA_TYPE,T.OPEN_PERCENT,OM.ORGSEQ,OM.ORGCODE
FROM O_F_PTY_P_CUST_BELG_INFO T
INNER JOIN OM_EMPLOYEE EMP
ON t.CUST_MNGR_ID = EMP.EMPCODE
and emp.empname not like ‘%虚拟%‘
INNER JOIN AC_OPERATOR opr
ON emp.operatorid = opr.operatorid
AND opr.status IN (‘running‘, ‘openuse‘)
INNER JOIN OM_ORGANIZATION OM
ON EMP.ORGADMINID = OM.ORGID
) t ON A.CUST_ID=T.CUST_ID
where A.activity_id = ‘20150114-1003‘ AND t.orgseq like ‘.10001.‘||‘%‘
and A.used_org = ‘BS001‘
and A.execute_man is null
) where rn = 1
) t1
on (A.CUST_ID = T1.cust_id AND A.activity_id = ‘20150114-1003‘)
when matched then
update
set A.execute_man = T1.CUST_MNGR_ID,
A.task_stat = ‘3‘,
a.Used_Org = T1.ORGCODE
WHERE A.activity_id = ‘20150114-1003‘

技术分享

 

5 - filter("RN"=1)

6 - filter(ROW_NUMBER() OVER ( PARTITION BY "T"."CUST_ID" ORDER BY
"T"."BELG_RELA_TYPE",INTERNAL_FUNCTION("T"."OPEN_PERCENT") DESC )<=1)
12 - filter("A"."USED_ORG"=‘BS001‘ AND "A"."EXECUTE_MAN" IS NULL)
13 - access("A"."ACTIVITY_ID"=‘20150114-1003‘)
15 - access("A"."CUST_ID"="T"."CUST_ID")
16 - filter("EMP"."EMPNAME" NOT LIKE ‘%虚拟%‘ AND "EMP"."EMPNAME" IS NOT NULL)
17 - access("T"."CUST_MNGR_ID"="EMP"."EMPCODE")
18 - filter("OPR"."STATUS"=‘openuse‘ OR "OPR"."STATUS"=‘running‘)
19 - access("EMP"."OPERATORID"="OPR"."OPERATORID")
20 - access("EMP"."ORGADMINID"="OM"."ORGID")
21 - filter("OM"."ORGSEQ" LIKE ‘.10001.%‘)
22 - access("A"."ACTIVITY_ID"=‘20150114-1003‘)
23 - filter("A"."CUST_ID"="CUST_ID")

看了这个执行计划,发现全都是NL连接,我让他查询相应sql返回的行数

F_Sal_P_Camp_Samp_Cust_Data 返回20w

select count(*) from O_F_PTY_P_CUST_BELG_INFO T; 369983
select count(*) from OM_EMPLOYEE EMP where emp.empname not like ‘%虚拟%; 6353
select count(*) from AC_OPERATOR opr where opr.status IN (‘running‘, ‘openuse‘); 3234
select count(*) from OM_ORGANIZATION OM; 1079

发现返回的行数都不少,走NL连接一定有问题,应该走hash join,而且执行计划上看统计信息也是不对的

由于网友是开发没有dba权限,所以不能 重新统计信息,所以只能使用hint,添加了hint强制走hash,运行时间是19秒,网友说可以了,我在帮他看看

merge /*+ use_hash(A,t1) leading(A) */ into F_Sal_P_Camp_Samp_Cust_Data A
using (select CUST_ID,
CUST_MNGR_ID,
ORGCODE From (
SELECT /*+ use_hash(A,t) leading(a) */t.CUST_ID,
t.CUST_MNGR_ID,
t.ORGCODE,
ROW_NUMBER() OVER(PARTITION BY t.cust_id ORDER BY t.BELG_RELA_TYPE asc,t.open_percent DESC) AS RN
From F_Sal_P_Camp_Samp_Cust_Data a
INNER JOIN (
SELECT T.CUST_ID,T.CUST_MNGR_ID,T.BELG_RELA_TYPE,T.OPEN_PERCENT,OM.ORGSEQ,OM.ORGCODE
FROM O_F_PTY_P_CUST_BELG_INFO T
INNER JOIN OM_EMPLOYEE EMP
ON t.CUST_MNGR_ID = EMP.EMPCODE
and emp.empname not like ‘%虚拟%‘
INNER JOIN AC_OPERATOR opr
ON emp.operatorid = opr.operatorid
AND opr.status IN (‘running‘, ‘openuse‘)
INNER JOIN OM_ORGANIZATION OM
ON EMP.ORGADMINID = OM.ORGID
) t ON A.CUST_ID=T.CUST_ID
where A.activity_id = ‘20150114-1003‘ AND t.orgseq like ‘.10001.‘||‘%‘
and A.used_org = ‘BS001‘
and A.execute_man is null
) where rn = 1
) t1
on (A.CUST_ID = T1.cust_id AND A.activity_id = ‘20150114-1003‘)
when matched then
update
set A.execute_man = T1.CUST_MNGR_ID,
A.task_stat = ‘3‘,
a.Used_Org = T1.ORGCODE
WHERE A.activity_id = ‘20150114-1003‘

这个sql中间嵌套了很多层,这是中间那层,这个sql跑了2:30秒

SELECT T.CUST_ID,T.CUST_MNGR_ID,T.BELG_RELA_TYPE,T.OPEN_PERCENT,OM.ORGSEQ,OM.ORGCODE
FROM O_F_PTY_P_CUST_BELG_INFO T
INNER JOIN OM_EMPLOYEE EMP
ON t.CUST_MNGR_ID = EMP.EMPCODE
and emp.empname not like ‘%虚拟%‘
INNER JOIN AC_OPERATOR opr
ON emp.operatorid = opr.operatorid
AND opr.status IN (‘running‘, ‘openuse‘)
INNER JOIN OM_ORGANIZATION OM
ON EMP.ORGADMINID = OM.ORGID

技术分享

select count(*) from O_F_PTY_P_CUST_BELG_INFO T; 369983
select count(*) from OM_EMPLOYEE EMP where emp.empname not like ‘%虚拟%; 6353
select count(*) from AC_OPERATOR opr where opr.status IN (‘running‘, ‘openuse‘); 3234
select count(*) from OM_ORGANIZATION OM; 1079

看了这个执行计划,看了sql返回的行数,发现非常好,走的hash连接是对的,那为什么走的慢呢,因为所有的表走的都是全表扫描,没有走索引,但是能走索引的都是小表,没有影响,

真正有影响的是T表,T表30多w,而且没有筛选条件,只能改写sql了

 

with X as ( select /*+ materialize parallel(T,6) */ T.CUST_ID,T.CUST_MNGR_ID,T.BELG_RELA_TYPE,T.OPEN_PERCENT from O_F_PTY_P_CUST_BELG_INFO T)
SELECT X.CUST_ID,X.CUST_MNGR_ID,X.BELG_RELA_TYPE,X.OPEN_PERCENT,OM.ORGSEQ,OM.ORGCODE
FROM X
INNER JOIN OM_EMPLOYEE EMP
ON X.CUST_MNGR_ID = EMP.EMPCODE
and emp.empname not like ‘%虚拟%‘
INNER JOIN AC_OPERATOR opr
ON emp.operatorid = opr.operatorid
AND opr.status IN (‘running‘, ‘openuse‘)
INNER JOIN OM_ORGANIZATION OM
ON EMP.ORGADMINID = OM.ORGID

技术分享

 

sql改写之后跑了1分钟,快了好多

整个sql跑起来,在10秒左右,改到这里就可以了

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。