SQL优雅编码总结

1 篇文章 0 订阅

1.

不要使用 count(列名)或 count(常量)来替代 count(),count()是 SQL92 定义的,标准统计行数的语法,跟数据库无关,跟 NULL 和非 NULL 无关。
说明:count(*)会统计值为 NULL 的行,而 count(列名)不会统计此列为 NULL 值的行。

2.

count(distinct col)
计算该列除 NULL 之外的不重复行数,注意 count(distinct col1, col2) 如果其中一列全为 NULL,那么即使另一列有不同的值,也返回为 0。

3.

当某一列的值全是 NULL 时,count(col)的返回结果为 0,但 sum(col)的返回结果为NULL,因此使用 sum()时需注意 NPE 问题
避免 sum 的 NPE 问题:SELECT IF(ISNULL(SUM(g)),0,SUM(g)) FROM table;

4.

在代码中写分页查询逻辑时,若 count 为 0 应直接返回,避免执行后面的分页语句。

5.

使用 ISNULL()来判断是否为 NULL 值。
说明:NULL 与任何值的直接比较都为 NULL。
1) NULL<>NULL 的返回结果是 NULL,而不是 false。
2) NULL=NULL 的返回结果是 NULL,而不是 true。
3) NULL<>1 的返回结果是 NULL,而不是 true。

6.

不得使用外键与级联,一切外键概念必须在应用层解决。
说明:以学生和成绩的关系为例,学生表中的 student_id是主键,那么成绩表中的 student_id
则为外键。如果更新学生表中的 student_id,同时触发成绩表中的 student_id 更新,即为级联更新。外键与级联更新适用于单机低并发,不适合分布式、高并发集群;级联更新是强阻塞,存在数据库更新风暴的风险;外键影响数据库的插入速度。

7.

禁止使用存储过程,存储过程难以调试和扩展,更没有移植性。

8.

in 操作能避免则避免,若实在避免不了,需要仔细评估 in 后边的集合元素数量,控制在 1000 个之内。

9.

事务:
a)@Transactional 事务不要滥用,事务会影响数据库的 QPS,另外使用事务的地方需要考虑各方面的回滚方案,包括缓存回滚、搜索引擎回滚、消息补偿、统计修正等。

b)如果需要事务的支持,在确认使用了 innodb 存储引擎的前提下,在数据库连接时,先关闭自动提交
比如,设定 set auto_commit =0 ;

10.

应用程序里的 SQL 语句,禁止一切 DDL 操作
例: Create table , Drop table , Create database , Drop database ,Alter table ,grant … …
如有特殊需要,必需与 技术专家(如DBA) 协商同意方可使用。

11.

获取当前时间请使用 now(),不要用 sysdate()来代替
这对复制来说是很危险的,会导致主从数据不一致的情况;因为 sysdate,取的是系统主机时间,在 BINLOG 会原文传输,当在应用时会与主库产生差异。

12.

写 SQL 的时候一定要给每个字段指定表名做前缀
比如:
select a.id,a.name from test a;
好处是:
一来带来性能的提升;
二来可以避免一些错误的发生。

13.

在 iBatis 的 SqlMap 文件中绑定变量使用 “#var_name#”表示,替代变量使用“ v a r n a m e var_name varname
所有需要动态 Order By 条件的 Query,在使用替代变量过程中,需要将可能传入的内
容以枚举类写死在代码中,禁止接受任何外部传入内容。

14.

请不要写 select * 这样的代码,指定需要的字段名
几个方面考虑:增加查询分析器解析成本、网络、二级索引(覆盖索引)、增减字段容易与 resultMap 配置不一致。

15.

Mysql 对日期(datetime)允许“不严格”语法
任何标点符都可以用做日期部分或时间部分之间的间隔符。
例如:‘22-12-31 11:30:45’、‘22.12.31 11+30+45’、'22/12/31 113045’和’22@12@31 113045’是等价的。
约定一种所有数据库通用的写法: ‘2009-12-31 11:30:45’

16.

Mysql 的日期与字符是相同的,所以不需要做另外的转换
比如:
Select e.username from employee e where e.birthday >=’1998-12-31 11:30:45’

17.

避免多余的排序。使用 GROUP BY 时,默认会进行排序,当你不需要排序时,可以使用order by null
比如:
Select product,count(*) cnt from crm_sale_detail group by product order by null;

18.

避免在 where 子句中对字段施加函数
a) 通常,不允许在字段上添加函数或者表达式,这样将导致索引失效,如:

错误的写法:select * from iw_account_log where substr(username,1,5)=’abcde’
正确的写法:select * from iw_account_log where username like ’abcde%’

b) 如果是业务要求的除外,但需要和技术经理、技术专家(DBA)等充分讨论;

c) 特别注意,当表连接时,用于连接的两个表的字段如果数据类型不一致,则必须在
一边加上类型转换的函数。

19.

严格要求使用正确类型的变量,杜绝 Mysql 做隐式类型转换的情况

20.全模糊查询无法使用 INDEX,应当尽可能避免

比如:select * from table where name like '%jacky%';

21. “join”、“in”、“not in”、"exsits"和"not exists"的使用

a) 比较 IN,EXISTS,JOIN
	按效率从好到差排序: 
		字段上有索引 : EXISTS, IN, JOIN 
		字段上没有索引: JOIN, EXISTS ,IN

b) Anti-Joins: NOT IN ,NOT EXISTS, LEFT JOIN
	按效率从好到差排序: 
		字段上有索引 : LEFT JOIN, NOT EXISTS, NOT IN
		字段上没有索引: NOT IN, NOT EXISTS, LEFT JOIN

22.表连接规范

a) 所有非外连接 SQL(即 INNER JOIN),请把关联表统一写到 FROM 字句中,关联条件与过滤条件统一写到 WHERE 字句中
b) 出于代码的可读性原因,所有外连接 SQL 语句中,请一律使用 LEFT JOIN,禁用RIGHT JOIN
c) 请注意 LEFT JOIN 字句中,右边位置表的条件书写位置不同的影响:
SELECT A.rolename,A.gmt_create,B.nickname FROM gl_role A LEFT JOIN gl_roledetail B ON A.ID=B.roleid AND B.roleID=2;
±------------±--------------------±---------+
| rolename | gmt_create | nickname |
±------------±--------------------±---------+
| 163.com | 0000-00-00 00:00:00 | test2 |
| sina.com | 0000-00-00 00:00:00 | NULL |
| hotmail.com | 0000-00-00 00:00:00 | NULL |
±------------±--------------------±---------+
SELECT A.rolename,A.gmt_create,B.nickname FROM gl_role A LEFT JOIN gl_roledetail B ON A.ID=B.roleid WHERE B.roleID=2;
±---------±--------------------±---------+
| rolename | gmt_create | nickname |
±---------±--------------------±---------+
| 163.com | 0000-00-00 00:00:00 | test2 |
±---------±--------------------±---------+

23. 表连接分页查询的使用

a) 常规分页语句写法(start:起始记录数,page_offset:每页记录数):
SELECT ID,username FROM gl_user
WHERE username like ‘%@163.com’ ORDER BY M.gmt_create LIMIT start, page_offset;

b) 多表 Join 的分页语句,如果过滤条件在单个表上,需要先分页,再 Join:
低性能写法:
SELECT M.username,P.rolename FROM gl_user M INNER JOIN gl_role P ON
M.ID=P.userid WHERE username like ‘%@163.com’ ORDER BY M.gmt_create LIMIT
start, page_offset;
高性能写法:
SELECT M.username,P.rolename FROM (
SELECT ID,username FROM gl_user WHERE username like ‘%@163.com’
ORDER BY M.gmt_create LIMIT start, page_offset
)M,gl_role P
WHERE M.ID=P.userid;

这样写的前提是关联的表之间记录一一对应,否则可能会返回的记录数目少于或多余
page_offset 的值。

  • 26
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值