编写高质量SQL语句的50条建议(满满的都是干货)
在数据库管理和开发中,SQL(Structured Query Language)是不可或缺的核心技能。无论是进行数据的增删改查,还是优化数据库性能,编写高质量的SQL语句都是至关重要的。以下,我将分享20条编写高质量SQL语句的建议,旨在帮助开发人员和数据库管理员提升效率,减少错误,并优化数据库性能。
1. 明确查询目的
在编写SQL之前,首先要明确查询的目标是什么。是获取某个具体的数据集,还是进行数据的汇总分析?明确目标有助于构建更加精确和高效的查询。
2. 使用合适的SELECT列
避免使用`SELECT *`,除非确实需要所有列。指定需要的列可以减少数据传输量,提高查询效率,并有助于避免不必要的列暴露给前端应用。
3. 利用表别名
在查询中,为表设置别名可以使SQL语句更加简洁易读,特别是在涉及多表连接时。同时,别名还可以帮助解决列名冲突的问题。
4. 合理使用WHERE子句
WHERE子句用于过滤记录,确保只返回满足条件的数据。合理使用索引列作为过滤条件可以显著提升查询速度。
5. 优化JOIN操作
在进行表连接时,确保连接条件被索引覆盖,且连接顺序合理。内连接(INNER JOIN)优先于外连接(LEFT/RIGHT JOIN),因为内连接通常更高效。
6.使用子查询需谨慎
子查询在某些情况下非常有用,但过多的子查询会增加查询的复杂度,降低性能。考虑是否可以使用JOIN替代子查询,或者将子查询作为临时表(CTE)处理。
7. 利用索引
索引是加速数据库查询的关键。确保在WHERE子句、JOIN条件、ORDER BY和GROUP BY子句中使用的列都被索引。同时,注意索引的维护,避免过多索引带来的写操作负担。
8. 避免在WHERE子句中使用函数
在WHERE子句中对列使用函数会阻止数据库利用索引,导致查询性能下降。尽可能在查询外部处理数据转换。
9.合理使用聚合函数
聚合函数如SUM、AVG、COUNT等是处理数据汇总的强大工具。但使用时要注意,它们会强制数据库进行全表扫描或至少扫描大部分数据,因此要确保查询的列已被索引或查询本身合理。
10.利用GROUP BY的HAVING子句
HAVING子句是对GROUP BY结果集的进一步过滤。与WHERE子句不同,HAVING可以应用于聚合函数的结果。合理使用HAVING子句可以进一步减少结果集的大小。
11.使用DISTINCT去除重复数据
当需要去除查询结果中的重复行时,可以使用DISTINCT关键字。但请注意,DISTINCT操作会消耗额外的计算资源,特别是在处理大数据集时。
12.考虑使用LIMIT限制结果集
当只需要部分数据时,使用LIMIT子句可以显著减少数据传输和处理时间。这对于分页显示数据尤其有用。
13.避免在SELECT中使用计算字段进行排序
在ORDER BY子句中使用计算字段(如`ORDER BY LENGTH(column_name)`)会阻止数据库使用索引,导致排序效率低下。如果可能,先计算并存储这些值,然后再进行排序。
14. 使用参数化查询
参数化查询不仅可以防止SQL注入攻击,还可以提高查询的重用性,从而优化性能。
15. 避免使用SELECT DISTINCT与多个列
当SELECT DISTINCT涉及多个列时,数据库需要创建临时表来存储唯一行,这会影响性能。如果可能,考虑使用其他逻辑来替代。
16. 利用EXPLAIN分析查询计划
大多数数据库管理系统都提供了EXPLAIN命令,用于分析SQL语句的执行计划。通过查看查询计划,可以了解数据库如何执行查询,并据此进行优化。
17. 避免过度使用子查询
虽然子查询在某些情况下很有用,但过度使用会导致查询性能下降。考虑将子查询重写为连接(JOIN)或使用临时表(CTE)来提高效率。
18.使用索引提示
在某些情况下,数据库可能无法选择最优的索引。此时,可以使用索引提示来指定数据库使用特定的索引。但请注意,这可能会降低查询的可移植性。
19. 定期审查和优化查询
数据库的性能会随着数据和查询的变化而变化。定期审查和优化查询是保持数据库高效运行的关键。
20.保持SQL语句的简洁性**
复杂的SQL语句不仅难以维护,还可能导致性能问题。尽量保持SQL语句的简洁性,避免不必要的复杂性和冗余。
21、查询SQL尽量不要使用select *,而是select具体字段反例:select * from employee;正例:select id,name from employee;
理由:只取需要的字段,节省资源、减少网络开销。select *进行查询时,很可能就不会使用到覆盖索引了,就会造成回表查询
22、如果知道查询结果只有一条或者只要最大/最小一条记录,建议用limit 1
反例:
select id,name from employee where name=jay
正例:
select id,name from employee where name='jay' limit 1;
理由:加上limit 1 后,只要找到了对应的一条记录,就不会继续向下扫描了,效率将会大大提高。
23、应尽量避免在where子句中使用or来连接条件反例:select * from user where usered=1 or age=18
正例:(使用union all )
select * from user where usered=1 union all select * from user where age=18
理由:使用or可能会使索引失效,从而全表扫描。
24、优化limit分页
反例:
select id, name, age from employee limit 10000,10
正例:
select id, name from employee whereid>10000 limit 10;
理由:当偏移量最大的时候,查询效率就会越低,因为Mysql并非是跳过偏移量直接去取后面的数据,而是先把偏移量+要取的条数,然后再把前面偏移量这一段的数据抛弃掉再返回的。
25、优化你的like语句
反例:
select userld, name from user where userld like '% 123';
正例:
select userld, name from user where userld like '123%;
理由:日常开发中,如果用到模糊关键字查询很容易想到like,但是like很可能让你的索引失效。把%放前面,并不走索引;把%放关键字后面,还是会走索引的。
26、使用where条件限定要查询的数据,避免返回多余的行
反例:
List<Long> userlds = sqlMap. queryList("select userld from user where isVip=1");boolean isVip = userlds.contains(userld)
正例:
Long userld = sqlMap.queryObject("select userld fromuser where userld='userld' and isVip='1'")boolean isVip = userld! =null;
理由:需要什么数据,就去查什么数据,避免返回不必要的数据,节省开销。
27、尽量避免在索引列上使用mysqI的内置函数
反例:
select userld, loginTime from loginuser where Date_ADD(loginTime, interval 7DAY) >=now();
正例:
explain select userld, loginTime from loginuser where loginTime >=Date ADD(NOW(), INTERVAL -7 DAY);
理由:索引列上使用mysql的内置函数,索引失效;如果索引列不加内置函数,索引还是会走的。
28、应尽量避免在 where 子句中对字段进行表达式操作,这将导致系统放弃使用索引而进行全表扫
反例:
select * from user where age-1 =10;
正例:
select * from user where age =11;
理由:虽然age加了索引,但是因为对它进行运算,索引直接迷路。
29、Inner join、left join、 right join,优先使用Inner join,如果是left join,左边表结果尽量小
30、应尽量避免在 where 子句中使用!=或 >操作符,否则将引擎放弃使用索引而进行全表扫描。
31、使用联合索引时,注意索引列的顺序,一般遵循最左匹配原则
反例:
select * from user where age = 10;
正例:
//符合最左匹配原则select * from user where userid=10 and age =10;//符合最左匹配原则select * from user where userid =10;
理由:联合索引不满足最左原则,索引一般会失效,但是这个还跟Mjsql优化器有关的。(案例表结构:userid在age之前)
32、对查询进行优化,应考虑在 where 及orderby 涉及的列上建立索引,尽量避免全表扫描
33、如果插入数据过多,考虑批量插入
34、在适当的时候,使用覆盖索引
35、慎用distinct关键字
36、删除冗余和重复索引
37、如果数据量较大,优化你的修改/删除语句38、where子句中考虑使用默认值代替null
39、不要有超过5个以上的表连接理由:
- (1)连表越多,编译的时间和开销也就越大。
- (2)把连接表拆开成较小的几个执行,可读性更高。
- (3)如果一定需要连接很多表才能得到数据,那么意味着糟糕的设计了。
40、exist & in的合理利用假设表A表示某企业的员工表,表B表示部门表,查询所有部门的所有员工,很容易有以下SQL:
select * from A where deptld in (select deptld from B);
这样写等价于:
先查询部门表
B select deptld from B
再由部门deptld,查询A的员工
select * from A where A.deptld=B,deptld
可以抽象成这样的一个循环
List<> resultSet ; for(int i=0;i<B.length;i++){ for(int j=0;j<A.length;j++)i if(A[i].id==B[jl.id){ resultSet.add(Alil); break; } } }
可以用exists实现一样的查询功能
select * from A where exists (select 1 from B where A.deptld= B.deptld);
exists查询的理解就是,先执行主查询,获得数据后,再放到子查询中做条件验证,根据验证结果(true或者false)来决定主查询的数据结果是否得意保留。
41、尽量用 union all 换 union
42、尽量使用数字型字段,若只含数值信息的字段尽量不要设计为字符型。
43、索引不宜太多,一般5个以内
44、尽量避免向客户端返回过多数据量
45、索引不适合建在有大量重复数据的字段上,如性别这类型数据库字段。
46、当在SOL语句中连接多个表时,请使用表的别名,并把别名前缀于每一列上,这样语义更加清晰。
47、尽可能使用varchar/nvarchar 代替char/nchar
48、为了提高group by 语句的效率,可以在执行到该语句前,把不需要的记录过滤掉。
49、如何字段类型是字符串,where时一定用引号括起来,否则索引失效。
50、使用explain 分析你SOL的计划日常开发写SQL的时候,尽量养成一个习惯。用explain分析一下你写的SQL,尤其是走不走索引这一块。
explain select * from user where userid=10086 or age =18;