SQL学习—优化SQL查询性能

1-11 让SQL飞起来

使用高效的查询

参数是子查询时,使用EXISTS代替IN。
两个代码对比:

--慢
SELECT 
    *
FROM
    Class_A
WHERE
    id IN (SELECT 
            id
        FROM
            Class_B)
--快
SELECT 
    *
FROM
    Class_A
WHERE
    EXISTS( SELECT 
            *
        FROM
            Class_B
        WHERE
            A.id = B.id)

原因

  • 如果连接列(id)上建立了索引,那么查询Class_B时不要查实际的表,只需查索引就可以了。
  • 如果使用EXISTS,那么只要查到一行数据满足条件就会终止查询,不用像使用IN时一样扫描全表,这点上NOT EXISTS也一样。

当IN的参数是子查询时,数据库会首先执行子查询,然后将结果存储在一张临时的工作表里(内联视图),然后扫描整个视图。使用EXISTS的话,数据库不会生成临时的工作表。
参数是子查询时,使用连接代替IN。

--使用连接代替IN
SELECT 
    A.id, A.name
FROM
    Class_A A
        INNER JOIN
    Class_B ON A.id = B.id

这种写法至少用到一张表的"id"列上的索引。而且没有子查询,数据库也不会生成一张中间表。

避免排序

SQL中会进行排序的运算有:

  • GROUP BY 子句
  • ORDER BY 子句
  • 聚合函数(SUM、COUNT、AVG、MAX、MIN)
  • DISTINCT
  • 集合运算符(UNION、INTERSECT、EXCEPT)
  • 窗口函数(RANK、ROW_NUMBER等)

思考:如何从上面的商品表Items中找出同时存在于销售记录表SalesHistory中的商品。换言之,找出有销售记录的商品。

SELECT 
    I.item_no
FROM
    Items I
       INNER JOIN
    SalesHistory SH ON I.item_no = SH.item_no

因为是一对多的连接,所以"item_no"列中会出现重复数据。为了排除重复数据,需要使用DISTINCT。

更好的做法是使用EXISTS。

SELECT 
    I.item_no
FROM
    Items I
WHERE
    EXISTS( SELECT 
            *
        FROM
            SalesHistory SH
        WHERE
            I.item_no = SH.item_no)

在极值函数中使用索引(MAX/MIN)
使用这两个函数时都会进行排序。但是如果参数字段上建有索引,则只需要扫描索引,不需要扫描整个表。

-- 这样写需要扫描全表
SELECT MAX(item) FROM Items
-- 这样写能用到索引
SELECT MAX(item_no) FROM Items

因为item_no是表Items的唯一索引,所以效果更好。
能写在WHERE子句里的条件不要写在HAVING子句里

-- 聚合后使用HAVING子句过滤
SELECT 
    sale_date, SUM(quantity)
FROM
    SalesHistory
GROUP BY sale_date
HAVING sale_date = '2017-10-01'
--聚合前使用WHERE子句过滤
SELECT 
    sale_date, SUM(quantity)
FROM
    SalesHistory
WHERE
    sale_date = '2017-10-01'
GROUP BY sale_date

从性能上看,第二条语句写法效率更高。原因:

  • 使用 GROUP BY 子句聚合时会进行排序,如果事先通过WHERE 子句筛选出一部分行,就能够减轻排序的负担。
  • WHERE子句的条件里可以使用索引。HAVING子句是针对聚合后生成的视图进行筛选的,但是很多时候聚合后的视图都没有继承原表的索引结构。

在GROUP BY 子句和ORDER BY 子句使用索引
一般来讲,GROUP BY 子句和 ORDER BY 子句都会进行排序,来对行进行排列和替换。不过,通过指定带索引的列作为GROUP BY 和 ORDER BY 的列,可以实现快速查询。

真的用到索引了吗

下面几种否定形式不能用到索引

  • <>
  • !=
  • NOT IN

减少中间表

在 SQL 中,子查询的结果会被看成一张新表,这张新表与原始表一样,可以通过代码进行操作。

  • 灵活使用HAVING子句
  • 需要对多个字段使用IN谓词时,将它们汇总到一处
  • 先连接再进行聚合
SELECT sale_date,MAX(quantity)
             FROM SalesHistory 
             GROUP BY sale_date
HAVING MAX(quantity) >=10
SELECT * FROM Addresses1 A1
WHERE (id,state,city) 
               IN (SELECT id,state,city 
       FROM Addresses2 A2)

猜你喜欢

转载自blog.csdn.net/weixin_43387060/article/details/86589537