SQL 书写规范有哪些?

规范 反例 正例 说明
禁止 select * 写法 select * from tableA select colA,colB from tableA Select * 由于没有明确列名,如果表格有DDL操作,会导致不可预料的返回结果集导致程序报错;同时select * 会返回需求以外的数据结果,额外增加查询成本
禁止在where条件后,对列进行计算 select colA from tableAwhere dateadd(day,DDJSJ,1)>getdate() select colA from tableAwhere DDJSJ>dateadd(day,getdate(),-1) where后对列进行计算的写法会导致索引失效,并且每次查询均需要对数据逐行计算才能得到结果,效率非常低下
多表连接的查询每个列必须写明列的来源 select col1,col2 from tableA join tableB on a.id =b.id select a.col1,b.col2 from tableA join tableB on a.id =b.id 明确列的来源可以避免当表发生DDL时出现同名列而无法解析的情况,同时明确的列来源可以缩短SQL语句解析的时间提高效率
禁止使用过时的SQL写法,例如*= select colA from tableA a,tableB b where a.id=b.id select colA from tableA a join tableB b on a.id=b.id 不标准的SQL写法会在未来对数据库进行升级或者更换数据库产品时造成报错,同时也会增加代码理解成本,应着手开始进行修改
禁止在查询的列中写子查询 select a.id,(select xxx from tableB b where a.id=b.id) as xxx from tableA a select a.id,b.xxxfrom tableA a join tableB b on a.id=b.id 数据库应采用结果集思维来进行思考,在列中写子查询会导致有多少行数据就查询多少次子查询,性能非常低下
建表时不同属性的值必须单独分列存放,禁止多属性仅存放在同一列中 将几个属性存在同一列中后在查询时实时计算 各属性单独存放,再使用一列存储混合属性,查询时where条件命中单一属性列,返回混合列或需要的列 多属性存放在同一个列中本身就属于违反数据库范式的操作,这样存储也非常不利于查询,会导致需要某个属性时必须对列进行计算,且查询一次就需要计算一次,性能非常低下
表名与列名均应避免数据库关键字 create table DESC(colA int) 使用关键字作为表名称或列名称首先容易出现书写错误,另外会增加语句解析成本
字符类数据长度如果数据长度为定长的,请使用char/nchar类型 varchar/nvarchar为变长类型,会额外使用2字节长度存储当前数据长度,所以如果数据长度为定长,请直接使用char/nchar
单表行数三年内超600W才考虑分表,如果预估3年内数据量达不到该级别,请勿在设计时就进行分表;预估能够达到该数据量的请与DBA沟通分表方案 分表过多在查询进行join时会导致SQL解析成本增加,且解析部分无法进行调优
存储过程、函数、触发器等必须要有明确的注释,方便后期维护 /**** createTime:2020-08-20 归属:收费业务xxxx子业务 负责人:xxx 描述:xxxxxxxxxx。。。。。 ****/
对于配置类型的表,在程序上配置缓存,严禁频繁的读取配置类型表 太过于频繁的查询某个表,会造成数据库热点问题,阻塞会严重浪费服务器资源
大批量更新删除数据需要分割成多次小事务,单次之间添加waitfor delay while(1=1) begin UPDATE Top(500) A SET a.colA=xxx from tableA A if(@@ROWCOUNT<500) begin break end waitfor delay ‘00:00:01’ end 单次大事务一方面是会导致长时间锁定资源从而阻塞其他进程,另一方面是事务过大会让数据库日志文件压力更大,还有就是如果中途出现错误,回滚更难
建表时必须有主键和索引,索引的正确性可以找DBA评估 没有聚集索引的表为堆表,在性能上会非常差
变量取值时必须明确top 1,否则必须明确使用了聚合函数 如果查询结果在意料之外,不写top或者聚合函数会导致查询报错
定义游标推荐指定关键字 声明局部、静态、只能向后滚动的、只读的游标;游标消耗更小 DECLARE xxx_cursor CURSOR LOCAL STATIC FORWARD_ONLY READ_ONLY
insert语句必须显式指定列名,禁止不指定列名的insert语句 当表结构出现修改时,不指定列名的语句会报错
表连接时将选择条件加到where之后,禁止加在on条件后(特殊要求除外)
所有对象必须按照规范命名 如果索引列过多,可只选取前三列加入到命名 主键命名规则: PK_表名默认值约束命名规则: DF_表名_列名聚集索引命名规则: CIX_表名_首列列名[次列列名]非聚集索引命名规则: IX_表名_首列列名[次列列名]标量函数:SF[动作][对象] 表值函数:TF[动作][对象] 规范的命名在后续进行调优或维护管理的时候会方便很多,可以通过拼接SQL的形式批量管理同类表和不同实例上的相同表,如果不进行规范命名则只有每个都独立维护,会极大增加维护工作量。 同时规范命名可以仅通过名称就确定对象的用途和范围,提高维护效率。
尽量避免使用自定义表值函数,如果可以使用存储过程替代,内联表值函数中必须显式定义返回的表结构 函数的限制比存储过程多,例如只能返回一个变量、不能使用临时表等等
禁止在存储过程的循环外使用事务,这会导致大事务问题(特殊情况请说明) 万一循环超出预期,会导致事务一直不结束,造成大事务问题
查询使用top时必须指定排序,否则有可能会因为排序问题引发业务错误 不指定排序时,查询出来的数据永远都是没有排序的,这会导致业务错误
小数类型的字段用decimal/numeric,禁用float float和 double在存储的时候,存在精度损失的问题,很可能在值的比较时,得到不正确的结果。如果存储的数据范围超过 decimal的范围,建议将数据拆成整数和小数分开存储