sql开发规范
SQL编写规约
SQL编写总原则声明:
在没有把握或者根本没意识时,遵守规约可以保障你的程序不会“越界”。
所以规约并不代表一种最优方案或者某种教条。
当你相当有把握,明确知道自己在做什么的时候,请读者使用自己以为合适的SQL,且时刻准备为你的SQL负责。
-
无论是单表查询还是多表联查,不许有大结果集的操作。
-
无论是单表查询还是多表联查,小心使用排序等耗性能的语句。
例如:order by , group by , distinct , union等 -
无论是单表查询还是多表联查,查看执行计划,确认能用索引的地方必须走到索引。
-
开发和测试库的数据量有限,开发和测试过程中,无论功能实现还是执行计划,必须考虑生产库庞大的数据量。
-
需求可以妥协,修改,拆分重构。性能有一条红线不能逾越。
powered by wanglifeng https://www.cnblogs.com/wanglifeng717
强制类规约
powered by wanglifeng https://www.cnblogs.com/wanglifeng717
强制类规约,请务必遵守
【强制】禁止使用select * ,需要什么字段,显式表示出来。
错误写法:select * from table;
正确写法:select t.id, t.name,t.age from table1 t;
【强制】禁止使用不加约束条件的查询,必须有where过滤条件(除非明确现在及未来数据量级都很小可控)
错误写法:select t.id from table1 t;
正确写法:select t.id from table1 t where t.id<=30;
正确写法:select t.id from table1 t where t.id<=30 and rownum<50;(如id<30的量也很多)
正确写法:select t.id from table1 t where rownum<50;(oracle推荐)
正确写法:select t.id from table1 t where limit 50;(mysql推荐)
【强制】insert语句必须明确指定列名。
错误写法:insert into tbl_pay_order_info values(101803309908713,101803309983716,91);
正确写法:insert into tbl_pay_order_info (main_orderid,pay_orderid,pay_channel) values (1018033099483713,1018033099083716,91);
【强制】组合索引的顺序强制等值查询列在前,范围查找列在后
错误写法:create index idx_t on t(age,gender)
Select t.name,t.address,t.age from t where t.age>20 and t.gender=’boy’
正确写法:create index idx_t on t(gender,age)
Select t.name,t.address,t.age from t where t.age>20 and t.gender=’boy’
例如:对于如下组合索引,各种查询写法效率
CREATE INDEX B_C_D_IDX ON DATA_TABLE(B,C,D);
【强制】批量更新语句的事务不能太大。逐条commit不合适,更新1000条以上commit一次也不合适,推荐500左右
【强制】禁止不加过滤条件的全表数据排序。
尽量减少order by和group by排序操作,distinct排重也会涉及到排序,有些排序操作可以由应用程序完成。如必须使用排序操作,请遵循如下规则:
1.排序列上有索引
2.如结果集不需要唯一,使用union all代替union
3.避免大结果集的排序,用where条件限制
错误写法:select * from t order by t.pay_order_id;
正确写法:select * from t where t.pay_order_id<101803300910017574 order by t.pay_order_id;【*注】--至少要过滤成小结果集
【强制】oracle 的rownum和order by的优先级,要分情况。执行结果,取决于执行计划。
强制用括号分层,区分优先级
有隐患写法:
select * from s where rownum<4 order by id ;--局部排序结果table full scan
select id from s where rownum<4 order by id ;--执行计划index full scan全局排序结果
正确写法:
select id from(select id from s order by id ) where rownum<4;--全局排序结果。【*注】 实际运用,内层结果集不能太大,加where条件限制,且要利用索引抵消order by的开销。
实例:批上送平时每天运行没问题,中断一段时间,量积累大了出现问题
select MAIN_ORDER_ID,CREATE_TIME from (
SELECT ROWNUM r, MAIN_ORDER_ID,CREATE_TIME
FROM TBL_SPTCC_TRANSACTION
WHERE ROWNUM <= 1000
and CARD_SEQ_NO = -1
ORDER BY CREATE_TIME )
where r > 0
【*注】 这个语句先拿1000条,后进行排序,局部排序结果。
【强制】超过三个表慎用join。需要join的字段,数据类型必须绝对一致;多表关联查询时,保证被关联的字段需要有索引;即使双表 join 也要注意表索引、SQL 性能;跨表联查必须添加where条件筛选控制结果集,结果集不能太大
错误写法:select t.id t.name from t,n where t.id = n.id;
正确写法:select t.id t.name from t,n where t.id = n.id and t.status=3;
正确写法:select t.id t.name from t,n where t.id = n.id and t.status=3 and rownum<50;
(有可能status=3的条数依然很多,oracle用rownum控制,mysql用limit)
正确写法:select t.id t.name from t,n where t.id = n.id and rownum<50;
【强制】严禁使用左模糊或者全模糊。
错误写法:select * from table t where t.name like '%df%'
正确写法:1.改需求
2. select * from table t where t.name like 'cdf%'
【强制】大数据量的表,必须分页拿数据,禁止全量拿数据,一次500-1000条合适
【强制】oracle经典三层select分页语句,如果数据量大的时候慎用,越往后分页越慢。(开发测试库数据量不足体现不出来)
最内层建议:最好可以加where条件控制结果集
SELECT *
FROM (SELECT TA.*, ROWNUM ROW_NUM
FROM (select UUID, pay_order_id
from t
order by pay_order_id) TA
WHERE ROWNUM <= 100)
WHERE ROW_NUM > 0;
【强制】不要使用 count(列名)或 count(常量)来替代 count(*),count(*)是 SQL92 定义的 标准统计行数的语法,跟数据库无关,跟 NULL 和非 NULL 无关。
说明:count(*)会统计值为 NULL 的行,而 count(列名)不会统计此列为 NULL 值的行。
【强制】多表连接时,使用表的别名来引用列
示例:Select s.name, s.age ,t.address,t.phone from student s ,teacher t where s.teacher_id=t.id and t.age=21
【强制】尽量避免对索引列进行计算,或者对列进行函数操作然后查询(会使索引失效)
错误写法:**select t.id from test t where upper(t.nick) = :1
正确写法: select t.id from test t where t.nick = upper(:1)
【强制】更新时,如果涉及状态机的操作,必须加上前置状态校验,防止多线程导致的状态更新错乱(乐观锁机制)。
错误写法:
1.select t.status from t where t.id=1; --第一步校验id=1的记录状态是否为2,如果为2则 更新状态机 status=3
2.update t set t.status=3 where t.id=1; --多线程的情况下,可能status已经被修改了。
正确写法:
1.select t.status from t where t.id=1;
2.update t set t.status=3 where t.id=1 and t.status=2; --加入前置状态校验
【强制】建表语句,如业务字段不为空,或者该字段要作为查询条件,将来要建立索引,则必须添加非空约束。
【强制】如果表有分区,where条件必须带上分区条件
例如:tbl_pay_order_info按照create_time时间进行了分区,则必须加where create_time限制条件。
【强制】对于频繁甚至大并发访问的表,无论数据量多少,哪怕只有2条数据,都需要根据相关SQL建立索引。
【强制】跨表联查,如果无业务需求,禁止使用left/right join,推荐使用where条件形式。
推荐类规约
powered by wanglifeng https://www.cnblogs.com/wanglifeng717
推荐类规约只做推荐,请根据需要接受。
【推荐】需要建立索引的列,如果满足唯一索引条件,尽量建立唯一索引。
【推荐】使用group by分组时。能用分组前筛选就尽量用分组前筛选(有时候只能用having)
不推荐写法:select count(*),department_id from table group by department_id
having department_id>20
推荐写法: select count(*),department_id from table where department_id>20
group by department_id
【推荐】代码中SQL语句大小写统一,禁止大小写混杂。
错误写法:SELECT t.id, t.name,t.AGE from table t;
正确写法:SELECT T.ID,T.NAME,T.AGE FROM TABLE t;
正确写法:select t.id,t.name,t.age from table t;
【推荐】bitmap,索引慎用。在OLAP数据仓库类型适合,如即席查询,查询速度非常快。位图索引适用在大量读的场合,但不适合大量写的环境,特别是并发写的环境。因为当一个index entry 被修改时,这个 index entry 指向的所有行都会被锁,oracle 无法锁住单独的 bit,而是锁住整个 bitmap index entry。因此一个 update 可能导致几百行被锁。因此位图索引在OLAP 系统中较常见,而 OLTP 系统中几乎不用。(如控制台,数据已经同步到独立的库中,很少并发和更新,主要是查可以考虑用其加速,效率非常高)
【推荐】单条语句不宜过长,子查询嵌套不宜超过3层
【推荐】 in 操作能避免则避免,若实在避免不了,需要仔细评估 in 后边的集合元素数量,控制在 1000 个之内;大于1000个,可以用exists代替in 用not exists 代替 not in。
不推荐写法:select t.id,t.name from t where id in(select id from n where n.id=t.id )
推荐写法:select t.id,t.name from t where exists(select 'A' from n where n.id=t.id)
推荐写法:select t.id,t.name from t where id in('1','8','9','3')
【推荐】 每条语句都要查看执行计划
【推荐】避免对索引列用not 或者 != 。索引列用 not 和索引列用函数一样,会走全表扫描。【*注】 例外:为了限定索引列属性 where order_id is not null 不算
索引是快速告诉你有什么数据,而不是告诉你没有什么数据
【推荐】外键列最好建索引
例如:student表 teacher表,一个老师带多个学生,所以学生表有teacher_id这个外键字段,则teacher_id字段最好建立索引 。
例如 select * from student s,teacher t whre s.teacher_id=t.id and t.name=’曾小贤’;
查询某个老师名下有哪些学生的时候,学生表作为被驱动表(name字段有索引,老师表是驱动表),teacher_id作为连接条件是需要索引的。
【推荐】推荐每个表有create_time 和last_update_time两个字段,可以为日后分表,清理数据等操作提供依据,不做强制要求
参考类规约
powered by wanglifeng https://www.cnblogs.com/wanglifeng717
参考类规约只做参考,拓展使用。
【参考】【*注】开发禁用。用truncate 代替delete 注意:truncate 无法回滚,且无法使用where条件。仅适用于大量清理数据。
delete 比较慢,耗性能,日志非常多,产生空间碎片。尤其大量数据删除时,尤其明显
参考:delete from t
参考:truncate table t
【参考】函数索引场景适合可以使用,性能肯定比B+树索引低一点,但查询性能提升了,需要平衡。
函数索引在索引块中保存的是函数的计算结果,固化函数计算结果,提升性能。
select ename, hiredate
from emp
where my_soundex(ename) = my_soundex('Kings')
如果没有函数索引,假如有 n 行,会调用 my_soundex 函数 n 次。如果有,则只需 1 次。
另外一个应用,在某些行上面建索引而忽略其他行,以节省空间。某些情景下可代替位图索引,比位图索引有更好的并发行,而且空间也小。例如processed_flag只有两个值,大部分都是’Y’,少部分是’N’,为了快速找到N。
建表
create table z
(id number primary key,
processed_flag varchar2(1),
payload varchar2(20)
)
processed_flag 只有两种值 'Y' or 'N'
只需要对该列的'N'值建索引
create index t_idx on z( decode(processed_flag, 'N', 'N'))
因为decode语法 DECODE(value, if1, then1, if2,then2, if3,then3, . . . else )
当值为'N'时返回'N',非'N'时返回NULL,而B*树索引不为NULL值做键。
【参考】 在一个表中剩余的库存中获取一个可用的库存
SELECT * FROM tbl_bts_goods_coupon WHERE STATUS =1;
上述这种写法当并发时,大家取到的可能是同一条库存,最终事务提交时导致只有一条成功。
此时应从设计上避免而不应靠数据库,如果确实靠数据库,下面提供一种sql参考,核心是在合法状态的库存集合中随机获取一个。
本文来自博客园,作者:wanglifeng,转载请注明原文链接:https://www.cnblogs.com/wanglifeng717/p/15846677.html