这么骚的SQL进阶技巧,不怕被揍么?( 三 )


怎样做可以减少排序呢?有如下几点:
使用集合运算符的 ALL 可选项:SQL 中有 UNION , INTERSECT , EXCEPT 三个集合运算符 。
默认情况下 , 这些运算符会为了避免重复数据而进行排序 , 对比一下使用 UNION 运算符加和不加 ALL 的情况:

这么骚的SQL进阶技巧,不怕被揍么?

文章插图
 
注意:加 ALL 是优化性能非常有效的手段 , 不过前提是不在乎结果是否有重复数据 。
使用 EXISTS 代表 DISTINCT:为了排除重复数据 , DISTINCT 也会对结果进行排序 , 如果需要对两张表的连接结果进行去重 , 可以考虑用 EXISTS 代替 DISTINCT , 这样可以避免排序 。
这么骚的SQL进阶技巧,不怕被揍么?

文章插图
 
如何找出有销售记录的商品 , 使用如下 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 SHWHERE I.item_no = SH.item_no); 既用到了索引 , 又避免了排序对性能的损耗 。
②在极值函数中使用索引(MAX/MIN)
使用 MAX/ MIN 都会对进行排序 , 如果参数字段上没加索引会导致全表扫描 , 如果建有索引 , 则只需要扫描索引即可 , 对比如下:
-- 这样写需要扫描全表SELECT MAX(item)FROM Items;-- 这样写能用到索引SELECT MAX(item_no)FROM Items; 注意:极值函数参数推荐为索引列中并不是不需要排序 , 而是优化了排序前的查找速度(毕竟索引本身就是有序排列的) 。
③能写在 WHERE 子句里的条件不要写在 HAVING 子句里
下列 SQL 语句返回的结果是一样的:
-- 聚合后使用 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 SalesHistoryWHERE sale_date = '2007-10-01'GROUP BY sale_date; 使用第二条语句效率更高 , 原因主要有两点:
  • 使用 GROUP BY 子句进行聚合时会进行排序 , 如果事先通过 WHERE 子句能筛选出一部分行 , 能减轻排序的负担 。
  • 在 WHERE 子句中可以使用索引 , 而 HAVING 子句是针对聚合后生成的视频进行筛选的 , 但很多时候聚合后生成的视图并没有保留原表的索引结构 。
④在 GROUP BY 子句和 ORDER BY 子句中使用索引
GROUP BY 子句和 ORDER BY 子句一般都会进行排序 , 以对行进行排列和替换 , 不过如果指定带有索引的列作为这两者的参数列 , 由于用到了索引 , 可以实现高速查询 , 由于索引是有序的 , 排序本身都会被省略掉
⑤使用索引时 , 条件表达式的左侧应该是原始字段
假设我们在 col 列上建立了索引 , 则下面这些 SQL 语句无法用到索引:
SELECT *FROM SomeTableWHERE col * 1.1 > 100;SELECT *FROM SomeTableWHERE SUBSTR(col, 1, 1) = 'a'; 以上第一个 SQL 在索引列上进行了运算, 第二个 SQL 对索引列使用了函数 , 均无法用到索引 , 正确方式是把列单独放在左侧 , 如下:
SELECT *FROM SomeTableWHERE col_1 > 100 / 1.1; 当然如果需要对此列使用函数 , 则无法避免在左侧运算 , 可以考虑使用函数索引 , 不过一般不推荐随意这么做 。
⑥尽量避免使用否定形式
如下的几种否定形式不能用到索引:
  • <>
  • !=
  • NOT IN
所以以下 了SQL 语句会导致全表扫描:
SELECT *FROM SomeTableWHERE col_1 <> 100; 可以改成以下形式:
SELECT *FROM SomeTableWHERE col_1 > 100 or col_1 < 100; ⑦进行默认的类型转换
假设 col 是 char 类型 , 则推荐使用以下第二 , 三条 SQL 的写法 , 不推荐第一条 SQL 的写法:
× 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 会默认把 10 转成 '10' , 但这种默认类型转换不仅会增加额外的性能开销 , 还会导致索引不可用 , 所以建议使用的时候进行类型转换 。


推荐阅读