后端程序员书写高质量SQL的30条建议( 二 )


boolean isVip = userIds.contains ( userId );
正例:
Long userId = sqlMap.queryObject ( "select userId from user where userId= 'userId' and isVip='1' ")
boolean isVip = userId!=;
理由:需要什么数据 , 就去查什么数据 , 避免返回不必要的数据 , 节省开销 。
7、尽量避免在索引列上使用mysql的内置函数 。
业务需求:查询最近七天内登陆过的用户(假设loginTime加了索引)
反例:
select userId , loginTime from loginuser where Date_ADD ( loginTime , Interval 7 DAY) >=now;
正例:
explain select userId , loginTime from loginuser where loginTime >= Date_ADD (NOW, INTERVAL -7 DAY);
理由:

  • 索引列上使用mysql的内置函数 , 索引失效;

后端程序员书写高质量SQL的30条建议文章插图
  • 如果索引列不加内置函数 , 索引还是会走的 。

后端程序员书写高质量SQL的30条建议文章插图
8、应尽量避免在where子句中对字段进行表达式操作 , 这将导致系统放弃使用索引而进行全表扫 。
反例:
select * from user where age-1=10;
正例:
select * from tab1 t1 left join tab2 t2 on t1.size = t2.size where t1.id>2;
select * from user where age =11;
理由:虽然age加了索引 , 但是因为对它进行运算 , 索引直接迷路了 。。。
后端程序员书写高质量SQL的30条建议文章插图
9、Inner join 、left join、right join , 优先使用Inner join , 如果是left join , 左边表结果尽量小 。
  • Inner join 内连接 , 在两张表进行连接查询时 , 只保留两张表中完全匹配的结果集;
  • left join 在两张表进行连接查询时 , 会返回左表所有的行 , 即使在右表中没有匹配的记录;
  • right join 在两张表进行连接查询时 , 会返回右表所有的行 , 即使在左表中没有匹配的记录 。
都满足SQL需求的前提下 , 推荐优先使用Inner join(内连接) , 如果要使用left join , 左边表数据结果尽量小 , 如果有条件的尽量放到左边处理 。
反例:
select * from tab1 t1 left join tab2 t2 on t1.size = t2.size where t1.id>2;
正例:
select * from (select * from tab1 where id >2) t1 left join tab2 t2 on t1.size = t2.size;
理由:
  • 如果inner join是等值连接 , 或许返回的行数比较少 , 所以性能相对会好一点;
  • 同理 , 使用了左连接 , 左边表数据结果尽量小 , 条件尽量放到左边处理 , 意味着返回的行数可能比较少 。
10、应尽量避免在where子句中使用!=或<>操作符 , 否则将引擎放弃使用索引而进行全表扫描 。
反例:
select age,name from user where age <>18;
正例:
//可以考虑分开两条sql写
select age,name from user where age <18;
select age,name from user where age >18;
理由:使用!=和<>很可能会让索引失效 。
后端程序员书写高质量SQL的30条建议文章插图
11、使用联合索引时 , 注意索引列的顺序 , 一般遵循最左匹配原则 。
表结构:(有一个联合索引idxuseridage , userId在前 , age在后)
CREATE TABLE `user`(
`id` int (11) NOT AUTO_INCREMENT,
`userId` int(11) NOT ,
`age` int (11) DEFAULT ,
`name` varchar (255) NOT ,
PRIMARY KEY (`id`),
KEY `idx_userid_age` (`userId`,`age`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
反例:
select * from user where age =10;
后端程序员书写高质量SQL的30条建议文章插图
正例:
//符合最左匹配原则
select * from user where userid=10 and age =10;
//符合最左匹配原则
select * from user where userid =10;
后端程序员书写高质量SQL的30条建议文章插图
后端程序员书写高质量SQL的30条建议文章插图
理由:
  • 当我们创建一个联合索引的时候 , 如(k1,k2,k3) , 相当于创建了(k1)、(k1,k2)和(k1,k2,k3)三个索引 , 这就是最左匹配原则;
  • 联合索引不满足最左原则 , 索引一般会失效 , 但是这个还跟Mysql优化器有关的 。
12、对查询进行优化 , 应考虑在where及order by涉及的列上建立索引 , 尽量避免全表扫描 。
反例:
select * from user where address ='深圳' order by age ;
后端程序员书写高质量SQL的30条建议文章插图
正例:
添加索引
alter table user add index idx_address_age (address,age)