如何通过自连接和分组来调整 SQL 查询
我在游标语句中编写了以下查询,以通过批处理过程更新也具有插入过程的多个表。
但这个过程批量需要超过 30 分钟才能完成,
每次可能需要处理大约 400k 条记录。查询中连接的表在名为 G_FOR_TB 的表中有 600 万条记录,其他表有大约 100 万条记录。如何调整以下查询?
SELECT rs_g_read_flg ,
RS_SYS_ID ,
RESP.RSH_SYS_ID ,
INTM.ENTRY_KEY ,
PY.POLICY_KEY ,
CLM.CLAIM_KEY ,
RSH_SYS_ID ,
RSH_INTM_NO AS INTIM_NUM ,
RSH_POLICY_NO AS POLICY_NUM ,
RSH_CLAIM_NO AS CLAIM_NUM ,
RESP.RSH_FOR_NO AS FOR_ID ,
RSH_REPRESEN_CD AS REPRESEN_CDE ,
RSH_REPRESEN_NM AS REPRESENT_NM ,
CASE
WHEN UPPER(RSH_STS) LIKE '%No Required%' AND NVL(UPPER(RSH_GLX_STS) ,'NA')<>'BOOK LEVEL CLOSE' THEN COALESCE(RSH_FOR_NOT_REQ_REASON ,RSH_SFOREX_COMNT)
WHEN UPPER(RSH_STS) LIKE '%No Required%' AND NVL(UPPER(RSH_GLX_STS) ,'NA')='BOOK LEVEL CLOSE' THEN RSH_SFOR_CLOSE_REMARKS
ELSE RSH_SFOREX_COMNT
END AS EX_COMMENTS ,
EMP.EMPLOYEE_KEY AS Booked_BY ,
EMP.EMP_FIRST_NAME AS Booked_BY_NAME ,
RSH_Booked_DT AS Booked_DATE ,
NULL AS BOOK_VISITED_DATE ,
CASE
WHEN RSH_DOC_REC_YN='Y' THEN 1
WHEN RSH_DOC_REC_YN='N' THEN 0
ELSE NULL
END DOC_REC_FLAG ,
RSH_DOC_REC_DT AS REPORT_RECEIVED_DATE ,
RSH_REBOOKED_YN AS REBOOKED_FLAG ,
RSH_FOR_NOT_REQ_REASON AS REASON_NOT_REQUIRED ,
RSH_REPRESENT_MOB AS REPRESENTATIVE_MOBILE_NO ,
RSH_REPRESENT_TEL AS REPRESENTATIVE_TEL_NO ,
CASE
WHEN RSH_STS ='Booked' THEN 51
WHEN RSH_STS ='SFOR Auto Skipped' THEN 52
WHEN RSH_STS ='DELIVERED' THEN 5
WHEN RSH_STS = 'No Required' AND NVL(RSH_GLX_STS ,
'No Required by Executive at ENTRY Level') =
'No Required by Executive at ENTRY Level' THEN 217
WHEN RSH_STS = 'No Required' AND RSH_GLX_STS =
'BOOK LEVEL CLOSE' THEN 218
WHEN RSH_STS ='Rebooked' THEN 51
ELSE NULL
END AS STATUS_ID ,
CASE
WHEN RSH_STS ='Booked' THEN 'ISFOR'
WHEN SFOR.STATUS_ID = 51 AND rs_sts ='No Required' THEN 'WSFOR'
WHEN SFOR.STATUS_ID = 49 AND rs_sts ='No Required' THEN 'ISFOR'
WHEN RSH_STS ='DELIVERED' THEN 'WSFOR'
WHEN RSH_STS ='Rebooked Closed' THEN
'REBOOKEDCLOSEDFROMGLX'
WHEN RSH_STS ='Rebooked' THEN 'ISFOR'
ELSE NULL
END AS PRESENT_LIST ,
CASE
WHEN RSH_STS ='Booked' THEN 'ISFORWSFOR'
WHEN RSH_STS ='No Required' THEN 'ISFORSMBT'
WHEN RSH_STS ='DELIVERED' THEN 'ISFORWSFOR'
WHEN RSH_STS ='Rebooked Closed' THEN 'REBOOKEDCLOSEDFROMGLX'
WHEN RSH_STS ='Rebooked' THEN 'ISFORWSFOR'
ELSE NULL
END AS OUTCOME ,
RSH_CALL_OPTION AS CALL_OPTION ,
RSH_CAL_REMARK AS CALL_REMARKS ,
RSH_CAL_MADE AS CALL_MADE ,
CASE
WHEN RSH_ID_REP='Level 1' THEN 1901
WHEN RSH_ID_REP='Level 2' THEN 1902
WHEN RSH_ID_REP='Level 3' THEN 1903
END AS ALLOTED_ID ,
RSH_CR_DT AS CRT_DATE ,
RSH_GLX_YN ,
RSH_SFOR_UPDATE_YN ,
RSH_G_UPD_YN ,
RSH_G_UPD_DATE ,
FOR_CNT.CNT AS SFOR_COUNT ,
UPPER(RESP.RSH_CODE) AS RSH_CODE ,
SFOR.SFOR_KEY ,
SFOR.status_iD AS SFOR_STATUS_ID
FROM RESPONSE_HIST RESP
LEFT JOIN RESPONSE RES
ON RES.RS_SYS_ID = RESP.RSH_SYS_ID
LEFT JOIN POLICY_TB PY
ON PY.POLICY_NUM=RESP.RSH_POLICY_NO
LEFT JOIN INTIM_TB INTM
ON RESP.RSH_INTM_NO=INTM.INTIM_NUM
LEFT JOIN CLAIM_tb CLM
ON CLM.CLAIM_NUM=RESP.RSH_CLAIM_NO
LEFT JOIN EMPLOY_MAS EMP
ON UPPER(EMP.EMP_ID)=UPPER(RESP.RSH_CODE)
LEFT JOIN
(SELECT RS_FOR_NO ,
COUNT(*)CNT
FROM RESPONSE R
WHERE (( NVL(RS_G_YN ,'N') ='N'
AND R.RS_FOR_NO IS NOT NULL )
AND ( rs_sts IN ('Rebooked' ,'Rebooked Closed')
OR rs_sts IN ('Rebooked' ,'No Required stopped')))
OR (NVL(RS_G_YN ,'N') ='Y'
AND NVL(RS_FOR_UPDATE_YN ,'N')='Y'
AND NVL(RS_G_UPD_YN ,'N') ='N'
AND R.RS_FOR_NO IS NOT NULL
AND ( rs_sts IN ('Rebooked' ,'Rebooked Closed')
OR rs_sts IN ('Rebooked' ,'No Required stopped')) )
GROUP BY RS_FOR_NO
) FOR_CNT
ON RESP.RSH_FOR_NO=FOR_CNT.RS_FOR_NO
LEFT JOIN
(SELECT RS_FOR_NO ,
COUNT(*)CNT_NR
FROM RESPONSE R
WHERE (R.RS_FOR_NO IS NOT NULL )
AND ( rs_sts IN ('No Required' ,'Rebooked Closed')
)
GROUP BY R.RS_FOR_NO
) FOR_CNT_NR
ON RESP.RSH_FOR_NO=FOR_CNT_NR.RS_FOR_NO
LEFT JOIN G_FOR_TB SFOR
ON RESP.RSH_FOR_NO=SFOR.FOR_ID
WHERE
( NVL(rs_g_read_flg ,'N')='N'
AND UPPER(RSH_STS) <> 'DELIVERED'
AND (SFOR.STATUS_ID <> 5
OR SFOR.STATUS_ID IS NULL) )
ORDER BY RESP.RSH_SYS_ID ,
RESP.RSH_FOR_NO ;
注意:我使用的是oracle 12c。 实现了连接列的索引。
I have written below query in my cursor statement to update multiple table also having insert process too through procedure as batch.
But this procedure batch taken more than 30 minutes to complete
Each time it may take to process around 400k records. Tables joined in the query have 6 million records in table called G_FOR_TB and other tables have around 1 million records. How to tune the below query?
SELECT rs_g_read_flg ,
RS_SYS_ID ,
RESP.RSH_SYS_ID ,
INTM.ENTRY_KEY ,
PY.POLICY_KEY ,
CLM.CLAIM_KEY ,
RSH_SYS_ID ,
RSH_INTM_NO AS INTIM_NUM ,
RSH_POLICY_NO AS POLICY_NUM ,
RSH_CLAIM_NO AS CLAIM_NUM ,
RESP.RSH_FOR_NO AS FOR_ID ,
RSH_REPRESEN_CD AS REPRESEN_CDE ,
RSH_REPRESEN_NM AS REPRESENT_NM ,
CASE
WHEN UPPER(RSH_STS) LIKE '%No Required%' AND NVL(UPPER(RSH_GLX_STS) ,'NA')<>'BOOK LEVEL CLOSE' THEN COALESCE(RSH_FOR_NOT_REQ_REASON ,RSH_SFOREX_COMNT)
WHEN UPPER(RSH_STS) LIKE '%No Required%' AND NVL(UPPER(RSH_GLX_STS) ,'NA')='BOOK LEVEL CLOSE' THEN RSH_SFOR_CLOSE_REMARKS
ELSE RSH_SFOREX_COMNT
END AS EX_COMMENTS ,
EMP.EMPLOYEE_KEY AS Booked_BY ,
EMP.EMP_FIRST_NAME AS Booked_BY_NAME ,
RSH_Booked_DT AS Booked_DATE ,
NULL AS BOOK_VISITED_DATE ,
CASE
WHEN RSH_DOC_REC_YN='Y' THEN 1
WHEN RSH_DOC_REC_YN='N' THEN 0
ELSE NULL
END DOC_REC_FLAG ,
RSH_DOC_REC_DT AS REPORT_RECEIVED_DATE ,
RSH_REBOOKED_YN AS REBOOKED_FLAG ,
RSH_FOR_NOT_REQ_REASON AS REASON_NOT_REQUIRED ,
RSH_REPRESENT_MOB AS REPRESENTATIVE_MOBILE_NO ,
RSH_REPRESENT_TEL AS REPRESENTATIVE_TEL_NO ,
CASE
WHEN RSH_STS ='Booked' THEN 51
WHEN RSH_STS ='SFOR Auto Skipped' THEN 52
WHEN RSH_STS ='DELIVERED' THEN 5
WHEN RSH_STS = 'No Required' AND NVL(RSH_GLX_STS ,
'No Required by Executive at ENTRY Level') =
'No Required by Executive at ENTRY Level' THEN 217
WHEN RSH_STS = 'No Required' AND RSH_GLX_STS =
'BOOK LEVEL CLOSE' THEN 218
WHEN RSH_STS ='Rebooked' THEN 51
ELSE NULL
END AS STATUS_ID ,
CASE
WHEN RSH_STS ='Booked' THEN 'ISFOR'
WHEN SFOR.STATUS_ID = 51 AND rs_sts ='No Required' THEN 'WSFOR'
WHEN SFOR.STATUS_ID = 49 AND rs_sts ='No Required' THEN 'ISFOR'
WHEN RSH_STS ='DELIVERED' THEN 'WSFOR'
WHEN RSH_STS ='Rebooked Closed' THEN
'REBOOKEDCLOSEDFROMGLX'
WHEN RSH_STS ='Rebooked' THEN 'ISFOR'
ELSE NULL
END AS PRESENT_LIST ,
CASE
WHEN RSH_STS ='Booked' THEN 'ISFORWSFOR'
WHEN RSH_STS ='No Required' THEN 'ISFORSMBT'
WHEN RSH_STS ='DELIVERED' THEN 'ISFORWSFOR'
WHEN RSH_STS ='Rebooked Closed' THEN 'REBOOKEDCLOSEDFROMGLX'
WHEN RSH_STS ='Rebooked' THEN 'ISFORWSFOR'
ELSE NULL
END AS OUTCOME ,
RSH_CALL_OPTION AS CALL_OPTION ,
RSH_CAL_REMARK AS CALL_REMARKS ,
RSH_CAL_MADE AS CALL_MADE ,
CASE
WHEN RSH_ID_REP='Level 1' THEN 1901
WHEN RSH_ID_REP='Level 2' THEN 1902
WHEN RSH_ID_REP='Level 3' THEN 1903
END AS ALLOTED_ID ,
RSH_CR_DT AS CRT_DATE ,
RSH_GLX_YN ,
RSH_SFOR_UPDATE_YN ,
RSH_G_UPD_YN ,
RSH_G_UPD_DATE ,
FOR_CNT.CNT AS SFOR_COUNT ,
UPPER(RESP.RSH_CODE) AS RSH_CODE ,
SFOR.SFOR_KEY ,
SFOR.status_iD AS SFOR_STATUS_ID
FROM RESPONSE_HIST RESP
LEFT JOIN RESPONSE RES
ON RES.RS_SYS_ID = RESP.RSH_SYS_ID
LEFT JOIN POLICY_TB PY
ON PY.POLICY_NUM=RESP.RSH_POLICY_NO
LEFT JOIN INTIM_TB INTM
ON RESP.RSH_INTM_NO=INTM.INTIM_NUM
LEFT JOIN CLAIM_tb CLM
ON CLM.CLAIM_NUM=RESP.RSH_CLAIM_NO
LEFT JOIN EMPLOY_MAS EMP
ON UPPER(EMP.EMP_ID)=UPPER(RESP.RSH_CODE)
LEFT JOIN
(SELECT RS_FOR_NO ,
COUNT(*)CNT
FROM RESPONSE R
WHERE (( NVL(RS_G_YN ,'N') ='N'
AND R.RS_FOR_NO IS NOT NULL )
AND ( rs_sts IN ('Rebooked' ,'Rebooked Closed')
OR rs_sts IN ('Rebooked' ,'No Required stopped')))
OR (NVL(RS_G_YN ,'N') ='Y'
AND NVL(RS_FOR_UPDATE_YN ,'N')='Y'
AND NVL(RS_G_UPD_YN ,'N') ='N'
AND R.RS_FOR_NO IS NOT NULL
AND ( rs_sts IN ('Rebooked' ,'Rebooked Closed')
OR rs_sts IN ('Rebooked' ,'No Required stopped')) )
GROUP BY RS_FOR_NO
) FOR_CNT
ON RESP.RSH_FOR_NO=FOR_CNT.RS_FOR_NO
LEFT JOIN
(SELECT RS_FOR_NO ,
COUNT(*)CNT_NR
FROM RESPONSE R
WHERE (R.RS_FOR_NO IS NOT NULL )
AND ( rs_sts IN ('No Required' ,'Rebooked Closed')
)
GROUP BY R.RS_FOR_NO
) FOR_CNT_NR
ON RESP.RSH_FOR_NO=FOR_CNT_NR.RS_FOR_NO
LEFT JOIN G_FOR_TB SFOR
ON RESP.RSH_FOR_NO=SFOR.FOR_ID
WHERE
( NVL(rs_g_read_flg ,'N')='N'
AND UPPER(RSH_STS) <> 'DELIVERED'
AND (SFOR.STATUS_ID <> 5
OR SFOR.STATUS_ID IS NULL) )
ORDER BY RESP.RSH_SYS_ID ,
RESP.RSH_FOR_NO ;
Note: I am using oracle 12c.
Index for joining columns are implemented.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
为什么您认为问题出在该查询中?你是怎么分析的? SQL 执行时间/计划/统计信息? PL/SQL 探查器?痕迹?
无论如何,除此之外,我在这里看到了一些奇怪的事情:
UPPER(EMP.EMP_ID)=UPPER(RESP.RSH_CODE)
- 它们真的是 varchar 列吗?如果是,为什么你允许大写和小写并且不知道它们的字母大小写?你不使用外键吗?无需通过 RESPONSE 两次(
FOR_CNT
和FOR_CNT_NR
)来加入聚合。这部分可以重写为
why do you think that the problem is in that query? How did you analyze it? SQL execution time/plans/statistics? PL/SQL Profiler? Traces?
Anyway, besides this, I see a few strange things here:
UPPER(EMP.EMP_ID)=UPPER(RESP.RSH_CODE)
- are they really varchar columns? If yes, why do you allow both uppercase and lowercase and don't know their letter case? Don't you use foreign keys?There is no need to join aggregates by RESPONSE twice (
FOR_CNT
andFOR_CNT_NR
). This partcan be rewritten to