使用高效的查询

  1. 使用 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 的话,数据库不会生成临时的工作表。
  1. 参数是子查询时,使用连接代替 IN
    -- 使用连接代替IN
    SELECT A.id, A.name
    FROM Class_A A INNER JOIN Class_B B
    ON A.id = B.id;
    • 这种写法至少能用到一张表的“id”列上的索引。
    • 因为没有了子查询,所以数据库也不会生成中间表。
  2. 避免排序

会进行排序的代表性的运算有下面这些:

  • GROUP BY 子句
  • ORDER BY 子句
  • 聚合函数(SUMCOUNTAVGMAXMIN
  • DISTINCT
  • 集合运算符(UNIONINTERSECTEXCEPT
  • 窗口函数(RANKROW_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 的列,可以实现高速查询。

避免索引未被使用

  1. 在索引字段上进行运算

    -- 不会用到索引
    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';

    使用索引时,条件表达式的左侧应该是原始字段!!!

  2. is null 谓词

    通常,索引字段是不存在 NULL 的,所以指定 IS NULL 和 IS NOT NULL 的话会使得索引无法使用,进而导致查询性能低下。

    在 DB2 和 Oracle 中,IS NULL 条件也能使用索引。这也许是因为它们在实现时为 NULL赋了某个具有特殊含义的值。但是,这个特性不是所有数据库都有的。

  3. 下面这几种否定形式不能用到索引,会进行全表查询。

    • <>

    • !=

    • NOT IN

  4. OR的使用

    在 col_1 和 col_2 上分别建立了不同的索引,或者建立了(col_1, col_2 )这样的联合索引时,如果使用 OR 连接条件,那么要么用不到索引,要么用到了但是效率比 AND 要差很多。

    SELECT *
    FROM SomeTable
    WHERE col_1 > 100
    OR col_2 = 'abc';
  5. 使用联合索引时,列的顺序错误

    假设存在这样顺序的一个联合索引“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 )必须写在查询条件的开头,而且索引中列的顺序不能颠倒。有些数据库里顺序颠倒后也能使用索引,但是性能还是比顺序正确时差一些。

    如果无法保证查询条件里列的顺序与索引一致,可以考虑将联合索引拆分为多个索引。

  6. 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%';
  7. 进行默认的类型转换

    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 中,子查询的结果会被看成一张新表,这张新表与原始表一 样,可以通过代码进行操作。

频繁使用中间表会带来两个问题:

  • 一是展开数据需要耗费内存资源。

  • 二是原始表中的索引不容易使用到(特别是聚合时)。

因此,尽量减 少中间表的使用也是提升性能的一个重要方法。

  1. 灵活使用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;
  2. 需要对多个字段使用 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);
  3. 合理使用视图

    视图是非常方便的工具。但是,如果没有经过深入思考就定义复杂的视图,可能会带来巨大的性能问题。特别是视图的定义语句中包含以下运算的时候,SQL会非常低效,执行速度也会变得非常慢。

    • 聚合函数(AVGCOUNTSUMMINMAX

    • 集合运算符(UNIONNTERSECTEXCEPT* 等)

  4. 先进行连接再进行聚合

    连接和聚合同时使用时,先进行连接操作可以避免产 生中间表。原因是,从集合运算的角度来看,连接做的是“乘法运算”。连接表双方是一对一、一对多的关系时,连接运算后数据的行 数不会增加。

SQL查询性能优化的更多相关文章

  1. mysql经纬度查询并且计算2KM范围内附近用户的sql查询性能优化实例教程

    之前很傻很天真地以为无非就是逐个计算距离,然后比较出来就行了,然后当碰到访问用户很多,而且数据库中经纬度信息很多的时候,计算量的迅速增长,能让服务器完全傻逼掉,还是老前辈的经验比我们丰富,给了我很大的 ...

  2. SQL 查询性能优化----解决书签查找

    先来看看什么是书签查找: 当优化器所选择的非聚簇索引只包含查询请求的一部分字段时,就需要一个查找(lookup)来检索其他字段来满足请求.对一个有聚簇索引的表来说是一个键查找(key lookup), ...

  3. Mysql sql查询性能侦查

    Mysql 服务性能优化配置:http://5434718.blog.51cto.com/5424718/1207526[该文章很好] Sql查询性能优化 对Sql进行优化,肯定是该Sql运行未能达到 ...

  4. SQL常见优化Sql查询性能的方法有哪些?

    常见优化Sql查询性能的方法有哪些? 1.查询条件减少使用函数,避免全表扫描 2.减少不必要的表连接 3.有些数据操作的业务逻辑可以放到应用层进行实现 4.可以使用with as 5.使用“临时表”暂 ...

  5. SQL SERVER 查询性能优化——分析事务与锁(五)

    SQL SERVER 查询性能优化——分析事务与锁(一) SQL SERVER 查询性能优化——分析事务与锁(二) SQL SERVER 查询性能优化——分析事务与锁(三) 上接SQL SERVER ...

  6. SQL Server 查询性能优化 相关文章

    来自: SQL Server 查询性能优化——堆表.碎片与索引(一) SQL Server 查询性能优化——堆表.碎片与索引(二) SQL Server 查询性能优化——覆盖索引(一) SQL Ser ...

  7. Sql Server查询性能优化之走出索引的误区

    据了解绝大多数开发人员对于索引的理解都是一知半解,局限于大多数日常工作没有机会.也什么没有必要去关心.了解索引,实在哪天某个查询太慢了找到查询条件建个索引就ok,哪天又有个查询慢了,再建立个索引就是, ...

  8. SQL Server查询性能优化——堆表、碎片与索引(二)

    本文是对 SQL Server查询性能优化——堆表.碎片与索引(一)的一些总结.  第一:先对 SQL Server查询性能优化——堆表.碎片与索引(一)中的例一的SET STATISTICS IO之 ...

  9. SQL Server查询性能优化——覆盖索引(二)

    在SQL Server 查询性能优化——覆盖索引(一)中讲了覆盖索引的一些理论. 本文将具体讲一下使用不同索引对查询性能的影响. 下面通过实例,来查看不同的索引结构,如聚集索引.非聚集索引.组合索引等 ...

随机推荐

  1. ci get_instance()

    你随便下个CI框架的源码都会看到很多的get_instance() 函数,这个函数是用来获取CI 的全局超级对象,CI 是单例模式的框架,所有全局有一个超级对象.因为只有一个实例,所以无论这个函数使用 ...

  2. 1. FrogRiverOne 一苇渡江 Find the earliest time when a frog can jump to the other side of a river.

    package com.code; public class Test04_3 { public static int solution(int X, int[] A) { int size = A. ...

  3. Alcatel OmniSwitch 重置密码

    OmniSwitch 6250重置密码 Press s to STOP AT MINIBOOT... [Miniboot]->cd "network" value = 0 = ...

  4. [学习笔记]JavaScript基础

    JavaScript概述 1. JavaScript定义 JavaScript是Netscape公司开发的一种基于对象和事件驱动的脚本语言.它是弱类型语言.仅仅能由浏览器解释运行. 当中: 脚本语言: ...

  5. 可编程数据平面将OpenFlow扩展至电信级应用(一)

    可编程数据平面将OpenFlow扩展至电信级应用(一) 案例:基于WinPath网络处理器的电信极OpenFlow (CG-OF)client实现 作者:Liviu Pinchas, Tao Lang ...

  6. 放大的X(杭电2565)

    /*放大的X 请你编程画一个放大的'X'. Input 输入数据第一行是一个整数T,表示有T组測试数据: 接下来有T行,每行有一个正奇数n(3 <= n <= 79).表示放大的规格. O ...

  7. Angular常用标记

    (如果没有特别指明,则所有的HTML元素都支持该标记) (如果没有特别指明,则 AngularJS 指令不会覆盖原生js的指令) 1.数据绑定类: 1.插值语法:{{}} 2.标签内容绑定:ng-bi ...

  8. YTU 2797: 复仇者联盟之关灯

    2797: 复仇者联盟之关灯 时间限制: 1 Sec  内存限制: 128 MB 提交: 563  解决: 160 题目描述 输入n(1~500)盏灯并编号,输入1~9(包含1和9)的数字m,灭掉编号 ...

  9. 那些有意思的Github

    https://github.com/ngosang/trackerslist Linux下那些有趣的命令.. https://www.linu&xp&robe.com/linux-i ...

  10. 5.3QBXT模拟赛

    出题人:钟惠兴 题目名称 讨厌整除的小明 吸血鬼 鱼的感恩 题目类型 传统型 传统型 传统型 题目目录/可执行文件名 ming vamp fool 输入文件名 ming.in vamp.in fool ...