(Oracle)SQL优化案例:隐式转换优化

项目场景

项目现场的某个kettle模型执行非常缓慢,原因在于某个SQL执行效率非常的低。甲方得知此事要求公司赶紧优化,负责该模块的同事对SQL优化并不熟悉。所以作为一个立志成为优秀DBA的ETL工程师,我自告奋勇:不是DBA,就不能搞SQL优化了吗?

查询效率慢的SQL格式如下,因为涉及到项目隐私,所以对表名、字段名做了匿名化处理。

SELECT
INFO.ID,
INFO.NAME,
INFO.AGE,
INFO.SEX,
MP.ADDR,
MP.PHONE
FROM INFORMATION INFO
LEFT JOIN MP ON MP.ID = INFO.ID
WHERE INFO.ID = 1234567

很简单的一个SQL。每次查询都是按照 INFO.ID = xxx 这个条件进行查询,每次只会返回1行数据。且关联字段 MP.ID 与 INFO.ID 都有索引,没有大字段。但每次执行查询,大概都要耗时2s左右;不要觉得这个耗时很短,因为有大量的数据需要查询出来进行同步,这个速度就不可被接受了。 

两张表的数据量如下

SELECT COUNT(*) FROM INFORMATION --3667874
SELECT COUNT(*) FROM MP --2125263

问题分析

以下是上述SQL格式的执行计划,是项目上真实SQL的执行计划。我已经将对此次优化的无关执行计划信息删除,只保留了执行计划和谓词信息;且将涉及项目隐私部分的表名字段名改写

Plan hash value: 3295416379-------------------------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                    | Name                   | Starts | E-Rows |E-Bytes| Cost (%CPU)| E-Time   | A-Rows |   A-Time   | Buffers |
-------------------------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |                        |      1 |        |       | 29513 (100)|          |      1 |00:00:01.20 |     108K|
|   1 |  NESTED LOOPS OUTER          |                        |      1 |      1 |   115 | 29513   (1)| 00:00:02 |      1 |00:00:01.20 |     108K|
|   2 |   TABLE ACCESS BY INDEX ROWID| INFORMATION            |      1 |      1 |    98 |     3   (0)| 00:00:01 |      1 |00:00:00.01 |       4 |
|*  3 |    INDEX UNIQUE SCAN         | UK_20230901211918_5341 |      1 |      1 |       |     2   (0)| 00:00:01 |      1 |00:00:00.01 |       3 |
|*  4 |   TABLE ACCESS FULL          | MP                     |      1 |      1 |    17 | 29510   (1)| 00:00:02 |      1 |00:00:01.20 |     108K|
-------------------------------------------------------------------------------------------------------------------------------------------------Predicate Information (identified by operation id):
---------------------------------------------------3 - access("A"."ID"=1234567)4 - filter((TO_NUMBER("MP"."ID")=1234567 AND "A"."ID"=TO_NUMBER("MP"."ID")))

通过执行计划我们可以分析出:

  • 连接方式 (执行计划:Id=1)

MP表和INFO表的连接方式是 NESTED LOOPS OUTER;因为此查询返回数据量少,每次执行只会返回1行数据,所以走嵌套循环连接是正确的。

  • 访问路径 (执行计划:Id=2)

INFO.ID=1234567 这个谓词过滤是走了索引ROWID扫描的;返回数据量少,走索引,也是正确的。

  • 访问路径(执行计划:Id=4) 

对MP表的访问路径是TABLE ACCESS FULL(全表扫描)。这个就有问题了,MP表有上百万条数据,走全表扫描肯定是低效的、错误的。MP表上的ID字段是有索引的,为什么没有走索引呢?我们继续往下看。

  • 谓词信息 

从谓词信息我们可以看到MP表在进行谓词过滤时,将MP表的ID字段从varchar类型的值通过to_number()转换成了number类型;因为使用了to_number()函数,索引索引失效。此转换属于是Oracle在比对不同数据类型的字段或者表达式时,自动发生的隐式转换;隐式转换的目的肯定是好的,但是在此处对SQL查询效率影响可太大了。

那么对于这个SQL优化而言,需要做的其实就是消除隐式转换带来的影响。那怎么消除嘞?大家继续看下面的SQL改写。

我不晓得会不会有朋友疑问,为什么发生隐式转换的是MP.ID 而不是 INFOMATION.ID。

我在这里多解释一句:

可以从执行计划中看出来,

距离NESTED LOOPS OUTER这个连接方式关键字下方最近的一张表就是驱动表。

这也就意味着,驱动表INFOMATION传一个number类型的数据给MP表,那么MP表必须对自己的字段进行转换才可以进行等值匹配。

SQL改写 

 

这是改写后的SQL,现在查询只需要0.05左右,速度提高了几十倍。完全可以满足ETL要求了。

SELECT
INFO2.ID,
INFO2.NAME,
INFO2.AGE,
INFO2.SEX,
MP.ADDR,
MP.PHONE
FROM (SELECT CAST(INFO.ID AS VARCHAR2(10) AS ID,INFO.NAME,INFO.AGE,INFO.SEX FROM INFORMATION INFO WHERE INFO.ID = 1234567) INFO2
LEFT JOIN MP ON MP.ID = INFO2.ID

 该SQL的执行计划如下,同样的,为了隐私表名和字段名我做了匿名化处理。

Plan hash value: 3589640507---------------------------------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                    | Name                           | Starts | E-Rows |E-Bytes| Cost (%CPU)| E-Time   | A-Rows |   A-Time   | Buffers |
---------------------------------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |                                |      1 |        |       |     9 (100)|          |      1 |00:00:00.01 |       8 |
|   1 |  NESTED LOOPS OUTER          |                                |      1 |      1 |   119 |     5   (0)| 00:00:01 |      1 |00:00:00.01 |       8 |
|   2 |   TABLE ACCESS BY INDEX ROWID| INFORMATION                    |      1 |      1 |    98 |     3   (0)| 00:00:01 |      1 |00:00:00.01 |       4 |
|*  3 |    INDEX UNIQUE SCAN         | UK_20230901211918_5341         |      1 |      1 |       |     2   (0)| 00:00:01 |      1 |00:00:00.01 |       3 |
|   4 |   TABLE ACCESS BY INDEX ROWID| MP                             |      1 |      1 |    21 |     2   (0)| 00:00:01 |      1 |00:00:00.01 |       4 |
|*  5 |    INDEX UNIQUE SCAN         | UK_20230901210612_192177       |      1 |      1 |       |     1   (0)| 00:00:01 |      1 |00:00:00.01 |       3 |
---------------------------------------------------------------------------------------------------------------------------------------------------------Predicate Information (identified by operation id):
---------------------------------------------------3 - access("INFO"."ID"=1234567)5 - access("MP"."PID"=CAST("INFO"."ID" AS VARCHAR2(20)))

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/615791.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

4.2 面向对象程序设计-类的继承实验

本文仅供学习交流,严禁用于商业用途,如本文涉及侵权请及时联系将于24小时内删除 目录 1.实验内容 2.实验原理 2.1类的继承 2.2 继承的优点和缺点 2.3 继承的方式 3.实验代码 1.实验内容 创建一个父类CalcTime,在父类中依次定义用于保存…

10 Php学习:循环

在 PHP 中,提供了下列循环语句: while - 只要指定的条件成立,则循环执行代码块do…while - 首先执行一次代码块,然后在指定的条件成立时重复这个循环for - 循环执行代码块指定的次数foreach - 根据数组中每个元素来循环代码块 当…

Python八股文:基础知识Part2

1. Python中变量的保存和访问 Python中的变量实际上是一个指向对象的引用,每个对象都有一个唯一的标识符(即内存地址)。对于一些不可变对象,如字符串和整数,因为它们的值不可更改,所以当多个变量引用相同的…

React Hooks 全解: 常用 Hooks 及使用场景详解

React Hooks 是 React 16.8 版本引入的一项重要特性,它极大地简化和优化了函数组件的开发过程。 React 中常用的 10 个 Hooks,包括 useState、useEffect、useContext、useReducer、useCallback、useMemo、useRef、useLayoutEffect、useImperativeHandle 和 useDebugValue。这些…

分享一下项目中遇到的排序失效问题

今天把原来的一个查询接口的业务代码进行了优化&#xff0c;减少了十几行冗余的代码。 原来的代码 ChongwuServiceImpl.java /*** author heyunlin* version 1.0*/ Slf4j Service public class ChongwuServiceImpl implements ChongwuService {Overridepublic JsonResult<…

中介者模式:简化对象间通信的协调者

在面向对象的软件开发中&#xff0c;中介者模式是一种重要的行为型设计模式&#xff0c;用于降低多个对象间通信的复杂性。通过提供一个中心化的对象来处理不同组件之间的交互&#xff0c;中介者模式使得组件间不必显式引用彼此&#xff0c;从而使其松散耦合、更易于维护。本文…

数据库系统概论(超详解!!!)第四节 数据库安全性

问题的提出&#xff1a; 数据库的一大特点是数据可以共享 数据共享必然带来数据库的安全性问题 数据库系统中的数据共享不能是无条件的共享。 1.数据库的安全概述 数据库的安全性是指保护数据库以防止不合法使用所造成的数据泄露、更改或破坏 。 系统安全保护措施是否有效…

【电控笔记6】电流回路+延迟效应

问题提出 数字控制系统的delay: 5.4节有介绍T0=0.5TS 低通滤波器的时间常数? 可用示例程序 m2 2 1b 如下图画出开环系统的伯德图进行比较,如图 2-2-4 所示,由于延迟组件会侵蚀系统的相位,因此从图可以看出,加入延迟效应后,q轴电流回路的相位裕度(Phase Margin) 从…

【python】python饮料销售数据分析可视化(源码+数据集)【独一无二】

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;公众号&#x1f448;&#xff1a;测试开发自动化【获取源码商业合作】 &#x1f449;荣__誉&#x1f448;&#xff1a;阿里云博客专家博主、5…

7/8电源连接器航空插头端子

概述 7/8电源连接器是一种工业电源连接器的规格型号之一&#xff0c;常见于工业领域的电力传输和连接应用。它的名称中的“7/8”代表连接器插头的直径尺寸&#xff0c;通常为7/8英寸。这种类型的连接器通常用于较大电流传输和较高功率设备的连接&#xff0c;具有较大的电流承载…

JS控制元素平滑滚动,页面自动滚动锚点实现

使用 scrollIntoView 实现元素内子元素的平滑滚动&#xff0c; 下面是模拟接口list返回&#xff0c;然后通过按钮切换下一个&#xff0c;页面就会滚动到响应的位置 具体 scrollIntoView 有一些其他参数来配置滚动的具体交换&#xff0c;网上去查即可 备注&#xff1a;下面的代码…

实时智能应答3D数字人搭建2

先看效果&#xff1a; 3d数字人讲黑洞 根据艾媒咨询数据&#xff0c;2021年&#xff0c;中国虚拟人核心产业规模达到62.2亿元&#xff0c;带动市场规模达到1074.9亿元&#xff1b;2025年&#xff0c;这一数据预计将达到480.6亿元与6402.7亿元&#xff0c;同比增长迅猛。数字人可…