优化案例1-尽量避免使用自定义函数进行大量运算

案例说明
在月底进行代码优化检查过程中。在SQL检查过程之执行次数最多的SQL。发现SQL_ID为grk7dk5amf5m7和gzzzkzbfg8j2m 在半个小时内产生大约分别15亿次执行。逻辑读也有15G
其实SQL本身很简单;是一个自定义的分割函数。

原SQL

  select to_char(a.logintime, ‘yyyymmdd‘),
           to_char(a.logintime, ‘HH24‘),
           2552,
           substr(a.qn, 5, 4) ad,
           regexp_substr(a.qn, ‘[^_]+‘, 1, 2) channelid,
           a.account,
           sysdate
      from ssdk_acc_login a
     where to_char(a.logintime, ‘yyyymmdd‘) = ‘20170728‘
       and splitstr(a.qn, 2, ‘_‘) in
           (select cid from tbl_channel where channel_tag like ‘广点通%‘);

该SQL的执行计划

Execution Plan
----------------------------------------------------------
Plan hash value: 2543353618

-----------------------------------------------------------------------------------------------------
| Id  | Operation                      | Name               | Rows  | Bytes | Cost (%CPU)| Time     |
-----------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT               |                    |  7236 |   558K|  2445   (1)| 00:00:30 |
|   1 |  VIEW                          | VM_NWVW_2          |  7236 |   558K|  2445   (1)| 00:00:30 |
|   2 |   HASH UNIQUE                  |                    |  7236 |   607K|  2445   (1)| 00:00:30 |
|*  3 |    HASH JOIN                   |                    |  7236 |   607K|  2444   (1)| 00:00:30 |
|*  4 |     TABLE ACCESS FULL          | TBL_CHANNEL        |   154 |  2772 |     5   (0)| 00:00:01 |
|   5 |     TABLE ACCESS BY INDEX ROWID| SSDK_ACC_LOGIN     | 95232 |  6324K|  2438   (1)| 00:00:30 |
|*  6 |      INDEX RANGE SCAN          | IND_SDK_LOGIN_TIME | 95232 |       |   670   (1)| 00:00:09 |
-----------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   3 - access("CID"="SPLITSTR"(INTERNAL_FUNCTION("A"."QN"),2,‘_‘))
   4 - filter("CHANNEL_TAG" LIKE ‘广点通%‘)
   6 - access(TO_CHAR(INTERNAL_FUNCTION("LOGINTIME"),‘yyyymmdd‘)=‘20170728‘)

Statistics
----------------------------------------------------------
     152770  recursive calls
    1833240  db block gets
     307000  consistent gets
          0  physical reads
          0  redo size
    1465885  bytes sent via SQL*Net to client
      20225  bytes received via SQL*Net from client
       1793  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
      26867  rows processed    

从执行计划上来看。存在有152770递归调用和约200M的逻辑读。但是返回的行数只有2万条。逻辑读跟返回的行数严重失调。本身表ssdk_acc_login是一个大表。
对有经验的人来说;产生大量的递归条用;很容易看出问题跟自定义函数SPLITSTR有关。

通过等价改下SQL

    select to_char(a.logintime, ‘yyyymmdd‘),
           to_char(a.logintime, ‘HH24‘),
           2552,
           substr(a.qn, 5, 4) ad,
           regexp_substr(a.qn, ‘[^_]+‘, 1, 2) channelid,
           a.account,
           sysdate
      from ssdk_acc_login a
     where to_char(a.logintime, ‘yyyymmdd‘) = ‘20170728‘
       and regexp_substr(a.qn,‘[^_]+‘,1,2) in
           (select cid from tbl_channel where channel_tag like ‘广点通%‘);

该SQL 的执行计划

Execution Plan
----------------------------------------------------------
Plan hash value: 2829062945

---------------------------------------------------------------------------------------------------
| Id  | Operation                    | Name               | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |                    |    47 |  3478 |  2443   (1)| 00:00:30 |
|   1 |  NESTED LOOPS                |                    |    47 |  3478 |  2443   (1)| 00:00:30 |
|   2 |   SORT UNIQUE                |                    |   154 |  2772 |     5   (0)| 00:00:01 |
|*  3 |    TABLE ACCESS FULL         | TBL_CHANNEL        |   154 |  2772 |     5   (0)| 00:00:01 |
|*  4 |   TABLE ACCESS BY INDEX ROWID| SSDK_ACC_LOGIN     |    47 |  2632 |  2437   (1)| 00:00:30 |
|*  5 |    INDEX RANGE SCAN          | IND_SDK_LOGIN_TIME | 95232 |       |   669   (1)| 00:00:09 |
---------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   3 - filter("CHANNEL_TAG" LIKE ‘广点通%‘)
   4 - filter("CID"= REGEXP_SUBSTR ("A"."QN",‘[^_]+‘,1,2))
   5 - access(TO_CHAR(INTERNAL_FUNCTION("LOGINTIME"),‘yyyymmdd‘)=‘20170728‘)

Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
     214489  consistent gets
          0  physical reads
        556  redo size
    1345632  bytes sent via SQL*Net to client
      20324  bytes received via SQL*Net from client
       1802  SQL*Net roundtrips to/from client
          1  sorts (memory)
          0  sorts (disk)
      27004  rows processed     

执行计划
与之前的SQL的执行计划相比;代价相差无几。连接方式由hash连接改为NESTED LOOPS
统计信息:
递归调用:前面是152770次。现在是0次。
逻辑读: 前面是约200M。现在是约20M。相差10倍。
redo日志。前面不产生。现在产生556kb。影响不大。
排序: 前面不产生;现在产生排序。此服务器专用于oracle。内存48G。影响不大。

综上所述:用正则函数regexp_substr来替换 自定义函数splitstr更好。尽量避免使用自定义函数进行大量运算

时间: 2024-08-03 08:39:43

优化案例1-尽量避免使用自定义函数进行大量运算的相关文章

Microsoft SQL Server 自定义函数整理大全

01.去除字符串中的html标记及标记中的内容 [叶子函数分享一]去除字符串中的html标记及标记中的内容 --1.创建函数 create function [dbo].[clearhtml] (@maco varchar(8000)) returns varchar(8000) as begin     declare @i int     while 1 = 1     begin        set @i=len(@maco)        set @maco=replace(@maco

对自定义函数使用不当的调优案例

一.问题. 下面这条SQL的运行时间较长,不满足业务需求. select rs.managecom, (select codename from ldcode where codetype = 'station' and code = rs.managecom), rs.insurername, rs.contplancode, case when sum(prem) - sum(prem2) + sum(prem3) = 0 then '' else case when substr(roun

python第十四课--排序及自定义函数之案例二:冒泡排序

案例二:冒泡排序 lt1=[45,12,56,-32,-3,44,75,-22,100] print('排序前:'+str(lt1)) 自定义函数:实现冒泡排序(升序)原则:1).有没有形参?有,接受一个列表对象 2).有没有返回值?没有,排完就排完 def bubbleSort(lt): length=len(lt) for i in range(length-1): for j in range(length-1-i): if lt[j]>lt[j+1]: lt[j],lt[j+1]=lt[

mysql优化案例

MySQL优化案例 Mysql5.1大表分区效率测试 Mysql5.1大表分区效率测试MySQL | add at 2009-03-27 12:29:31 by PConline | view:60, comment:0 mysql5.1开始支持数据表分区了,原来的分表可以不用了,分表的不足在于多表查询不方便.呵呵,下面来简单测试下表分区的查询效率. 1.用来测试的数据为discuz论坛的数据库,表为cdb_posts表,数据量为1500多万条mysql> select count(*) fro

数据库优化案例——————某知名零售企业ERP系统

写在前面 记得在自己学习数据库知识的时候特别喜欢看案例,因为优化的手段是容易掌握的,但是整体的优化思想是很难学会的.这也是为什么自己特别喜欢看案例,今天也分享自己做的优化案例. 之前分享过OA系统.HIS系统,今天我们来一个最常见的ERP,ERP系统各行各业都在用,不同行业也有不同的特点,博主在做研发的时候还自己写过ERP也算是比较熟悉了. 不管是本文分享的零售类,还是鞋服门店.家居.汽车.地产等等,也不管是某友.某碟,ERP有一个共同的特点,单据流程长,业务复杂,热点表明显,数据量大,涉及众多

java mysql自定义函数UDF之调用c函数

正如sqlite可以定义自定义函数,它是通过API定义c函数的,不像其他,如这里的mysql.sqlite提供原生接口就可以方便的调用其他语言的方法,同样的mysql也支持调用其它语言的方法. google "mysql call c function"发现一片文章 MySQL User Defined Functions  This tutorial explains what an User Defined Function (UDF) is, what it does and w

python学习之--自定义函数:

Python之--自定义函数: 在Python中,定义一个函数要使用def语句,依次写出函数名.括号.括号中的参数和冒号:,然后,在缩进块中编写函数体,函数的返回值用return语句返回. 以下自定义一个函数用于判断一个人是成年人好事青少年: 1 >>> def judge_person(age): 2 ... if age < 18: 3 ... print("teenager!") 4 ... else: 5 ... print("adult!&q

JavaWeb学习之JSTL自定义标签库的使用、JSTL自定义函数库(7)

一.自定义标签,步骤 * 确定需求 * <my:date /> 输出当前系统的时间 yyyy-MM-dd hh:mm:ss:SSS * 编写Java类 新建包名:com.yxl.tag,新建类MyDateTag,实现SimpleTag接口 * 只要与此接口有关的实现类,javax.servlet.jsp.tagext.JspTag * 需要实现具体的接口 * javax.servlet.jsp.tagext.SimpleTag,简单标签,JSP2.0**,选择这个 * javax.servle

Microsoft SQL Server 自定义函数整理大全(下)

34.字符串转成16进制函数 /**************************** 字符串转成16进制 作者:不得闲 QQ: 75492895 Email: [email protected] ****************************/ --创建函数(suiyunonghen(不得闲)) Create Function VarCharToHex(@Str Varchar(400)) returns varchar(800) as begin declare @i int,@