简介:《8数据库设计规范》是一份面向Oracle数据库设计人员的规范文档,旨在解决系统设计中命名混乱、字段类型随意、数据完整性难以保证等问题。文档从数据库策略、命名规范、数据模型产出物等方面展开,适用于需要建立统一数据模型的企业项目团队、开发工程师与数据库管理员。资源为单个doc文档,压缩包大小296KB,内容完整,包含编写目的、数据库对象长度策略、字段类型选用原则、表/字段/索引等命名规则,以及常用字段定义示例。已有267人学习/浏览,适合在项目起步阶段或数据库评审时参考使用。文档特别强调OLTP与OLAP分开设计、规范化与性能权衡,并给出VARCHAR2长度建议、金额/税率等字段类型推荐,可帮助团队降低沟通成本、减少设计返工并提升数据库可维护性。
1. 一份 Oracle 数据库设计规范,先解决的是"沟通成本"
做过几年 Oracle 项目的人,多半都见过这样的库:订单表叫orders,另一套系统里叫T_ORD_INFO,第三套系统里直接叫DD01;金额字段有的是NUMBER(10,2),有的是VARCHAR2(20);主键有人用自增,有人用GUID,还有人直接拿业务编码当主键。单看哪张表都没毛病,但把表汇到一起做数据迁移或报表汇总时,光对齐口径就要花掉一两个迭代。这份《8数据库设计规范》的价值不在于定义了某个具体表的字段,而是把从数据库命名、字段类型到产出物管理的一整条链路做了统一约定。它适合三类人:正在做 Oracle 库表结构设计的新手,需要统一多个子系统建模规范的架构师,以及要接手存量库做治理的 DBA——按这套规则走,至少能保证新落地的模型风格一致,可维护性可预期。
2. 对象命名规范:从库名到约束的一整套编码体系
命名规范是这份文档里信息密度最高的部分,几乎每个对象类型都有独立规则。它不只是"取个好名字"的问题,而是通过命名把对象的业务属性、类型属性、层级关系一次性编码进名字里,让人不用打开文档就能读取出大量信息。
2.1 数据库命名:项目简称 + 类型码 + 识别码 + 序号
数据库命名规则是整条命名链的起点。文档给出的公式是:
项目简称 + 数据库类型码 + 识别码 + 序号其中类型码固定为:
| 类型码 | 含义 | 典型用途 |
|---|---|---|
| T | 业务型数据库 | OLTP 在线交易 |
| A | 分析型数据库 | OLAP 报表与分析 |
| H | 历史数据库 | 数据归档与历史查询 |
识别码是环境标识:DEV开发库、TEST测试库、生产库不写识别码。末尾序号只在同类库有多个实例时追加。文档给的例子是出入系统:生产业务库AOCT、开发业务库AOCTDEV、测试业务库AOCTTEST。
我接手过的项目里,常见的坑是同类库扩容时直接在原库名后加_2、_3这类后缀,或者把历史库和业务库混放在同一实例里。按这套规则,历史库固定用项目简称+H+序号的格式,和业务库从命名上就天然隔离,做备份策略和数据生命周期管理时边界清晰得多。
2.2 表空间与表命名:按业务域拆分
表空间规则简洁,主表空间统一用TS_业务规则格式,临时表空间用TS_TMP_业务规则。这里有一个实战里容易被忽视的点:TS_TMP前缀不只是起标识作用,更是在提醒运维同学这块表空间是用来跑排序、去重、建索引的临时数据落盘位置,备份时可以跳过,监控时阈值也要单独设,不能和生产数据表空间共用一套告警线。
表的命名规则区分了业务库和分析库:
业务库: 子系统简称_业务含义 分析库: ODS_业务规则 -- 操作型数据存储区 FACT_业务规则 -- 事实表 DIM_业务规则 -- 维表 MID_业务规则 -- 中间表这在数据仓库建模里是很经典的分层约定。ODS 层落原始数据,DIM 层管理维度,FACT 层存事实,MID 放中间计算结果。从表名直接能判断这张表在数仓里的位置,以及它是否可以直接暴露给报表层查询——MID 开头的中间表通常只是临时加工产物,不应该成为下游报表的长期依赖。
2.3 索引、约束和存储过程:前缀即语义
索引和约束的命名常被忽略,但恰恰是线上事故的高发区域。文档给出的规则是:
| 对象 | 命名格式 | 说明 |
|---|---|---|
| 视图 | VW_子系统简称_业务含义 | 与表名区分,避免混用 |
| 序列 | SEQ_表名 | 一个表一个序列,规则统一 |
| 存储过程 | PRC_子系统简称_业务含义 | 动词开头更佳,如PRC_CRM_SYNC_ORDER |
| 函数 | FUN_子系统简称_业务含义 | 返回值含义要能从名字读出 |
| 索引 | IDX_表名_有关字段 | 不允许自动生成索引名 |
| 主键约束 | PK_表名 | 表名过长时需简化 |
| 外键约束 | FK_表名_字段_被参照表名 | 长度过长时需简化 |
两个容易被忽略的细节:一是索引"不允许自动生成的索引",这是针对建表工具默认行为的,Oracle 的约束会自动创建索引,如果主键名是系统生成的SYS_C0012345,出问题时排查成本极高,统一命名为PK_表名后通过dba_constraints一眼就能定位。
二是外键命名FK_表名_字段_被参照表名里同时包含了"来源"和"去向"。遇到一条锁等待或外键校验失败,不需要去查表结构才知道是哪两张表在联动,名字本身就是线索。
2.4 命名一般原则:长度与保留字
文档里有一条容易踩坑的约束:对象命名长度最好不要超过 18 个字符。Oracle 的对象名上限虽然是 30 字节,但超过 18 字符后,在部分版本的数据字典视图里会截断显示,导出工具也可能出现对齐问题。另外文档在附录里给了完整的保留字清单,像USER、NUMBER、COMMIT、ROWNUM这类词都不允许作为对象名或字段名。
结合字段命名规则看,这套体系的逻辑是:主键区分业务无关和业务相关,分别加_ID和_CODE后缀;人名、单位名加_NAME后缀。下面按照这套规范建一张订单表的实际 DDL:
-- 按照规范创建的订单表 CREATE TABLE TRD_ORDER ( ORDER_ID VARCHAR2(32) NOT NULL, -- 主键,前缀+流水号规则生成 ORDER_CODE VARCHAR2(30) NOT NULL, -- 业务编码,OMS订单号 CUST_NAME VARCHAR2(50), -- 客户名称 ORDER_AMT NUMBER(16,2), -- 订单金额 optr_code VARCHAR2(50), -- 操作员工号 opt_date DATE, -- 操作时间 remark VARCHAR2(200), -- 备用备注 stand VARCHAR2(200), -- 备用字段 CONSTRAINT PK_TRD_ORDER PRIMARY KEY (ORDER_ID) ); COMMENT ON TABLE TRD_ORDER IS '业务库交易域订单表';这段 DDL 里,表名TRD_ORDER符合"子系统简称_业务含义"的规则;主键使用了无业务含义的ORDER_ID;人名类字段遵循_NAME后缀;金额使用NUMBER(16,2)匹配精度要求。约束名沿用PK_前缀规则,未来通过dba_constraints查询时,不需要多余过滤条件。
3. 字段类型与精度设计:NUMBER(P,S) 和 VARCHAR2(N) 的真实边界
字段类型策略这部分,规范给出的不是孤立的几个注意事项,而是一套从"业务特征"到"数据类型"的映射逻辑。理解了这个逻辑,就不会出现用VARCHAR2存日期、用NUMBER存手机号这类结构性问题。
3.1 CHAR 与 VARCHAR2 的取舍
文档明确说:CHAR只用于静态编码和固定长度的年月日字段,长度不为 1 的字段不推荐使用CHAR。这是因为CHAR(N)是定长存储,即使只存一个字符也会占满 N 的空间,表里字段一多,行迁移和存储浪费就上来了。常见的CHAR(1)用于Y/N这类标志位,CHAR(8)存YYYYMMDD格式的日期字符串。
这里要刻意避开两个误区。第一个是用户态枚举字段,比如状态值1/2/3,有人习惯用CHAR(2)预留位数,但状态值的变化不可预期,一旦出现两位数就麻烦了,不如直接用VARCHAR2(2)。第二个是VARCHAR2的长度定义,文档要求按业务特征定义适当长度并写成偶数。偶数长度的要求不是硬性的,但在中文环境下,VARCHAR2(20)和VARCHAR2(21)的边界差异在实际数据录入时经常引发长度溢出的偶发报错,统一用偶数能少踩一个坑。
3.2 NUMBER(P,S) 精度怎么选
文档给出了几个具体的精度参考值,我整理成表格,并补上实际使用场景的解读:
| 字段业务含义 | 推荐类型 | 说明 |
|---|---|---|
| 销售额、订单金额、账户余额 | NUMBER(16,2) | 保留两位小数,总位数 16 位 |
| 税率、比例、分成 | NUMBER(10,6) | 六位小数,避免比例精度被截断 |
| 货物单价 | NUMBER(16,6) | 单价需要更高的精度做乘法运算 |
| 人数、件数等整数 | NUMBER(10) | 不使用INTEGER,统一 NUMBER |
| 人名 | VARCHAR2(50) | 中文姓名 3-5 字,50 留足余量 |
| 单位名称、地址 | VARCHAR2(100) | 长文本使用VARCHAR2(200) |
| 说明、理由、意见 | VARCHAR2(200) | 超出则考虑CLOB |
值得说明的是,Oracle 里的INTEGER、REAL、FLOAT最终都会转换成NUMBER存储,建表时直接用NUMBER(P,S)反而能避免类型转换的歧义。NUMBER(10,6)这个精度选型并不是拍脑袋——比如分成比例 0.123456,六位小数才能保证计算过程中不丢失精度;而金额用NUMBER(16,2)则是在"能存得下大额数值"和"避免小数点后多余位造成显示混乱"之间取的平衡。
3.3 时间字段与二进制大字段
文档规定 DATE 类型处理时间数据,BLOB 处理二进制,CLOB 处理字符大文本。这里有一个区分度问题:TIMESTAMP和DATE在 Oracle 里都能存时间,但规范写的是 DATE,原因在于 DATE 类型已经包含时分秒,且占 7 字节,TIMESTAMP 默认 11 字节还会带小数秒。对绝大多数业务系统的"操作时间"字段来说,DATE 足够,TIMESTAMP 带来的精度收益无感,反而多占空间。
3.4 主键与公共字段
文档里有一个反直觉的主张:主键要有一定业务含义,推荐"前缀+流水号"规则,不推荐自增主键和纯数字类型主键。这在 Oracle 生态里是合理的:Oracle 的NUMBER自增需要序列(Sequence)配合,而序列一旦在迁移时没同步,主键冲突就是事故。用前缀+年月日+流水号的形式(如ORD202501010001),主键本身携带了时间信息,定位问题时可以直接从主键判断数据归属区间。
同时文档要求每张业务表按需追加四个公共字段:
optr_code VARCHAR2(50) 操作员工号 opt_date DATE 操作时间 remark VARCHAR2(200) 备用字段 stand VARCHAR2(200) 备注字段这四个字段的价值不在于功能本身,而在于统一审计口径。任何一张表有数据变更,查optr_code和opt_date就知道谁在什么时候改的;remark和stand作为预留字段,避免业务紧急加需求时频繁走ALTER TABLE的变更流程。命名不花哨,但一线排障时非常实用。
这里我想强调一点:文档里"涉及'是、否'类型的字段命名,避免使用IS_开头"这条建议值得重视。IS_DELETE、IS_VALID这类命名在 Java 持久层框架里映射时容易与isXxx()方法产生歧义,导致序列化行为异常。规范虽然没展开解释,但这确实是实际开发中的常见坑,建议统一改为DELETE_FLAG、VALID_FLAG这类_FLAG后缀。
4. 数据完整性策略与规范化权衡:第三范式不是银弹
规范在"数据库策略"这一章里提出了一个容易被人跳过但很重要的点:数据完整性尽量通过业务逻辑实现,数据库设计应避免大量外键约束并避免触发器。这句话在多数同行眼里可能和"数据库设计要保证完整性"的传统认知冲突,但放在 Oracle 的生产环境里是讲得通的。
4.1 为什么少用外键和触发器
外键约束的开销体现在两个层面:一是每次 DML 操作都要校验参照关系,高并发写入时这个校验会放大锁竞争;二是分布式架构或分库分表后,跨库外键根本无法生效。文档建议"数据完整性尽量通过业务逻辑实现",意味着在应用层做校验,数据库只解决存储和查询的效率问题。
触发器的问题更明显:它隐式执行,应用层难以感知,出现 bug 时排查链路很长。而且触发器中的逻辑一旦复杂,会让一条简单UPDATE的执行计划变得不可预期。所以要避免。
但这不意味着完全不建外键。如果团队有能力保证应用层校验的一致性,主外键约束可以不做物理实现,而是在文档(PDM)层面维护逻辑关系。这里给出一种偏实用的落地姿势——主表主键PK必须创建,外键逻辑关系记录在设计文档里,但不一定都在数据库里物理创建。
4.2 OLTP 用第三范式,OLAP 允许冗余
规范化与性能的权衡,核心观点是:OLTP 系统遵循第三范式,OLAP 系统为了减少表间连接,合理的数据冗余是必要的。
OLTP:订单表只存客户ID,客户名称通过JOIN客户表获取 OLAP:订单表冗余客户名称、省份、区域等维度属性判断依据就是查询特征。OLTP 查询命中单条数据,按主键或索引走就行,多表 JOIN 的开销可控。而 OLAP 的报表查询要扫大量行,每多一次 JOIN 都可能让执行计划翻车,冗余字段在 ETL 阶段一次性算好,查询时直接取,会稳定得多。
数据冗余由谁保证一致性?常见做法是:在 ETL 任务里由程序控制冗余字段的更新,写清楚更新窗口,而不是技术手段约束。这也是为什么文档要求数据模型统一管理元数据——冗余字段不是随意的,要由同一个元数据模型描述,避免不同团队对"冗余"的理解不一致。
4.3 枚举值字段定义与 XML 配置
SQL 层面之外,文档在附录 A 里给出了XML文件的用法,它承担了字段说明、页面展示属性和 CRUD 代码生成的元数据描述。关键属性有:
| 属性 | 作用 |
|---|---|
queryShow | 查询列表页是否显示该列 |
searchShow | 查询条件区是否显示该列 |
updateShow | 编辑页面是否显示该列 |
insertShow | 新增页面是否显示该列 |
detailShow | 明细页面是否显示该列 |
enumValue | 枚举值定义,格式1:JSP,2:CLASS |
pkg | 生成的 Java 类所在包 |
jspPath | 生成的 JSP 文件路径 |
也就是说,这张表的字段定义不仅决定了数据库结构,还直接约束了上层代码生成器的行为。enumValue="1:JSP,2:CLASS"里的冒号和逗号是一种紧凑的枚举表达,表示该字段取值只能从给定的集合里选,且每个取值对应不同的渲染方式。
这里有一个实用技巧:当枚举值定义需要扩展时,直接在 XML 中追加值即可,但生产库里已经存在的旧值不能被删除,只允许追加。这能在代码生成和运行兼容性之间留足回退空间。
5. 数据模型产出物与版本控制:PDM、XML、SQL 三位一体
规范的第 4 章是落地层面的产物要求,我在这里展开说明一下实际项目里怎么管理这些文件。
5.1 PDM 文件作为模型主源
PDM(PowerDesigner Physical Data Model)是物理数据模型的标准产物。规范要求所有表结构修改必须实时更新 PDM 和创建表脚本,修改表脚本只作备忘。
实际项目中我建议把 PDM 文件纳入 Git 仓库管理,使用 Git LFS 或二进制文件管理策略,避免多人同时编辑导致合并冲突。流程是:先在 PDM 里改模型 → 生成 SQL → 走变更评审 → 执行到测试库 → 验证通过后再上生产。PDM 优先的意义在于,它能保证模型文档与真实库表结构始终一致,避免出现"代码和库表脱节、文档还停留在上个版本"的失控状态。
5.2 建表脚本的分类管理
规范要求的脚本文件共五类:
| 文件名 | 内容 | 使用场景 |
|---|---|---|
项目简称_create_table.sql | 全量创建表结构 | 新环境初始化 |
项目简称_alter_table.sql | 表结构增量变更 | 版本升级时增量执行 |
项目简称_create_prc.sql | 所有存储过程 | 编译存储过程 |
项目简称_create_fun.sql | 所有函数 | 编译函数 |
项目简称_create_view.sql | 所有视图 | 编译视图 |
一个值得注意的细节是:alter_table.sql只作为备忘,最终结构以 PDM 和 create_table.sql 为准。这意味着每次变更执行完成后,要把增量变更合并回全量脚本中。好处是三套环境(开发、测试、生产)始终可以基于同一份全量脚本重建,不会出现"测试库改到一半、生产库还停在老结构"的分叉问题。
这里的增量合并常见做法是,在create_table.sql对应的脚本片段上直接修改,再重跑一次全量脚本到独立的 schema 里验证一致性。不要仰仗手工比对。
5.3 脚本规范落地的一个小技巧
为了确保脚本可重复执行,我一般在脚本前缀加一段存在性判断逻辑:
-- 重复执行不报错,幂等判断 BEGIN EXECUTE IMMEDIATE 'DROP TABLE TRD_ORDER PURGE'; EXCEPTION WHEN OTHERS THEN IF SQLCODE != -942 THEN -- ORA-00942: table or view does not exist RAISE; END IF; END; /这段代码的作用是:在重建表之前先尝试删除同名的旧表,但如果表不存在就跳过,而不是被ORA-00942报错打断脚本执行。PURGE关键字表示连回收站一起清掉,避免多次重建触发 Oracle 的"表空间碎片"问题。加上这段逻辑之后,create_table.sql在全新环境和已有环境都能跑通,也减少了生产变更时"第一次执行报错"的尴尬。
6. 让规范真正落地:用数据字典审计与历史库迁移的实战技巧
规范写得再完整,真正推行时最难的不是编写规则,而是检查存量库是否遵守规则。我在实际治理项目里习惯了用数据字典视图做自动化巡检,下面给出几个可以参考的查询。
6.1 查出所有违反命名规则的索引
-- 查询命名不符合 IDX_ 开头的索引,排除系统自增 SELECT owner, table_name, index_name FROM dba_indexes WHERE index_name NOT LIKE 'IDX\_%' ESCAPE '\' AND index_name NOT LIKE 'PK\_%' ESCAPE '\' AND index_name NOT LIKE 'SYS\_%' ESCAPE '\' AND owner = 'TRD' ORDER BY table_name;这段查询用ESCAPE '\'把下划线转义成普通字符,避免 LIKE 里_被当成通配符。它在核查存量库时能直接列出哪些索引名是系统自动生成的或手工命名不一致的。生产环境不建议直接执行 DDL 改名,先把清单找出来,再在变更窗口里逐一处理。
6.2 V$SQL 定位超长 SQL 与字段类型隐患
-- 查找执行时间较长的 SQL,关注隐式类型转换 SELECT sql_id, elapsed_time/1000000 AS elapsed_sec, sql_text FROM v$sql WHERE elapsed_time/1000000 > 5 AND sql_text LIKE '%VARCHAR2%' ORDER BY elapsed_time DESC;elapsed_time单位是微秒,除以 1000000 转成秒。这个查询的意义在于检查规范是否被 SQL 层面的写法破坏——比如WHERE number_col = '123'会诱发隐式类型转换,索引失效,这条 SQL 往往就在 TOP N 里。通过这个视角,可以倒追是哪条 SQL 使用了不符合字段定义的类型。
6.3 历史存量库的分阶段改造
存量库不会因为一纸规范就自动合规,建议按三个批次推动:第一批强制规范新建表,从本次迭代开始的所有新模型都必须符合命名和类型规则;第二批改造核心表的optr_code、opt_date等公共字段,因为这些字段直接影响审计追踪,优先级最高;第三批处理纯历史表,不动结构,只在数据字典里登记备注,标明"历史遗留,新逻辑禁止依赖"。
这里有个不可忽略的细节:存量改造通过 DDL 变更时,要提前检查脚本对应用可用性的影响,比如加字段时需要明确DEFAULT值。Oracle 11g 之后的版本加了默认值且指定NOT NULL,可以做到只更新数据字典而不锁表,但如果是先加可空字段再回填数据,回填期间会对业务造成时间窗口内的数据不一致。
6.4 新项目建表时直接套用模板
最后一招,把规范固化成团队内部的建表模板,省得每次都要回忆规则。先取业务表的公共字段集合,做成一个团队级模板,每次建表直接复制再扩展。模板核心结构如下:
-- 团队公共模板:所有业务表统一追加四个公共字段 CREATE TABLE /* 子系统简称_业务含义 */ ( /* 主键 */ VARCHAR2(32) NOT NULL, /* 业务编码 */ VARCHAR2(30), -- 业务字段区域 optr_code VARCHAR2(50), opt_date DATE, remark VARCHAR2(200), stand VARCHAR2(200), CONSTRAINT PK_表名 PRIMARY KEY (主键字段) );模板的意义在于降低执行规范的门槛。如果规则只存在于文档里,新成员每次都查文档,效率低下且容易遗漏;模板直接解决了大部分重复劳动,规范只需要在模板之外补充特例说明即可。等存量库治理完成,这套模板就是团队唯一的建模入口。
本文还有配套的精品资源,点击获取