优化 让SQL起飞( 二 )

但是从性能上来看,第二条语句写法效率更高 。原因有两个:

  1. 使用GROUP BY子句聚合时会进行排序,如果事先通过WHERE子句筛选出一部分行,就能够减轻排序的负担 。
  2. 在WHERE子句的条件里可以使用索引 。HAVING子句是针对聚合后生成的视图进行筛选的,但是很多时候聚合后的视图都没有继承原表的索引结构 。
二、真的用到索引了吗2.1 隐式的类型转换如下,col_1字段是char类型:
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)); -- 走了索引当查询条件左边和右边类型不一致时会导致索引失效 。
2.2 在索引字段上进行运算如下:
SELECT *FROM SomeTable WHERE col_1 * 1.1 > 100;在索引字段col_1上进行运算会导致索引不生效,把运算的表达式放到查询条件的右侧,就能用到索引了,像下面这样写就OK了 。
【优化 让SQL起飞】WHERE col_1 > 100 / 1.1如果无法避免在左侧进行运算,那么使用函数索引也是一种办法,但是不太推荐随意这么做 。「使用索引时,条件表达式的左侧应该是原始字段请牢记」,这一点是在优化索引时首要关注的地方 。
2.3 使用否定形式下面这几种否定形式不能用到索引 。
  • <>
  • !=
  • NOT
这个是跟具体数据库的优化器有关,如果优化器觉得即使走了索引,还是需要扫描很多很多行的哈,他可以选择直接不走索引 。平时我们用!=、<>、not in的时候,要注意一下 。
2.4 使用OR查询前后没有同时使用索引例如下表:
CREATE TABLE test_tb (id int(11) NOT NULL AUTO_INCREMENT,name varchar(55) NOT NULL PRIMARY KEY (id)) ENGINE=InnoDB DEFAULT CHARSET=utf8;使用OR条件进行查询
SELECT * FROM test_tb WHERE id = 1 OR name = 'tom'这个SQL的执行条件下,很明显id字段查询会走索引,但是对于OR后面name字段的查询是需要进行全表扫描的 。在这个场景下,优化器直接进行一遍全表扫描就完事了 。
2.5 使用联合索引时,列的顺序错误使用联合索引需要满足最左匹配原则,即最左优先 。如果你建立一个(col_1, col_2, col_3)的联合索引,相当于建立了 (col_1)、(col_1,col_2)、(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)必须写在查询条件的开头,而且索引中列的顺序不能颠倒 。
2.6 使用LIKE查询并不是用了like通配符,索引一定会失效,而是like查询是以%开头,才会导致索引失效 。
-- 没走索引SELECT*FROMSomeTableWHEREcol_1LIKE'%a';-- 没走索引SELECT*FROMSomeTableWHEREcol_1LIKE'%a%';-- 走了索引SELECT*FROMSomeTableWHEREcol_1LIKE'a%';2.7 连接字段字符集编码不一致如果两张表进行连接,关联字段编码不一致会导致关联字段上的索引失效,这是博主在线上经历一次SQL慢查询后的得到的结果,举例如下,有如下两表,它们的name字段都建有索引,但是编码不一致,user表的name字段编码是utf8mb4,user_job表的name字段编码是utf8,
CREATE TABLE `user` (`id` int NOT NULL AUTO_INCREMENT,`name` varchar(255) CHARACTERSET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL,`age` int NOT NULL,PRIMARY KEY (`id`),KEY `idx_name` (`name`)) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;CREATE TABLE `user_job` (`id` int NOT NULL,`userId` int NOT NULL,`job` varchar(255) DEFAULT NULL,`name` varchar(255) DEFAULT NULL,PRIMARY KEY (`id`),KEY `idx_name` (`name`)) ENGINE=InnoDB DEFAULT CHARSET=utf8;进行SQL查询如下:
EXPLAINSELECT * from `user` u join user_job j on u.name = j.name
优化 让SQL起飞

文章插图
 
由结果可知,user表的查询没有走索引 。想要user表也走索引,那就需要把user表name字段的编码改成utf8即可 。
三、减少中间表在SQL中,子查询的结果会被看成一张新表,这张新表与原始表一样,可以通过代码进行操作 。这种高度的相似性使得SQL编程具有非常强的灵活性,但是如果不加限制地大量使用中间表,会导致查询性能下降 。


推荐阅读