百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术资源 > 正文

SQL优化思路(以oracle为例)

moboyou 2025-03-10 13:54 30 浏览

单表查询的优化思路

单表查询是最简单也是最重要的模块,它是多表等查询的基础。

避免对数据重复扫描

能一次扫描拿到的数据,不要重复扫描,查一次库能解决的问题,最好不要多次查。数据的读取非常消耗资源,减少对数据块的扫描。

例如:

1.SELECT COUNT (*)

FROM employees

WHERE salary < 2000;

2.SELECT COUNT (*)

FROM employees

WHERE salary BETWEEN 2000 AND 4000;

3.SELECT COUNT (*)

FROM employees

WHERE salary>4000;

统计任务经常用的语句。其实每个语句基本都把全表或索引扫了一遍,既然要全扫,就把握机会,能一次搞定的就一次搞定。

改写成

SELECT COUNT (CASE WHEN salary < 2000 then 1 else null end count1count case when salary between 2001 and 4000 then 1 else null end count2count case when salary> 4000 THEN 1 ELSE null END) count3 FROM employees;

严格来说,我们不推荐写过度复杂“炫技”的SQL,不要生搬硬套示例,只是为了让大家有个“节省持家”的意识。

例如如下经典写法,通过object_id字段上的索引全扫一遍,拿到了多种类别信息,不要分三次查询。

select max(object_id),min(object_id),sum(object_id),avg(object_id),count(object_id) from t where object_id is not null;



从大表中获取少量数据

从大选小,索引是你的不二选择。

例如:select t.name,t.status from t where t.pay_order_id = 101803309910017574;

索引利用B+树的原理可以快速找到某条数据,所以如果你想在大表中找到某条数据,索引是你必须要使用的技术。如上例所示,通过在pay_order_id上索引快速锁定这条数据的rowid,通过回表找到其他字段 t.name,t.status。这条语句就可以迅速执行,即使是千万级别表。原因还是全表扫描读的块非常多,而索引锁定数据快,读的块非常少,所以时间很快。

如果表记录数很少,使用索引效率反而低。例如,只有几十条记录,所有数据在一个

block 内。则全表扫描只需 1 个 block 的 io,而索引读由于回表等可能需要几个 block。

从大表中获取部分数据

例如:select t.name,t.status from t where t.pay_order_id < 101803309910017574;

上例所示,执行计划可能是全表扫描,也可能走索引。

主要决定因素之一是oracle的代价计算(cost),如果数据量比较大,走索引读,每条数据都伴随着一次回表操作。而全表扫描可以一次读多个块进内存。两种方式相比之下,哪条路径的代价低,oracle就会选择哪条。

所以,全表扫描的速度不一定慢。如果上述的SQL没有满足你的性能需求,且需求不能变,导致SQL已经不能修改时,我们可以考虑能否消除索引的回表操作。无论表多大,结果集多大,一旦所要的数据在索引块中都能找到,就不需要回表。因为索引全扫的块肯定比全表扫的块少的多的多,oracle肯定走索引全扫。

例如:

create index t_union_uuid_order_id on t(pay_order_id,uuid);

select uuid,pay_order_id from t where t.pay_order_id<101803300910017574;

如上例所示,所要字段数据在组合索引块中都能找到,所以没有回表操作。而索引块的数量远远小于全表数据的块数量,即使索引全扫,性能也非常好。

绝大多数情况下,这条select t.name,t.status from t where t.pay_order_id < 101809910017574语句我们可以控制下结果集,让索引即使回表,代价也远低于全表扫描。

组合索引不推荐三个及以上的字段建立组合索引,如果需要的字段非常多,不方便建立组合索引,建议控制结果集,少量快速多次,索引或两字段组合索引,多手段结合使用。具体使用要具体问题具体分析。宗旨就是控制结果集,使得走索引的代价低于全表扫描,然后利用索引快速,读块少的优点提高效率。这样分批几次拿数据,可能速度比一次全拿还快。事实是结果集控制的好,往往全表扫描的效率都能满足需求,更何况是索引扫描。



从大表中获取大量数据

这种场景首先要反问的就是这个需求是否存在问题,是否真的适合用关系型数据库?如果确实有这种需求。大表的数据量往往是惊人的,只能分页去拿。而ORACLE的三层select分页会越分越慢。

SELECT *

FROM (SELECT TA.*, ROWNUM ROW_NUM

FROM (select UUID, pay_order_id

from t

order by pay_order_id) TA

WHERE ROWNUM <= 100)

WHERE ROW_NUM > 0;

主要矛盾就是内层的WHERE ROWNUM <= 1002.order by3. where row_num> 0随着页数越来越大,需要过滤的结果集也越来越大。

推荐方式:

SELECT t.*

FROM (select uuid, pay_order_id

from t

where t.pay_order_id is not null【*注】如果没有非空约束必须显示标明,否则索引失效

and t.pay_order_id >= '101809020001428452'

order by t.pay_order_id) t

WHERE ROWNUM <= 100;

pay_order_id 字段的需求是只增不减,为了不重不漏必须排序。索引是有序的,我们想用索引抵消掉排序,所以要查看执行计划,必须要走到索引。WHERE ROWNUM <= 100oraclet.pay_order_id>= '101809020001428452'之后的100条数据。所以结果集控制住了,索引代价肯定低于全表扫描,肯定走索引,索引又抵消了排序,同时 WHERE ROWNUM <= 100;每页都是100,rownum的性能损耗也控制住了。

这样额外的代价是,程序每次要记住最后一条pay_order_id,下次分页的时候将其带入。

推广到其他应用则可以选择表中的create_time字段代替pay_order_id。



多表查询的优化思路

多表连接把握住连接方式

多表查询和单表查询,唯一不同的就是把握住连接方式,只要连接方式把握住,多表查询其实就是多次单表查询。

三种连接方式:

nested loops join拿驱动表的结果集,去连接另外一个表,类似于两重嵌套循环(典型使用:小表驱动大表)。

hash join 拿驱动表的结果集去做hash表,PGA区,结果集大了,会到磁盘里。

merge join 无驱动表的概念,较少用到,对于连接键有序。


powered by wanglifeng https://www.cnblogs.com/wanglifeng717


从原理图可以看出,循环嵌套连接和hash连接中驱动表非常关键,准确说驱动表的结果集非常关键。循环嵌套连接的结果集大了,双层循环非常低效,哈希连接结果集大了可能导致排序开销变大,PGA区放不下等问题。

驱动表是oracle自动选择的,默认是加了过滤条件后,结果集小的那个表。如果查看执行计划,驱动表不如你所愿,你需要检查结果集是否相比另一个表结果集来说,明显是小结果集。或者自动收集信息不准确,需要更新。

如果是多表连接查询少量数据,推荐走循环嵌套连接。

create index n_index_order_id on n(order_id);

create index t_index_query_id on t(query_id);

select t.id ,t.name,n.address from n, t where t.pay_order_id=n.order_id and t.query_id='261801163544557068';

在驱动表的过滤条件上建立索引,快速锁定需要的少量数据行,在被驱动表的连接字段上建立索引,方便连接条件迅速匹配。这样的配合,就算两个表都是千万级别的表,只要索引不失效,速度都非常快。

如果是多表连接要查询出一部分数据,推荐走哈希连接

首先过滤条件过滤出小结果集,小结果集是个相对的概念,有时1000条算小结果集,有时10条也算大结果集,这里的小结果集一般在百条量级。

哈希连接的特点就是,无论驱动表的结果集在一定范围内如何变化,理论上,一次查询的时间近似等于扫一遍被驱动表的时间。性能表现相当高效和稳定。

控制驱动表的结果集,在被驱动表的连接字段上建立索引,忽略回表等细节,确认走到索引,这样一次查询的时间近似等于被驱动表的索引全扫时间,而我们知道,索引块相对全表块是非常少的,索引全扫非常高效。

走哪种连接方式,是oracle自动选择的,oracle选择的规则就是基于上述原理,所以我们决定不了走哪种执行计划,但是我们能让oracle”不得不走”哪种执行计划。



控制住结果集

控制结果集,不仅体现在单表查询的索引选择问题,还有体现在多表查询的连接方式和效率上。

除此之外还存在很多误区。结果集的概念并不是简单的数据量,而是一种意识,有控制结果集的意识,而不是教条主义的定义多少数量算大结果集。

结果集经典示例:

把in换成exists就完事了,性能就优化了,这是常犯的误区。

in是判断一个值是否在某个列中,而exists是判断一个值是否存在

Select * from tab where id in ( select id from tabel );

In 是先产生子查询结果集,然后主查询区结果集中寻找符合要求的字段列表,符合要求的输出。

Exists不返回列表值,而是true或者false,运行方式为,先运行主查询一次,在去子查询中查询与之对应的结果,如果子查询返回true则输出,反之不输出,在根据主查询的每一行去子查询中查询。

从原理可以看出,如果in的子查询结果集很大,外层的结果集也很大,相当于两个大结果集在连接运算,很耗性能。

Exists的运算比in优化了,但是就是搜索内层子查询的时候优化了,但是关键点是要把握住内外层的结果集,如果结果集很大,exists同样很慢,结果集控制的好,in操作也能符合要求。

总结:不管你多有把握,请一定要看下执行计划,一定要看下执行计划,一定要看下执行计划。。。。



相关推荐

iis部署php项目(iis发布php)

1.启动iis服务器最后点击确定就完成了2.打开iis点击进入即可3.创建网站进入添加网站。添加注意事项如图所示!启动、浏览、重启、停止网站这个如图所示。需要说明的是只要修改了配置就必须重启下网站4...

Win10安装Apache和PHP(apache安装php模块)

说明:虽然PHPStudy之类的软件给我们提供了方便的集成环境,但是其使用的是非线程安全的PHP版本(NotThreadSafe,NTS),某些功能不可以使用。所以,我们还需要自己再安装一个Apa...

两个php框架在一个网站上开发好吗

框架就是通过提供一个开发Web程序的基本架构,PHP开发框架把PHPWeb程序开发摆到了流水线上。换句话说,PHP开发框架有助于促进快速软件开发(RAD),这节约了你的时间,有助于创建更为稳定的程序,...

Ubuntu linux 上的 Nginx 和 Php 安装

教程-在UbuntuLinux上安装Nginx1.安装Nginx服务器和所需的包。apt-getupdatebrapt-getinstallnginx2.在Nginx配置文件...

网站开发初级(3) 之 PHP运行环境搭建

这篇文章主要是讲windows下php环境的搭建这里我们用的是phpstudy,作为入门学者足够了,解压安装就能用了,适当配置下基本能应付我们的开发要求下载地址:http://rj.baidu.co...

真实案例:优化用老PHP7.4的网站让其速度提高4倍

我让反应迟钝的PHP应用程序性能提升了4倍,而且我甚至没有改动任何源代码。没有重构,没有重写,只是进行了一系列精心规划的配置调整和部署优化。这篇文章将详细告诉你我是如何做到的。如果你的PHP应用程序运...

mac下快速搭建本地php开发环境(macbook开发php)

本教程主要是告诉大家,怎么快速的在mac下搭建php+mysql服务。一、安装CommandLineTools苹果系统下很多开发工具是基于CommandLineTools,所以安装它是首先。...

php手把手教你做网站(二十)vue+tp6简单案例(demo)

很多时候搭建好了环境,但是不知道怎么入手去开发。下面我们通过简单案例说明如何快速入门开发模块:例1:开发helloworld模块搭建好环境,新建项目以后,进入项目所在文件夹,依次进入src/compo...

Windows安装phpstudy(windows安装docker desktop)

说明:phpstudy是一个PHP+MySQL+Apache的集成环境,可以减少单独部署各个所需软件的麻烦,以及更加方便地切换版本。phpenv、wamp等软件的作用一样。由于环境的不同,安装过程中可...

服务器安装PHP网站的运行环境(php的服务器app)

首先要确认的是你的服务器的系统,Windows、或者linux系统。要想在Windows系统里运行php网站,可以选择iis或者Apache,如果你单独的去安装,可能会花很多时间去配置这个软件直接问题...

折腾群晖NAS:使用群晖web功能搭建个人博客「 小白玩NAS系列」

大家好,今天分享给大家如何利用群晖自带的web功能,来搭建一个属于自己的个人网站或者博客。搭建群晖web环境1、首先进入套件中心,下载webstation,群晖自带的web环境,安装会提示安装PHP...

黑客搭建钓鱼平台,手把手教你如何钓鱼?

跨站脚本攻击XSS:通过HTML注入篡改了网页,插入了恶意的脚本,从而用户浏览网页时,控制用户浏览器的一种攻击那么,我们搭建一个XSS钓鱼平台吧,注意:这个平台仅用于学习和测试,小伙伴们不要动有坏心思...

PHP 环境 搭建教程(php环境搭建apache)

PHP是一种编程语言,很多网站都用PHP语言编写,我们有时候需要测试一个网站,就需要PHP环境才能运行,又要安装Apache、又要安装MySQL……真的非常麻烦。其实我们可以使用PHP集成...

php手把手教你做网站(六)IIS创建站点注意事项

站点名称:随便填写;应用程序池:创建站点的时候会自动生成对应的,默认就好;物理路径:选择站点所在目录;类型:本地一定是http,网上可能会有https;IP地址:默认端口默认,如果是别的端口,网址访问...

分享PHP网站开发过程中的一些经验

现在的网站建设语言有几种,分别是ASP.NET,PHP,JSP这三种最为常见,这三种语言做出来的网站各有各的特点,asp属于最成熟的建站模式,jsp一般使用在大型网站系统上,对于php是目前比较流行的...