SQL查询性能优化
使用高效的查询
使用 EXISTS 代替 IN
-- 查询A表中同时存在B表的数据
-- 慢
SELECT *
FROM Class_A
WHERE id IN (SELECT id
FROM Class_B);
-- 快
SELECT *
FROM Class_A A
WHERE EXISTS (SELECT *
FROM Class_B B
WHERE A.id = B.id);
EXISTS更快的原因:
- 如果连接列(id )上建立了索引,那么查询 Class_B 时不用查 实际的表,只需查索引就可以了。
- 如果使用 EXISTS ,那么只要查到一行数据满足条件就会终止 查询,不用像使用 IN 时一样扫描全表。在这一点上 NOT EXISTS 也一样。
- 当 IN 的参数是子查询时,数据库首先会执行子查询,然后将结果存 储在一张临时的工作表里(内联视图),然后扫描整个视图,很多情况下很消耗资源。使用 EXISTS 的话,数据库不会生成临时的工作表。
参数是子查询时,使用连接代替 IN
-- 使用连接代替IN
SELECT A.id, A.name
FROM Class_A A INNER JOIN Class_B B
ON A.id = B.id;
- 这种写法至少能用到一张表的“id”列上的索引。
- 因为没有了子查询,所以数据库也不会生成中间表。
避免排序
会进行排序的代表性的运算有下面这些:
- GROUP BY 子句
- ORDER BY 子句
- 聚合函数(SUM 、COUNT 、AVG 、MAX 、MIN )
- DISTINCT
- 集合运算符(UNION 、INTERSECT 、EXCEPT )
- 窗口函数(RANK 、ROW_NUMBER 等)
排序如果只在内存中进行,那么还好;但是如果内存不足因而需要在 硬盘上排序,那么伴随着“呲啦呲啦”的硬盘访问声,排序的性能也会 急剧恶化
灵活使用集合运算符的 ALL 可选项
SQL 中有 UNION 、INTERSECT 、EXCEPT(MINUS) 三个集合运算符。在默认的使用方式下,这些运算符会为了排除掉重复数据而进行排序。
SELECT * FROM Class_A
UNION
SELECT * FROM Class_B;
如果不在乎结果中是否有重复数据,或者事先知道不会有重复数据,请使用 UNION ALL 代替 UNION 。这样就不会进行排序了。
使用 EXISTS 代替 DISTINCT
为了排除重复数据,DISTINCT 也会进行排序。如果需要对两张表的连接结果进行去重,可以考虑使用 EXISTS 代替 DISTINCT ,以避免排序。
-- 使用DISTINCT去重
SELECT DISTINCT I.item_no
FROM Items I INNER JOIN SalesHistory SH
ON I. item_no = SH. item_no;
-- 使用exists去重,避免排序
SELECT item_no
FROM Items I
WHERE EXISTS (SELECT *
FROM SalesHistory SH
WHERE I.item_no = SH.item_no);
在极值函数中使用索引(MAX/MIN)
SQL 语言里有 MAX 和 MIN 两个极值函数。使用这两个函数时都会进行排序。但是如果参数字段上建有索引,则
只需要扫描索引,不需要扫描整张表。
能写在 WHERE 子句里的条件不要写在 HAVING 子句里
-- 聚合后使用HAVING 子句过滤
SELECT sale_date, SUM(quantity)
FROM SalesHistory
GROUP BY sale_date
HAVING sale_date = '2007-10-01';
-- 聚合前使用WHERE 子句过滤
SELECT sale_date, SUM(quantity)
FROM SalesHistory
WHERE sale_date = '2007-10-01'
GROUP BY sale_date;
但是从性能上来看,第二条语句写法效率更高。原因通常有两个。第一个是在使用 GROUP BY 子句聚合时会进行排序,如果事先通过WHERE 子句筛选出一部分行,就能够减轻排序的负担。第二个是在WHERE 子句的条件里可以使用索引。HAVING 子句是针对聚合后生成的视图进行筛选的,但是很多时候聚合后的视图都没有继承原表的索引结构 。
在 GROUP BY 子句和 ORDER BY 子句中使用索引
一般来说,GROUP BY 子句和 ORDER BY 子句都会进行排序,来对行 进行排列和替换。不过,通过指定带索引的列作为 GROUP BY 和 ORDER BY 的列,可以实现高速查询。
避免索引未被使用
在索引字段上进行运算
-- 不会用到索引
SELECT *
FROM SomeTable
WHERE col_1 * 1.1 > 100; --把运算的表达式放到查询条件的右侧,就能用到索引了
SELECT *
FROM SomeTable
WHERE col_1 > 100 / 1.1; --在查询条件的左侧使用函数时,也不能用到索引。
SELECT *
FROM SomeTable
WHERE SUBSTR(col_1, 1, 1) = 'a';
使用索引时,条件表达式的左侧应该是原始字段!!!
is null 谓词
通常,索引字段是不存在 NULL 的,所以指定 IS NULL 和 IS NOT NULL 的话会使得索引无法使用,进而导致查询性能低下。
在 DB2 和 Oracle 中,IS NULL 条件也能使用索引。这也许是因为它们在实现时为 NULL赋了某个具有特殊含义的值。但是,这个特性不是所有数据库都有的。
下面这几种否定形式不能用到索引,会进行全表查询。
<>
!=
NOT IN
OR的使用
在 col_1 和 col_2 上分别建立了不同的索引,或者建立了(col_1, col_2 )这样的联合索引时,如果使用 OR 连接条件,那么要么用不到索引,要么用到了但是效率比 AND 要差很多。
SELECT *
FROM SomeTable
WHERE col_1 > 100
OR col_2 = 'abc';
使用联合索引时,列的顺序错误
假设存在这样顺序的一个联合索引“col_1, col_2, col_3 ”这时,指定条件的顺序就很重要。
○ SELECT * FROM SomeTable WHERE col_1 = 10 AND col_2 = 100 AND col_3 = 500;
○ SELECT * FROM SomeTable WHERE col_1 = 10 AND col_2 = 100 ;
× SELECT * FROM SomeTable WHERE col_1 = 10 AND col_3 = 500 ;
× SELECT * FROM SomeTable WHERE col_2 = 100 AND col_3 = 500 ;
× SELECT * FROM SomeTable WHERE col_2 = 100 AND col_1 = 10 ;
联合索引中的第一列(col_1 )必须写在查询条件的开头,而且索引中列的顺序不能颠倒。有些数据库里顺序颠倒后也能使用索引,但是性能还是比顺序正确时差一些。
如果无法保证查询条件里列的顺序与索引一致,可以考虑将联合索引拆分为多个索引。
LIKE谓词
使用 LIKE 谓词时,只有前方一致的匹配才能用到索引。
× SELECT * FROM SomeTable WHERE col_1 LIKE '%a';
× SELECT * FROM SomeTable WHERE col_1 LIKE '%a%';
○ SELECT * FROM SomeTable WHERE col_1 LIKE 'a%';
进行默认的类型转换
对 char 类型的“col_1”列指定条件的示例
× SELECT * FROM SomeTable WHERE col_1 = 10;
○ SELECT * FROM SomeTable WHERE col_1 = '10';
○ SELECT * FROM SomeTable WHERE col_1 = CAST(10, AS CHAR(2));
默认的类型转换不仅会增加额外的性能开销,还会导致索引不可用, 可以说是有百害而无一利。虽然这样写还不至于出错,但还是不要嫌麻烦,在需要类型转换时显式地进行类型转换吧(转换要写在 条件表达式的右边)。
减少使用中间表
在 SQL 中,子查询的结果会被看成一张新表,这张新表与原始表一 样,可以通过代码进行操作。
频繁使用中间表会带来两个问题:
一是展开数据需要耗费内存资源。
二是原始表中的索引不容易使用到(特别是聚合时)。
因此,尽量减 少中间表的使用也是提升性能的一个重要方法。
灵活使用having子句
对聚合结果指定筛选条件时,使用 HAVING 子句是基本原则。
不习惯使用 HAVING 子句的可能会倾向于生成一 张中间表
SELECT *
FROM (SELECT sale_date, MAX(quantity) AS max_qty
FROM SalesHistory
GROUP BY sale_date) TMP ←----- 没用的中间表
WHERE max_qty >= 10; --对聚合结果指定筛选条件时不需要专门生成中间表
SELECT sale_date, MAX(quantity)
FROM SalesHistory
GROUP BY sale_date
HAVING MAX(quantity) >= 10;
需要对多个字段使用 IN 谓词时,将它们汇总到一处
-- 这段代码中用到了两个子查询
SELECT id, state, city
FROM Addresses1 A1
WHERE state IN (SELECT state
FROM Addresses2 A2
WHERE A1.id = A2.id)
AND city IN (SELECT city
FROM Addresses2 A2
WHERE A1.id = A2.id); --把字段连接在一起,子查询不用考虑关联性,而且只执行一次就可以
SELECT *
FROM Addresses1 A1
WHERE id || state || city
IN (SELECT id || state|| city
FROM Addresses2 A2); -- 如果所用的数据库实现了行与行的比较
SELECT *
FROM Addresses1 A1
WHERE (id, state, city)
IN (SELECT id, state, city
FROM Addresses2 A2);
合理使用视图
视图是非常方便的工具。但是,如果没有经过深入思考就定义复杂的视图,可能会带来巨大的性能问题。特别是视图的定义语句中包含以下运算的时候,SQL会非常低效,执行速度也会变得非常慢。
聚合函数(AVG 、COUNT 、SUM 、MIN 、MAX )
集合运算符(UNION 、NTERSECT 、EXCEPT* 等)
先进行连接再进行聚合
连接和聚合同时使用时,先进行连接操作可以避免产 生中间表。原因是,从集合运算的角度来看,连接做的是“乘法运算”。连接表双方是一对一、一对多的关系时,连接运算后数据的行 数不会增加。
SQL查询性能优化的更多相关文章
- mysql经纬度查询并且计算2KM范围内附近用户的sql查询性能优化实例教程
之前很傻很天真地以为无非就是逐个计算距离,然后比较出来就行了,然后当碰到访问用户很多,而且数据库中经纬度信息很多的时候,计算量的迅速增长,能让服务器完全傻逼掉,还是老前辈的经验比我们丰富,给了我很大的 ...
- SQL 查询性能优化----解决书签查找
先来看看什么是书签查找: 当优化器所选择的非聚簇索引只包含查询请求的一部分字段时,就需要一个查找(lookup)来检索其他字段来满足请求.对一个有聚簇索引的表来说是一个键查找(key lookup), ...
- Mysql sql查询性能侦查
Mysql 服务性能优化配置:http://5434718.blog.51cto.com/5424718/1207526[该文章很好] Sql查询性能优化 对Sql进行优化,肯定是该Sql运行未能达到 ...
- SQL常见优化Sql查询性能的方法有哪些?
常见优化Sql查询性能的方法有哪些? 1.查询条件减少使用函数,避免全表扫描 2.减少不必要的表连接 3.有些数据操作的业务逻辑可以放到应用层进行实现 4.可以使用with as 5.使用“临时表”暂 ...
- SQL SERVER 查询性能优化——分析事务与锁(五)
SQL SERVER 查询性能优化——分析事务与锁(一) SQL SERVER 查询性能优化——分析事务与锁(二) SQL SERVER 查询性能优化——分析事务与锁(三) 上接SQL SERVER ...
- SQL Server 查询性能优化 相关文章
来自: SQL Server 查询性能优化——堆表.碎片与索引(一) SQL Server 查询性能优化——堆表.碎片与索引(二) SQL Server 查询性能优化——覆盖索引(一) SQL Ser ...
- Sql Server查询性能优化之走出索引的误区
据了解绝大多数开发人员对于索引的理解都是一知半解,局限于大多数日常工作没有机会.也什么没有必要去关心.了解索引,实在哪天某个查询太慢了找到查询条件建个索引就ok,哪天又有个查询慢了,再建立个索引就是, ...
- SQL Server查询性能优化——堆表、碎片与索引(二)
本文是对 SQL Server查询性能优化——堆表.碎片与索引(一)的一些总结. 第一:先对 SQL Server查询性能优化——堆表.碎片与索引(一)中的例一的SET STATISTICS IO之 ...
- SQL Server查询性能优化——覆盖索引(二)
在SQL Server 查询性能优化——覆盖索引(一)中讲了覆盖索引的一些理论. 本文将具体讲一下使用不同索引对查询性能的影响. 下面通过实例,来查看不同的索引结构,如聚集索引.非聚集索引.组合索引等 ...
随机推荐
- springMvc把client传过来一个String类型,转换为日期类型为例
springMvc--接受日期类型参数处理 目录 步骤 2.自定义类型转换规则 3.注册自定义的类型转换类 4.地址栏访问 这个问题,也即是springMvc如何进行参数类型的转换 , 以把cli ...
- addEventListener()、attachEvent()和removeEventListener()、detachEvent()的差别?
addEventListener()和attachEvent()的差别? addEventListener:在HTML元素上绑定事件,FF.chrome.opera.safari及IE9浏览器以上的支 ...
- 线程调度策略SCHED_RR(轮转法)和SCHED_FIFO(先进先出)之对照
我们在用pthread创建线程时,能够指定调度策略policy--SCHED_OTHER(默认).SCHED_RR和SCHED_FIFO.这里TALK一下两个实时策略--SCHED_RR和SCHED_ ...
- bbed改动undo段状态(ORA-01578)
ZBDBA@orcl11g>select * from zbdba; select * from zbdba * ERROR at line 1: ORA-01578: ORACLE data ...
- UITableView和UITableViewCell的几种样式
UITableView和UITableViewCell的几种样式 转至 http://blog.csdn.net/crazyzhang1990/article/details/12503163 一. ...
- 《iOS Human Interface Guidelines》——Wallet
Wallet Wallet(钱包)帮助人们查看和管理Pass(凭证),这是一种相似于登机牌.优惠券.会员卡.奖励卡和各种票的物理凭证的数字替代.Wallet也同意人们加入信用卡.借记卡和储值卡来和Ap ...
- Java程序学习中各阶段的建议
第一部分:对于尚未做过Java工作的同学,包括一些在校生以及刚准备转行Java的同学. 一.Java基础 首先去找一个Java的基础教程学一下,这里可以推荐一个地址,或者你也可以参照这个地址上去找相应 ...
- BestCoder Round #60.1003.GT and set/HDU5506 dfs
GT and set Accepts: 35 Submissions: 194 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 655 ...
- POJ 3221 Diamond Puzzle.
~~~~ 题目链接:http://poj.org/problem? id=3221 显然是BFS找最优解.但是终止条件不好写.看到有一仅仅队交上去一直TLE. 比赛完了看题解原来是以目标状态为起点,B ...
- 超级有用的git reset --hard和git revert命令
很多时候,git新手容易误操作,比如,在levelIISZ-1.4.dev分支下,运行了git pull idc cpp-1.0的结果,这样做麻烦很大,经常导致maven项目格式不正确,这个时候,可以 ...