SELECT后面有自定义函数的优化方法
又是EBS,一个SQL有700多行,要跑2个多小时,EBS开发请求帮忙优化
因为SQL太长,将其做了简化处理,简化后的SQL文本如下:
INSERT INTO nso_allocaton_fr_tmp(create_date,request_id,c1,c2,c3,c4,c5,c6,c7,c8,c9,c10,c11,c12,c13,c14,c15,c16,c17,c18,c19,c20,c21,c22,c23,c24,c25,c26,c27,c28,c29,c30,c31,c32,c33,c34,c35,c36,c37,c38,c39,c40,c41,c42,c43,c44,c45,c46,c47,c48,c49,c50,c51,c52,c53,c54,c55,c56,c57,c58,c59,c60,c61,c62,c63,c64,c65,c66,c67,c68,c70)(SELECT SYSDATE,fnd_global.conc_request_id,t.ou_name,t.ou_name,t.department_name,t.sales_person,t.project_number,t.item_category,(SELECT ffv.descriptionFROM fnd_flex_value_sets ffvs, fnd_flex_values_vl ffvWHERE ffvs.flex_value_set_id = ffv.flex_value_set_idAND ffvs.flex_value_set_name = 'XY_COA_PD'AND ffv.flex_value = t.item_categoryAND ffv.enabled_flag = 'Y'AND nvl(ffv.start_date_active, trunc(SYSDATE)) <=trunc(SYSDATE)AND nvl(ffv.end_date_active, trunc(SYSDATE)) >= trunc(SYSDATE)AND rownum = 1) item_category_desc,t.item_type,t.trade_mode,t.po_type,t.so_area,t.so_type,DECODE('NET','TOTAL',DECODE(SUBSTR(t.segment3, 1, 8),'60010118',NULL,t.so_quantity),t.so_quantity),DECODE('NET','TOTAL',DECODE(SUBSTR(t.segment3, 1, 8),'60010118',NULL,t.internal_so_quantity),t.internal_so_quantity),DECODE('NET','TOTAL',DECODE(SUBSTR(t.segment3, 1, 8),'60010118',NULL,t.actual_so_quantity),t.actual_so_quantity),DECODE('NET','TOTAL',DECODE(SUBSTR(t.segment3, 1, 8),'60010118',NULL,t.so_revenue),t.so_revenue),DECODE('NET','TOTAL',DECODE(SUBSTR(t.segment3, 1, 8),'60010118',NULL,t.internal_so_revenue),t.internal_so_revenue),DECODE('NET','TOTAL',DECODE(SUBSTR(t.segment3, 1, 8),'60010118',NULL,t.actual_so_revenue),t.actual_so_revenue),DECODE('NET','TOTAL',DECODE(SUBSTR(t.segment3, 1, 8),'64010102',NULL,t.so_cost),t.so_cost),DECODE('NET','TOTAL',DECODE(SUBSTR(t.segment3, 1, 8),'64010102',NULL,t.internal_so_cost),t.internal_so_cost),t.so_cost_diff,(decode(t.actual_so_revenue,0,NULL,NULL,NULL,round(100 *(nvl(t.so_cost_diff, 0) - nvl(t.expense_amount, 0)) /t.actual_so_revenue,2))),t.expense_amount,t.margin_amount,(decode(t.actual_so_revenue,0,NULL,NULL,NULL,round(100 *(nvl(t.so_cost_diff, 0) - nvl(t.expense_amount, 0)) /t.actual_so_revenue,2))),abs(decode(t.actual_so_revenue,0,NULL,NULL,NULL,round(100 * t.expense_amount / t.actual_so_revenue, 2))),t.so_number,t.so_line_num,t.ar_trx_number,t.ar_trx_line_num,t.gl_je_name,t.gl_je_line_num,t.lot_number,t.inv_organization_name,t.inv_item_number,t.inv_item_description,t.plan_po_number,t.plan_po_line_number,t.po_number,t.po_line_number,t.ap_invoice_number,t.ap_invoice_line_num,t.ra_trx_type_name,t.so_party_name,t.po_vendor_name,t.so_name,t.po_name,to_char(t.trans_date, 'YYYY-MM-DD HH24:MI:SS'),t.po_type1,t.comments,t.sales_type,t.primary_uom_code,t.project_number_desc,t.sales_percentage_rate,t.summary,t.so_country_name,t.rec_method,t.blanket_number,t.source_type,nvl(t.so_cost, 0) - nvl(t.purchase_cost, 0) -nvl(t.sales_cost, 0),t.purchase_cost,t.sales_cost,t.innerordercode,t.parent_project_number,nar_ar14_analysis_extract_pkg.get_out_contract_number(t.plan_po_number,t.project_number,t.org_id),DECODE(SUBSTR(t.segment3, 1, 8),'60010118',DECODE((SELECT COUNT(*)FROM gl_je_headers gjh,gl_je_sources_tl gsWHERE gjh.je_header_id = t.je_header_idAND gjh.je_source = gs.je_source_nameAND gs.language = 'ZHS'AND gs.user_je_source_name IN('人工', '自动复制')),0,NULL,(SELECT TO_NUMBER(gjl.attribute5) *DECODE(nvl(nvl(gjl.accounted_dr,gjl.entered_dr),0),0,-1,1)FROM gl_je_lines gjlWHERE gjl.je_header_id = t.je_header_idAND gjl.je_line_num = t.gl_je_line_num)),NULL),DECODE(SUBSTR(t.segment3, 1, 8),'60010118',DECODE((SELECT COUNT(*)FROM gl_je_headers gjh,gl_je_sources_tl gsWHERE gjh.je_header_id = t.je_header_idAND gjh.je_source = gs.je_source_nameAND gs.language = 'ZHS'AND gs.user_je_source_name IN('人工', '自动复制')),0,NULL,(SELECT nvl(gjl.accounted_cr, 0) -nvl(gjl.accounted_dr, 0)FROM gl_je_lines gjlWHERE gjl.je_header_id = t.je_header_idAND gjl.je_line_num = t.gl_je_line_num)),NULL),DECODE(SUBSTR(t.segment3, 1, 8),'64010102',DECODE((SELECT COUNT(*)FROM gl_je_headers gjh,gl_je_sources_tl gsWHERE gjh.je_header_id = t.je_header_idAND gjh.je_source = gs.je_source_nameAND gs.language = 'ZHS'AND gs.user_je_source_name IN('人工', '自动复制')),0,NULL,(SELECT nvl(gjl.accounted_dr, 0) -nvl(gjl.accounted_cr, 0)FROM gl_je_lines gjlWHERE gjl.je_header_id = t.je_header_idAND gjl.je_line_num = t.gl_je_line_num)),NULL), 'RST' FROM x t ---x原本是一个超大的SELECT语句,它没有性能瓶颈,为了简化SQL,创建临时表x代替);
x一共有4768314行数据,没有过滤条件
SQL> select count(*) from x;
COUNT(*)
----------
4768314
SQL里面有一堆标量子查询和自定义函数,肯定是标量子查询或者自定义函数影响了SQL的性能
可以把SQL拆了,注释掉自定义函数或者注释掉标量子查询,再跑SQL看谁影响了SQL性能
最终定位到是自定义函数拖慢了整个SQL
nar_ar14_analysis_extract_pkg.get_out_contract_number(t.plan_po_number,
t.project_number,
t.org_id)
自定义函数的代码如下:
FUNCTION get_out_contract_number(p_plan_po_number IN VARCHAR2,p_project_number IN VARCHAR2,p_org_id IN NUMBER) RETURN VARCHAR2 ISl_return VARCHAR2(240);BEGINIF p_plan_po_number IS NOT NULL THENSELECT pha.attribute1INTO l_returnFROM po_headers_all phaWHERE pha.type_lookup_code = 'PLANNED'AND pha.attribute_category = 'CONTRACT'AND pha.segment1 = p_plan_po_numberAND pha.org_id = p_org_idAND pha.attribute1 IS NOT NULLAND ROWNUM = 1;ELSIF p_project_number IS NOT NULL THENSELECT pha.attribute1INTO l_returnFROM po_distributions_all pda,po_headers_all pha,pa_projects_all ppaWHERE pha.po_header_id = pda.po_header_idAND pda.project_id = ppa.project_idAND pha.type_lookup_code = 'PLANNED'AND pha.attribute_category = 'CONTRACT'AND ppa.segment1 = p_project_numberAND rownum = 1;ELSEl_return := NULL;END IF;RETURN l_return;EXCEPTIONWHEN OTHERS THENRETURN NULL;END get_out_contract_number;
通过监控ASH,也能定位到是自定义函数拖慢了整个SQL
SQL> select sql_id, sql_child_number, event, top_level_sql_id, count(*)2 from gv$active_session_history3 where session_id = 9374 and sample_time >5 to_date('2022-12-01 14:18:00', 'yyyy-mm-dd hh24:mi:ss')6 group by sql_id, sql_child_number, event, top_level_sql_id7 order by 5 desc;
SQL_ID SQL_CHILD_NUMBER EVENT TOP_LEVEL_SQL_ID COUNT(*)
------------- ---------------- ------------------------------- ---------------- ----------
16j3dr5ws843j 5 1upgmu861mtaz 3110
1upgmu861mtaz 0 1upgmu861mtaz 55-1 1upgmu861mtaz 3
1upgmu861mtaz 0 db file sequential read 1upgmu861mtaz 20 1
264xbz7w614gx 5 1upgmu861mtaz 1SQL> select sql_text from v$sql where sql_id='16j3dr5ws843j' and rownum=1;
SQL_TEXT
--------------------------------------------------------------------------------
SELECT PHA.ATTRIBUTE1 FROM PO_DISTRIBUTIONS_ALL PDA ,PO_HEADERS_ALL PHA ,PA_PROJSQL> select sql_text from v$sql where sql_id='1upgmu861mtaz' and rownum=1;
SQL_TEXT
--------------------------------------------------------------------------------
INSERT INTO nso_allocaton_fr_tmp (create_date, request_id, c1, c2,
定位到是因为自定义函数部分拖慢了性能就好办了
优化方法一:
将自定义函数的逻辑改写为标量子查询(为了方便读者阅读,进一步简化了SQL,省略了其他列)
SQL> INSERT INTO nso_allocaton_fr_tmp2 (c65)3 (SELECT4 case5 when plan_po_number IS NOT NULL THEN6 (SELECT pha.attribute17 FROM po_headers_all pha8 WHERE pha.type_lookup_code = 'PLANNED'9 AND pha.attribute_category = 'CONTRACT'10 AND pha.segment1 = t.plan_po_number11 AND pha.org_id = t.org_id12 AND pha.attribute1 IS NOT NULL13 AND ROWNUM=114 )15 when project_number IS NOT NULL THEN16 (SELECT pha.attribute117 FROM po_distributions_all pda,18 po_headers_all pha,19 pa_projects_all ppa20 WHERE pha.po_header_id = pda.po_header_id21 AND pda.project_id = ppa.project_id22 AND pha.type_lookup_code = 'PLANNED'23 AND pha.attribute_category = 'CONTRACT'24 AND ppa.segment1 = t.project_number25 AND ROWNUM=126 )27 else null end FROM x t28 );4768314 rows inserted
Executed in 21.315 seconds
优化方法二:
不改写自定义函数逻辑,把对自定义函数的直接访问改成标量子查询+dual来访问
SQL> INSERT INTO nso_allocaton_fr_tmp2 (c65)3 (SELECT (select nar_ar14_analysis_extract_pkg.get_out_contract_number(t.plan_po_number,4 t.project_number,5 t.org_id)6 from dual)7 FROM x t);4768314 rows inserted
Executed in 25.836 seconds
优化方法一跑了21秒,优化方法二跑了25秒,优化方法二多出来的4秒是SQL引擎和PLSQL引擎上下文切换引起的
读者可能会有疑问,为什么将SQL
INSERT INTO nso_allocaton_fr_tmp
(c65)
(SELECT nar_ar14_analysis_extract_pkg.get_out_contract_number(t.plan_po_number,
t.project_number,
t.org_id)
FROM x t);
改成
INSERT INTO nso_allocaton_fr_tmp
(c65)
(SELECT (select nar_ar14_analysis_extract_pkg.get_out_contract_number(t.plan_po_number,
t.project_number,
t.org_id)
from dual)
FROM x t);
就能从2个多小时优化到几十秒呢?
那是因为没有改写之前,函数nar_ar14_analysis_extract_pkg.get_out_contract_number要被调用4768314次
而改写之后,函数nar_ar14_analysis_extract_pkg.get_out_contract_number只会被调用2485次(标量子查询特性)
SQL> select count(*)
2 from (
3 select t.plan_po_number, t.project_number, t.org_id
4 from x t
5 group by t.plan_po_number, t.project_number, t.org_id);
COUNT(*)
----------
2485
如果函数传参的列是主键,那么前面两种优化方法都不对,需要将函数的SQL改写为LEFT JOIN,这里就不做展示了
各位读者,如果以后遇到了自定义函数引起SQL慢,自定义函数是直接访问(没有通过select...dual访问)
自定义函数逻辑非常复杂(改写困难),传参列distinct值少,可以采用本案例第二种优化方法试一试
SELECT后面有自定义函数的优化方法相关推荐
- html 下拉框高度,如何自定义设置select下拉框高度的优化方法
在项目开发中经常会遇到select下拉框优化问题,默认的样式简直难看到死,所以大家都会想到去设置select高度问题,当然一些组件或者插件也可以实现对其完美优化,我之前的一篇文章也写到了相关优化方法& ...
- c语言中系统函数设定像素,在imag中对每个像素应用自定义函数的快速方法
我正在寻找一种更快的方法来应用一个自定义函数到一个图像,我用来删除蓝色背景.我有一个函数可以计算出每个像素与背景中蓝色的距离.带有循环的原始代码如下所示:def dist_to_blue(pix): ...
- 04 | 函数与优化方法:模型的自我学习(上)
目录 一.损失函数.代价函数和目标函数 损失函数与代价函数 目标函数 常用损失函数 1.0-1 损失函数 2.平方损失函数 3.均方误差损失函数 4.交叉熵损失函数 5.Softmax 损失函数 二. ...
- 自定义函数的使用方法
自定义函数 1.举一个简单的例子 return_Type fun_Name (part1,*) { statement; } return_Type 是返回值类型,它可以是C语言中的任意数据类型,例如 ...
- beego validate验证包的修改原有验证提示信息与使用自定义函数验证的方法
1.增加验证包 validate 我首先定义了一个默认的验证包,在控制器中会调用验证包中的方法进行验证表单提交时候传入的字段 2.在控制器中书写验证方法 // 发表文章的内容提交 // @route ...
- matlab自定义函数的使用方法,Matlab自定义函数的几种方法
1. 函数文件:定义.m文件 function y=myFun(x) y=x^2;以上是myFun.m文件,Matlab命令行使用: clear; clc; y=myFun(2); 2.inline方 ...
- C语言自定义函数——实现优化冒泡排序
首先我们引入冒泡排序思想: 冒泡排序(Bubble Sort),是一种计算机科学领域的较简单的排序算法. 它重复地走访过要排序的元素列,依次比较两个相邻的元素,如果顺序(如从大到小.首字母从Z到A)错 ...
- oracle自定义函数获取省份,oracle 自定义函数 方法 基本例子
核心提示:函数用于返回特定数据.执行时得找一个变量接收函数的返回值; 语法如下: create or replace function function_name ( argu1 [mode1] da ...
- MySQL1为男_MySQL自定义函数(0显示为女1显示为男
MySQL自定义函数用法详解 自定义函数 (user-defined function UDF)就是用一个象ABS() 或 CONCAT()这样的固有(内建)函数一样作用的新函数去扩展MySQL. 所 ...
最新文章
- 四位先行进位电路逻辑表达式_计算机硬件基础:二进制半加器、全加器与加法电路...
- vue 引入bootstarp --webpack
- Android数据库高手秘籍
- 队列读取器代理 遇到错误 Row handle is invalid
- PCB设计检查表( 布局后检查一次 ; 布线完再检查一次 )
- 5年,我从文员一路晋升到总监,薪资翻了5倍[转]
- 插图 引用 同一行两个插图_将图标变成插图的五个简单步骤
- Java代码实现时钟
- 1023_MISRA C规范学习_Rule 2.2
- Python基础数据类型:字符串,列表,元组,集合,字典用法总结
- 论Python常见的内置模块
- 区块链名词解析:ICO、IFO、IEO和IMO,分别是什么呢?
- ecap捕捉epwm波形的占空比及频率(总结)
- Django批量修改 get_field_display foreignkey
- 代码评审的价值和规范
- 微信小程序weui-form表单的使用
- apple pencil有买的必要吗?便宜的平替电容笔推荐
- 用前沿区块链技术为“元宇宙”筑底
- 瑰珀翠全新伊芙琳玫瑰系列全球首发
- 【小迪安全】web安全|渗透测试|网络安全 | 学习笔记-5