--慢 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时更快的原因有以下两个:
GROUP BY子句 ORDER BY子句 聚合函数(SUM、COUNT、AVG、MAX、MIN) DISTINCT 集合运算符(UNION、INTERSECT、EXCEPT) 窗口函数(RANK、ROW_NUMBER等)如上列出的六种运算(除了集合运算符),它们后面跟随或者指定的字段都可以添加索引,这样可以加快排序。
SELECT DISTINCT I.item_no FROM Items I INNER JOIN SalesHistory SH ON I. item_no = SH. item_no; item_no ------- 10 20 30 使用EXISTS代替DISTINCT去重,SQL如下: SELECT item_no FROM Items I WHERE EXISTS (SELECT * FROM SalesHistory SH WHERE I.item_no = SH.item_no); item_no ------- 10 20 30这条语句在执行过程中不会进行排序。而且使用EXISTS和使用连接一样高效。
--聚合后使用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;但是从性能上来看,第二条语句写法效率更高。原因有两个:
2.在WHERE子句的条件里可以使用索引。HAVING子句是针对聚合后生成的视图进行筛选的,但是很多时候聚合后的视图都没有继承原表的索引结构。
/***** 堆代码 duidaima.com ****/ 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)); -- 走了索引当查询条件左边和右边类型不一致时会导致索引失效。
SELECT * FROM SomeTable WHERE col_1 * 1.1 > 100;在索引字段col_1上进行运算会导致索引不生效,把运算的表达式放到查询条件的右侧,就能用到索引了,像下面这样写就OK了。
WHERE col_1 > 100 / 1.1如果无法避免在左侧进行运算,那么使用函数索引也是一种办法,但是不太推荐随意这么做。「使用索引时,条件表达式的左侧应该是原始字段请牢记」,这一点是在优化索引时首要关注的地方。
<> != NOT这个是跟具体数据库的优化器有关,如果优化器觉得即使走了索引,还是需要扫描很多很多行的哈,他可以选择直接不走索引。平时我们用!=、<>、not in的时候,要注意一下。
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字段的查询是需要进行全表扫描的。在这个场景下,优化器直接进行一遍全表扫描就完事了。
-- 走了索引 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)必须写在查询条件的开头,而且索引中列的顺序不能颠倒。
-- 没走索引 SELECT * FROM SomeTable WHERE col_1 LIKE'%a'; -- 没走索引 SELECT * FROM SomeTable WHERE col_1 LIKE'%a%'; -- 走了索引 SELECT * FROM SomeTable WHERE col_1 LIKE'a%';2.7 连接字段字符集编码不一致
CREATE TABLE `user` ( `id` int NOT NULL AUTO_INCREMENT, `name` varchar(255) CHARACTER SET 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查询如下:
EXPLAIN SELECT * from `user` u join user_job j on u.name = j.name由结果可知,user表的查询没有走索引。想要user表也走索引,那就需要把user表name字段的编码改成utf8即可。
SELECT * FROM ( SELECT sale_date, MAX(quantity) max_qty FROM SalesHistory GROUP BY sale_date ) tmp WHERE max_qty >= 10然而,对聚合结果指定筛选条件时不需要专门生成中间表,像下面这样使用HAVING子句就可以。
SELECT sale_date, MAX(quantity) FROM SalesHistory GROUP BY sale_date HAVING MAX(quantity) >= 10;HAVING子句和聚合操作是同时执行的,所以比起生成中间表后再执行的WHERE子句,效率会更高一些,而且代码看起来也更简洁。
SELECT * FROM Addresses1 A1 WHERE id || state || city IN (SELECT id || state|| city FROM Addresses2 A2);这样一来,子查询不用考虑关联性,而且只执行一次就可以。