简介:Oracle 数据库设计开发规范是一份面向数据库设计人员、后端开发工程师与 DBA 的实用文档,用于解决建库建表、命名混乱、权限失控、备份缺失等常见工程问题,适合从入门到进阶的 Oracle 开发者对照落地。资源包共 1 个文件,为 doc 格式的规范文档,压缩包约 245KB,内容按章节组织,便于逐条查阅与团队内部培训使用。文档共 51 页,涵盖范围与简介、数据库整体设计规范、数据库对象设计规范、数据库安全规范、数据备份与恢复规范等模块,并细化了表、字段、命名、权限分配、数据加密、审计日志、编码规则、测试验证与日常维护等具体条款。目前已有 624 人学习下载,读者可据此建立统一的数据库设计标准,减少返工与安全隐患,提升系统稳定性、可扩展性与开发协作效率。
1. 从一次 SQL 审核翻车说起:这份 Oracle 设计规范到底管什么
上周帮一个朋友看他们新上的业务系统,开发环境跑得好好的,上线第二天就收到告警:一张核心表的全表扫描把 IO 打满了。翻了下建表语句,VARCHAR2(4000)的字段建了一堆,主键用的是业务流水号字符串,连个像样的索引都没有。这种问题在 Oracle 里太常见了——语法能跑通,不代表设计过关。这份《Oracle 数据库设计开发规范》就是冲着这类问题来的,它不是语法手册,而是一套把命名、数据类型、索引、SQL 写法、权限管理全部框死的约束文档,适合做后台开发的、做 DBA 的、以及需要评审建表语句的技术负责人。你拿到手之后,能直接把它当成团队内部的 check list 用,建表前过一遍,能省掉后面大量的返工和性能排查。
2. 命名与数据类型:规范里最容易吵起来的两块
2.1 命名规范为什么值得单独拎出来
很多人觉得命名是小事,但 Oracle 的标识符有硬性长度限制——12.2 之前是 30 字节,之后虽然放宽到 128 字节,可实际项目里没人真去用满。规范里一般会约定:表名用业务模块前缀加下划线,比如ORD_ORDER_MAIN;字段名不用保留字,不用中文拼音缩写;索引名带上表名和字段名,比如IDX_ORD_ORDER_MAIN_USER_ID。这些约定看着琐碎,但真到了几百张表的库里面,没有命名规则就是灾难。我见过一个库,索引名全是IDX_1、IDX_2,后来要删一个废弃索引,查了半天才确认删的是哪个。
命名规范落地的时候,建议直接写进建表模板里。下面这个模板是我自己常用的,把表名、字段名、索引名、注释全部占位好,开发只需要填空:
-- 建表模板:替换 {模块}_{业务} 和字段定义 CREATE TABLE {MOD}_{BIZ}_MAIN ( ID NUMBER(19) NOT NULL, -- 主键,统一用 NUMBER(19) BIZ_CODE VARCHAR2(32) NOT NULL, -- 业务编码,定长用 CHAR,变长用 VARCHAR2 BIZ_NAME VARCHAR2(128) NOT NULL, -- 业务名称 STATUS CHAR(1) DEFAULT '0' NOT NULL, -- 状态位,统一 CHAR(1) CREATE_TIME DATE DEFAULT SYSDATE NOT NULL, UPDATE_TIME DATE DEFAULT SYSDATE NOT NULL, CONSTRAINT PK_{MOD}_{BIZ}_MAIN PRIMARY KEY (ID) ); -- 注释必须补,否则后面谁都不知道字段干嘛的 COMMENT ON TABLE {MOD}_{BIZ}_MAIN IS '业务主表'; COMMENT ON COLUMN {MOD}_{BIZ}_MAIN.BIZ_CODE IS '业务编码,唯一';这段模板里几个点值得说清楚。ID用NUMBER(19)而不是VARCHAR2,是因为数值型主键在 Oracle 里做索引和关联的效率更稳,而且 19 位刚好覆盖NUMBER的精度范围,不会出现隐式转换。STATUS用CHAR(1)而不是VARCHAR2(1),是因为定长字段在行迁移和存储上更可控。CREATE_TIME和UPDATE_TIME用DATE而不是TIMESTAMP,除非业务真需要毫秒级精度,否则DATE足够且更省空间。注释这块别偷懒,Oracle 的COMMENT ON是元数据,后面用USER_COL_COMMENTS一查就能生成数据字典。
2.2 数据类型选型的几个硬边界
规范里对数据类型的约束通常是最细的,因为这块直接决定存储和性能。VARCHAR2的长度不要超过 4000,超过就用CLOB,但CLOB不能建普通索引,查询时要注意。NUMBER不带精度的时候,Oracle 会按最大精度存,容易浪费空间,所以金额字段一般写成NUMBER(18,2)。日期字段统一用DATE,别混用TIMESTAMP和DATE,否则关联查询时会出现隐式转换,索引直接失效。
还有一个容易忽略的点:NULL值在 Oracle 的索引里是不存的。如果某个字段经常用IS NULL查,普通 B 树索引帮不上忙,得考虑位图索引或者函数索引。规范里一般会写一条:业务上非空的字段必须加NOT NULL约束,别指望应用层保证。下面这个查询可以帮你快速找出库里哪些字段没加非空约束:
-- 查当前用户下所有允许为空的字段 SELECT table_name, column_name, data_type, nullable FROM user_tab_columns WHERE nullable = 'Y' AND table_name NOT LIKE 'BIN$%' -- 排除回收站对象 ORDER BY table_name, column_name;跑完这个查询,如果发现核心业务表的BIZ_CODE、STATUS这些字段是Y,那就得回头补NOT NULL。补的时候注意,如果表里已经有NULL数据,直接加约束会报错,得先UPDATE再ALTER TABLE ... MODIFY ... NOT NULL。
3. 索引与 SQL 写法:规范落地时最见功力的地方
3.1 索引不是越多越好,但该建的必须建
索引规范的核心就两条:主键索引自动建,外键字段手动建,高频查询条件按需建。但实际项目里经常走极端——要么一个索引不加,要么每个字段都加。我见过一张 20 个字段的表建了 15 个索引,插入性能直接掉一半。规范里一般会约定:单表索引数量不超过 5 个,组合索引的字段顺序按区分度从高到低排,函数索引要单独评审。
组合索引的顺序特别关键。比如WHERE STATUS = '1' AND CREATE_TIME > SYSDATE - 7,如果建的是(CREATE_TIME, STATUS),那STATUS就用不上索引;建(STATUS, CREATE_TIME)才能两个条件都走索引。这个规则在规范里最好配一个示例,不然开发记不住。下面这个脚本可以帮你检查现有索引的使用情况:
-- 查索引被监控到的使用次数(需要先开索引监控) ALTER INDEX IDX_ORD_ORDER_MAIN_STATUS MONITORING USAGE; -- 过一段时间后查 SELECT index_name, table_name, used, start_monitoring, end_monitoring FROM v$object_usage WHERE used = 'NO';如果某个索引USED一直是NO,而且确认业务上确实不用,就可以考虑删掉。但删之前一定要确认没有隐藏的查询在用,最好先在测试库跑一周。
3.2 SQL 写法的几条红线
规范里对 SQL 的约束通常包括:禁止SELECT *,禁止在WHERE里对索引字段做函数运算,禁止用OR连接不同字段,禁止隐式类型转换。这几条每一条都是血泪教训。SELECT *的问题不只是多取字段,更麻烦的是当表结构变更时,应用层拿到的结果集列数变了,容易出问题。对索引字段做函数运算,比如WHERE TO_CHAR(CREATE_TIME, 'YYYY-MM-DD') = '2024-01-01',索引直接失效,得改成WHERE CREATE_TIME >= TO_DATE('2024-01-01', 'YYYY-MM-DD') AND CREATE_TIME < TO_DATE('2024-01-02', 'YYYY-MM-DD')。
隐式类型转换更隐蔽。比如字段是VARCHAR2,你写WHERE BIZ_CODE = 123,Oracle 会把字段转成数字再比较,索引失效。这种问题在开发环境数据量小的时候看不出来,上线就翻车。规范里最好直接写死:所有字面量必须和字段类型一致,字符串加单引号,数字不加。
-- 错误写法:索引失效 SELECT * FROM ORD_ORDER_MAIN WHERE BIZ_CODE = 123; -- 正确写法:类型匹配,走索引 SELECT ID, BIZ_CODE, BIZ_NAME FROM ORD_ORDER_MAIN WHERE BIZ_CODE = '123';3.3 执行计划怎么看才不白看
规范落地之后,怎么验证 SQL 有没有按预期走索引?看执行计划。但很多人只看TABLE ACCESS FULL就慌了,其实得结合Rows、Cost、Bytes一起看。如果表很小,全表扫描比索引扫描还快,那走全表扫描是合理的。关键看的是Cardinality估算和实际行数的偏差,偏差大说明统计信息过期,得重新收集。
-- 查看执行计划 EXPLAIN PLAN FOR SELECT ID, BIZ_CODE FROM ORD_ORDER_MAIN WHERE STATUS = '1' AND CREATE_TIME > SYSDATE - 7; SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY);看完执行计划,如果发现INDEX RANGE SCAN变成了INDEX FULL SCAN,或者Cost突然飙高,先别急着改 SQL,跑一下DBMS_STATS.GATHER_TABLE_STATS收集统计信息,很多时候是统计信息不准导致的。
4. 避坑与排查:规范执行中最容易翻车的五个点
4.1 现象:建表时用了VARCHAR2(4000),插入报错 ORA-01461
原因:VARCHAR2(4000)在NLS_LENGTH_SEMANTICS为BYTE时,实际能存的字节数受字符集影响,中文环境下 4000 字节可能只够存 1000 多个汉字。解决:建表时明确用VARCHAR2(4000 CHAR),或者把长度降到 2000 以内,超过就用CLOB。
4.2 现象:主键用SYS_GUID()生成,查询越来越慢
原因:SYS_GUID()生成的是 32 位十六进制字符串,作为主键时索引块分裂频繁,而且字符串比较比数字慢。解决:主键统一用序列SEQ_XXX.NEXTVAL生成数值型 ID,或者用IDENTITY列(12c 及以上)。
4.3 现象:DELETE大量数据后表空间没释放
原因:DELETE只是标记删除,高水位线没降,全表扫描还是扫那么多块。解决:确认业务允许后,用TRUNCATE TABLE或者ALTER TABLE ... MOVE重建表,再ALTER INDEX ... REBUILD重建索引。
4.4 现象:COUNT(*)在亿级表上跑几分钟不出结果
原因:Oracle 的COUNT(*)要扫全表或者最小的索引,数据量大就是慢。解决:如果只是要判断有没有数据,用SELECT COUNT(*) FROM (SELECT 1 FROM TABLE_NAME WHERE ROWNUM = 1);如果真要精确计数,考虑建物化视图或者用/*+ PARALLEL */提示。
4.5 现象:应用连接池报 ORA-00060 死锁
原因:两个会话互相等待对方持有的锁,通常是UPDATE顺序不一致导致的。解决:规范里约定所有UPDATE按主键排序执行,或者用SELECT ... FOR UPDATE NOWAIT快速失败,避免长时间等待。
5. 把规范变成自动化检查:几个能省人力的脚本
规范写在文档里,靠人肉 review 迟早会漏。我一般会把几条硬性规则写成 SQL 脚本,定期跑一遍,输出不合规的对象清单。比如检查没有注释的表、没有主键的表、索引过多的表、字段类型不合理的表。下面这个脚本查的是没有主键的表:
-- 查没有主键的表 SELECT t.table_name FROM user_tables t WHERE NOT EXISTS ( SELECT 1 FROM user_constraints c WHERE c.table_name = t.table_name AND c.constraint_type = 'P' ) AND t.table_name NOT LIKE 'BIN$%';跑出来如果有核心业务表,那就得补主键。补的时候注意,如果表里已经有重复数据,得先清理。另一个常用脚本是查索引数量超过 5 个的表:
-- 查索引数量超过 5 个的表 SELECT table_name, COUNT(*) AS idx_count FROM user_indexes WHERE table_name NOT LIKE 'BIN$%' GROUP BY table_name HAVING COUNT(*) > 5 ORDER BY idx_count DESC;这两个脚本我一般放在一个check.sql里,每周跑一次,输出结果直接发给开发负责人。时间长了,大家建表前会自己先跑一遍,省得被点名。
还有一个进阶用法:把规范里的命名规则写成正则,用REGEXP_LIKE去匹配表名和字段名。比如规定表名必须是大写字母加下划线,且以模块前缀开头:
-- 查不符合命名规范的表名 SELECT table_name FROM user_tables WHERE NOT REGEXP_LIKE(table_name, '^[A-Z]+_[A-Z_]+$') AND table_name NOT LIKE 'BIN$%';这个脚本能揪出那些用拼音缩写、用数字开头、或者带特殊字符的表名。跑完把结果整理成清单,在团队周会上过一遍,比口头强调管用得多。
从那以后我每次接手新库,第一件事就是跑一遍这几个检查脚本,把不合规的对象列出来,再决定是先改还是先忍着。规范这东西,写出来只是第一步,能自动查、能落地,才算真用起来。希望帮到你。
本文还有配套的精品资源,点击获取