孤尽列出了11条MySQL的语句规范,但实际我们项目的使用中远远不止这些,积少成多吧,尽力避免这些事情对于码农来对于项目来说都是一件好事。闲言少叙,开始:
1.[强制] 不要使用count(列名)或count(常量)来替代count(*),count(*)是SQL92定义的标准统计行数的语法,跟数据库无关,跟NULL和非NULL无关。
说明:count(*)会统计值为NULL的行,count(列名)不会统计此列为NULL值的行。
老四附言:
MySQL的聚集函数:
- avg():通过对表中行数计数并计算特定列值之和,求得该列的平均值。
- count():统计结果行数
- max():返回指定列中的最大值,要求指定列名
- max():与max()相反
2.[强制] count(distinct column)计算该列除NULL之外的不重复行数,注意count(distinct column1, column2)如果其中一列全为NULL,那么即使另一列有不同的值,也返回为0;
老四附言:
MySQL学艺尚浅,这种情况一般都是用group by的,建议你也这么做。
3.[强制] 当某一列的值全是NULL时,count(col)的返回结果为0,但sum(col)的返回结果为NULL,因此使用sum()时需注意NPE(空指针异常)。
正例:可以使用如下方式来避免sum()的NPE问题:SELECT IF(ISNULL(SUM(g)),0,SUM(g));
老四附言:
也可以根据sql的实际情况在程序中来判断NPE问题。
4.[强制] 使用ISNULL()来判断是否为NULL值。
说明:NULL与任何值的直接比较都为NULL。
- NULL<>NULL的返回结果是NULL,而不是false。
- NULL=NULL的返回结果是NULL,而不是true。
- NULL<>1的返回结果是NULL,而不是true。
老四附言:
MySQL中和NULL值判断相关的三个函数:
- ISNULL():判空
- IFNULL(expr1,expr2):假如expr1不为NULL,则IFNULL()的返回值为expr1;否则其返回值为expr2。IFNULL()的返回值是数字或是字符串,具体情况取决于其所使用的语境。
- NULLIF(expr1,expr2):如果expr1=expr2成立,那么返回值为NULL,否则返回值为expr1。和CASE WHEN expr1=expr2 THEN NULL ELSE expr1 END一样。
5.[强制] 在代码中写分页查询逻辑时,若count为0应直接返回,避免执行后面的分页语句。
老四附言:
MySQL分页sql:
1 |
select * from tableName where 条件 limit (第几页-1)*页面容量, 页面容量; |
Oracle分页sql:
1 |
select * from (select a.*,rownum rn from (select * from emp) a where rownum<=10) where rn>5; |
6.[强制] 不得使用外键与级联,一切外键概念必须在应用层解决。
说明:以学生和成绩的关系为例,学生表中的student_id是主键,那么成绩表中的student_id则为外键。如果更新学生表中的student_id,同时触发成绩表中的student_id更新,即为级联更新。外键与级联更新适用于单机低并发,不适合分布式、高并发集群;级联更新是强阻塞,存在数据库更新风暴的风险;外键影响数据库的插入速度。
老四附言:
阻塞:因为不同锁之间的兼容性关系,在有些时刻一个事务中的锁需要等待另一个事务中的锁释放它占有的资源,这就是阻塞。
外键为什么影响插入速度和性能:
- 数据库需要维护外键的内部管理
- 外键等于把数据的一致性事务实现,全部交给数据库服务器完成
- 有了外键,当做一些涉及外键字段的增删改操作之后,需要触发相关操作去检查,而不得不消耗资源
- 外键还会因为需要请求对其他表内部加锁而容易出现死锁情况
7.[强制] 禁止使用存储过程,存储过程难以调试和扩展,更没有移植性。
老四附言:
首先引用一下孤尽在知乎上面的解释:
解释一下这个事情:曾经写过近1200行的存储过程,没有办法断点,下层数据结构只是稍微变动,根本无法找到出错点,只是提示一下说:ERROR:1064啥的。在数据库迁移的时候,由于数据库版本变更,居然存储过程无法执行。另外,业务上需要扩展一下,那就是灾难性的啊。没想到,我觉得毫无争议的这一条,反而成了一个最大的争议点。存储过程只是单机时代的产物,并不适合互联网时代。
即使从事互联网的我们,其实早就不用存储过程了,但是在面试的时候或者dba在出报表或者统计的时候,存储过程用的也是蛮多的,所以我们要基本了解一下:
存储过程的特点、优点:
- 批处理
- 简单
- 安全
- 高性能
至于缺点,孤尽已经通过血的教训描述了,使用存储过程的命令:
1 2 |
# 调用名为 productpricing 的存储过程 call productpricing(@pricelow, @pricehigh, @priceaverage); |
创建存储过程:
1 2 3 4 5 6 |
CREATE PROCEDURE `crashcourse`.`productpricing`(OUT pl DECIMAL(8,2),OUT ph DECIMAL(8,2),OUT pa DECIMAL(8,2)) BEGIN SELECT MIN(prod_price) INTO pl FROM products; SELECT MAX(prod_price) INTO ph FROM products; SELECT AVG(prod_price) AS priceaverage FROM products; END; |
删除存储过程:
1 |
drop procedure productpricing; |
8.[强制] 数据订正(特别是删除、修改记录操作)时,要先select,避免出现误删除,确认无误才能执行更新语句。
老四附言:
这里一定要补充一点,一定要这么做!一定要这么做!一定要这么做!除此以外,尽量使用sql进行数据订正操作,不要使用工具按钮或者快捷键进行数据订正!尽量使用sql进行数据订正操作,不要使用工具按钮或者快捷键进行数据订正!尽量使用sql进行数据订正操作,不要使用工具按钮或者快捷键进行数据订正!(一个因为不使用sql而删库的站长遗言…)
9.[推荐] in操作能避免则避免,若实在避免不了,需要仔细评估in后边的集合元素数量,控制在1000个之内。
老四附言:
因为查找次数并不会随着in字段是否有索引而减少,所以应尽量避免使用in查询。
10.[参考] 如果有全球化需要,所有的字符存储与表示,均以utf-8编码,注意字符统计函数的区别。
说明:
SELECT LENGTH(“轻松工作”); 返回为12
SELECT CHARACTER_LENGTH(“轻松工作”); 返回为4
如果需要存储表情,那么选择utf8mb4来进行存储,注意它与utf-8编码的区别。
老四附言:
我看网上都是这么写的,我也就随手复制过来了,不知道原创是谁。
mysql支持的 utf8 编码最大字符长度为 3 字节,如果遇到 4 字节的宽字符就会插入异常了。三个字节的 UTF-8 最大能编码的 Unicode 字符是 0xffff,也就是 Unicode 中的基本多文种平面(BMP)。也就是说,任何不在基本多文本平面的 Unicode字符,都无法使用 Mysql 的 utf8 字符集存储。包括 Emoji 表情(Emoji 是一种特殊的 Unicode 编码,常见于 ios 和 android 手机上),和很多不常用的汉字,以及任何新增的 Unicode 字符等。
11.[参考] TRUNCATE TABLE比DELETE速度快,且使用的系统和事务日志资源少,但TRUNCATE无事务且不触发trigger,有可能造成事故,故不建议在开发代码中使用此语句。
说明:TRUNCATE TABLE在功能上与不带WHERE子句的DELETE语句相同。
老四附言:
自己忘记什么时候总结的关于drop、truncate和delete的区别:
delete:
- delete是DML(Data Manipulation Language,数据操纵语言),执行delete操作时,每次从表中删除一行,并且同时将该行的的删除操作记录在redo和undo表空间中以便进行回滚(rollback)和重做操作,但要注意表空间要足够大,需要手动提交(commit)操作才能生效,可以通过rollback撤消操作。
- delete可根据条件删除表中满足条件的数据,如果不指定where子句,那么删除表中所有记录。
- delete语句不影响表所占用的extent,高水线(high watermark)保持原位置不变。
truncate:
- truncate是DDL(Data Definition Language,数据库模式定义语言),会隐式提交,所以,不能回滚,不会触发触发器。
- truncate会删除表中所有记录,并且将重新设置高水线和所有的索引,缺省情况下将空间释放到minextents个extent,除非使用reuse storage,。不会记录日志,所以执行速度很快,但不能通过rollback撤消操作(如果一不小心把一个表truncate掉,也是可以恢复的,只是不能通过rollback来恢复)。
- 对于外键(foreignkey )约束引用的表,不能使用 truncate table,而应使用不带 where 子句的 delete 语句。
- truncatetable不能用于参与了索引视图的表。
drop:
- drop是DDL,会隐式提交,所以,不能回滚,不会触发触发器。
- drop语句删除表结构及所有数据,并将表所占用的空间全部释放。
- drop语句将删除表的结构所依赖的约束,触发器,索引,依赖于该表的存储过程/函数将保留,但是变为invalid状态。
更博不易,如果觉得文章对你有帮助并且有能力的老铁烦请赞助盒烟钱,点我去赞助。抱拳。