# 通用数据库规范

> 本文件由仓库根目录 `AGENTS.md` 纳入强制规则，适用于 MySQL 8.0 新项目及新增表。历史表以兼容为先，不得为统一格式进行无业务收益的大范围重命名。

## 1. 基本原则

1. 默认使用 `InnoDB`、`utf8mb4` 和 `utf8mb4_unicode_ci`。
2. 数据库、表、字段、索引只使用小写英文字母、数字和下划线。
3. 命名必须表达业务含义，禁止拼音、无意义缩写和中文标识符。
4. 新表、新字段和索引必须有明确用途，禁止预留 `field1`、`ext1` 等无语义字段。
5. 表和业务字段必须填写中文 Comment。
6. 只保存必要数据，禁止在普通业务字段中保存密码、密钥、令牌等敏感明文。
7. 无法确认的业务规则必须先确认，禁止根据名称猜测。
8. DDL 必须经过审核、备份和测试环境验证后执行。

## 2. 数据库命名

1. 数据库名称必须由项目负责人或数据库负责人按实际部署方案人工确定。
2. AI、代码生成器和迁移工具不得自动推断、创建或修改数据库名称。
3. 未提供数据库名称时必须先确认，禁止使用项目名、目录名或示例名代替。
4. 确定后由项目配置统一管理，业务 SQL 不得硬编码其他环境的数据库名称。

## 3. 表命名

### 3.1 通用格式

1. 表名、字段名应在 32 个字符以内优先完整表达业务含义，并保持简洁。
2. 删除无实际含义的 `data`、`info`、`record`、`business` 等冗余词。
3. 仅在名称超过限制或完整单词明显冗长时使用项目统一且无歧义的缩写，禁止为追求短名称进行过度缩写。
4. 常规缩写取英文单词前 3 个字母；行业通用缩写或项目已有统一词典除外。
5. 同一英文单词在项目内只能使用一种缩写；缩写存在歧义时保留完整单词。
6. 表名使用：`模块_业务对象`。
7. 使用单数名词，禁止同一项目混用单数和复数。
8. 禁止添加 `tbl_`、`table_` 前缀。
9. 禁止使用 MySQL 保留字，如必须使用应更换业务名称，不依赖反引号规避。

```text
system      -> sys
customer    -> cus
product     -> pro
department  -> dep
category    -> cat
```

```text
sys_user
sys_role
sales_order
sales_invoice
```

### 3.2 特殊用途后缀

| 用途 | 格式 | 示例 |
| --- | --- | --- |
| 明细表 | `{主对象}_{明细对象}` | `sales_order_item` |
| 关联表 | `{对象1}_{对象2}_link` | `sys_user_role_link` |
| 日志表 | `{业务对象}_log` | `sys_user_login_log` |
| 历史快照表 | `{业务对象}_history` | `sales_order_history` |
| 配置表 | `{业务对象}_config` | `sys_notice_config` |
| 统计汇总表 | `{业务对象}_summary` | `sales_month_summary` |
| 临时表 | `tmp_{业务对象}` | `tmp_order_import` |

1. `log` 保存事件记录，原则上只新增、不修改。
2. `history` 保存业务对象历史版本或快照。
3. 明细表优先使用具体明细对象名，如 `item`、`product`、`service`；无法提炼明确对象名时才使用 `_detail`。
4. `_detail` 只表示主从明细，不得代替纯关联表。
5. `link` 表示对象之间的多对多或独立授权关系。
6. 临时表必须注明生命周期和清理方式，不得长期承担正式业务。

### 3.3 表级短前缀

1. 每张表定义唯一、稳定的字段短前缀。
2. 短前缀使用 2～6 个小写字母，由表名关键词首字母或稳定缩写组成。
3. 短前缀在项目内不得重复，确定后不得随意修改。
4. 表内业务字段统一使用该短前缀，禁止混用裸 `id` 和带前缀字段。

```text
sales_order           -> so
sales_order_item      -> soi
sys_user              -> su
sys_user_role_link    -> surl
```

## 4. 字段命名

### 4.1 通用格式

1. 使用：`{表短前缀}_{业务名称}`。
2. 名称按“对象 + 属性”组织，优先使用完整、简洁的英文单词。
3. 名称需要缩写时遵守表命名中的缩写规则。
4. 同一业务概念在全项目使用相同英文词汇。

```text
so_id
so_order_no
so_customer_id
so_total_amount
so_status
so_create_date
```

### 4.2 常用字段后缀

| 含义 | 后缀 | 示例 |
| --- | --- | --- |
| 主键或关联 ID | `_id` | `so_customer_id` |
| 编号 | `_no` | `so_order_no` |
| 业务代码 | `_code` | `so_source_code` |
| 名称 | `_name` | `su_name` |
| 类型 | `_type` | `so_type` |
| 状态 | `_status` | `so_status` |
| 是否标记 | `_is_xxx` | `so_is_deleted` |
| 数量 | `_count`、`_qty` | `sod_qty` |
| 金额 | `_amount` | `so_total_amount` |
| 比率 | `_rate` | `so_discount_rate` |
| 排序 | `_sort` | `so_sort` |
| 日期时间 | `_date` | `so_pay_date` |
| Unix 时间戳 | `_time` | `so_callback_time` |
| JSON 数据 | `_json` 或明确业务名 | `so_snapshot_json` |

1. `_no` 用于展示或业务流转编号，`_code` 用于稳定代码或外部编码。
2. `_status` 表示生命周期状态，`_type` 表示分类，禁止混用。
3. 是否字段使用肯定含义，如 `is_enabled`，禁止双重否定。
4. 日期时间和时间戳字段必须包含事件名称，禁止只写 `date` 或 `time`。

## 5. 主键规范

1. 新业务表默认使用单字段无业务含义主键：`{表短前缀}_id`。
2. 单机或单库场景默认使用 `BIGINT UNSIGNED AUTO_INCREMENT`。
3. 分布式 ID 使用 `BIGINT UNSIGNED` 或项目统一的字符串类型，不得混用多种 ID 策略。
4. 主键字段必须 `NOT NULL`。
5. 主键 Comment 写：`业务名称；主键/自增` 或 `业务名称；主键`。

```sql
`so_id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '订单ID；主键/自增'
```

### 5.1 联合主键

1. 默认优先使用单字段主键，使用唯一索引约束业务唯一性。
2. 纯关联表且一组关联字段天然唯一时，可以使用联合主键。
3. 允许同一对象重复出现、需要被其他表引用或需要独立生命周期时，使用单字段主键，不使用联合主键。
4. 每个联合主键字段的 Comment 都列出全部成员，顺序与 `PRIMARY KEY` 一致。
5. 联合主键作为普通字段说明的末尾扩展。

```text
用户ID；关联 sys_user.su_id，联合主键：surl_user_id + surl_role_id
角色ID；关联 sys_role.sr_id，联合主键：surl_user_id + surl_role_id
```

## 6. 关联字段与外键

1. 关联字段使用：`{本表短前缀}_{目标对象}_id`。
2. 关联字段类型、长度和 UNSIGNED 属性必须与目标主键完全一致。
3. 高频查询、连接或完整性检查使用的关联字段必须建立索引。
4. Comment 必须写完整目标：`业务名称；关联 table.column`。
5. 自关联、多态关联和无物理外键的逻辑关联同样必须说明。
6. 缺失关联值使用 `NULL`，禁止使用 `0`、空字符串或负数表示不存在。

```text
so_customer_id  -> sales_customer.sc_id
so_parent_id    -> sales_order.so_id
```

### 6.1 物理外键

1. 同库、生命周期一致且不存在拆库需求的强关系可以使用物理外键。
2. 跨库、分库分表、批量导入、历史兼容或高写入链路使用逻辑关联。
3. 无论是否使用物理外键，都必须填写关联 Comment 并建立必要索引。
4. 外键默认使用 `RESTRICT`，只有业务明确允许时才使用 `CASCADE` 或 `SET NULL`。
5. 外键名称使用：`fk_{源表}_{目标表}`；同一目标存在多个关系时追加字段语义。

## 7. 字段类型

### 7.1 整数

1. 根据实际范围选择 `TINYINT`、`SMALLINT`、`INT` 或 `BIGINT`。
2. 非负值使用 `UNSIGNED`。
3. 禁止使用已废弃的整数显示宽度和 `ZEROFILL`。
4. 布尔值使用 `TINYINT UNSIGNED`，值固定为 `0` 和 `1`，并写枚举 Comment。

### 7.2 精确数值

1. 金额、重量、税率、比例等精确数值使用 `DECIMAL`，禁止使用 `FLOAT`、`DOUBLE`。
2. 金额默认使用 `DECIMAL(18,2)`；币种小数位不同或计算精度更高时按业务调整。
3. 比率存储小数还是百分数必须在项目内统一，并在 Comment 写明单位或口径。

### 7.3 字符串

1. 固定长度代码使用 `CHAR`，可变文本使用 `VARCHAR`。
2. 长度按业务上限设计，禁止所有字段默认使用 `VARCHAR(255)`。
3. 长文本使用 `TEXT`、`MEDIUMTEXT` 或 `LONGTEXT`。
4. IP 地址使用 `VARCHAR(45)` 或统一的 `VARBINARY(16)` 方案。
5. UUID 优先使用 `BINARY(16)`；必须保留标准文本格式时使用 `CHAR(36)`。

### 7.4 日期时间

1. 业务日期使用 `DATE`，业务时间点默认使用 `DATETIME`，字段名统一以 `_date` 结尾。
2. 需要 MySQL 时区自动转换时使用 `TIMESTAMP`，字段名仍以 `_date` 结尾，全项目必须统一理解其语义。
3. Unix 时间戳只在外部协议或历史兼容需要时使用 `INT UNSIGNED` 或 `BIGINT UNSIGNED`，字段名以 `_time` 结尾。
4. 时长使用整数并注明单位，禁止使用 `TIME` 保存超过 24 小时的时长。

### 7.5 JSON、枚举和二进制

1. 结构不固定但需要整体读写的数据可以使用 `JSON`。
2. 高频查询、排序或关联的属性必须拆成独立字段，不得长期隐藏在 JSON 中。
3. 业务状态默认使用 `TINYINT`、`SMALLINT` 或稳定字符串代码；只有值集合永久封闭时才使用 MySQL `ENUM`。
4. 文件默认存储对象地址和元数据，非必要不直接存储大二进制内容。

## 8. NULL 与默认值

1. 必填字段使用 `NOT NULL`。
2. 未知、不适用或尚未发生使用 `NULL`。
3. 禁止使用 `0`、空字符串、`0000-00-00` 或特殊负数代替 `NULL`。
4. 默认值必须有明确业务含义，禁止为了省略写入逻辑随意设置默认值。
5. 状态字段必须设置明确默认状态，并在 Comment 中说明。
6. 金额、数量是否默认 `0` 必须按业务区分“未填写”和“值为零”。
7. `TEXT`、`BLOB`、`JSON` 默认不设置无必要的默认表达式。
8. `create_date` 默认当前时间；`update_date` 可以使用 `ON UPDATE CURRENT_TIMESTAMP`。

## 9. 索引规范

### 9.1 索引命名

| 索引 | 格式 |
| --- | --- |
| 主键 | `PRIMARY KEY` |
| 唯一索引 | `uk_{表名}_{字段1}_{字段2}` |
| 普通索引 | `idx_{表名}_{字段1}_{字段2}` |
| 物理外键 | `fk_{源表}_{目标表}` |
| 全文索引 | `ft_{表名}_{字段}` |

索引名过长时使用表短前缀和字段业务缩写，但必须保持可识别。

### 9.2 索引设计

1. 业务唯一性必须由唯一索引保证，禁止只依赖应用层查询。
2. 联合索引按“等值条件、范围条件、排序或分组字段”评估顺序。
3. 索引必须匹配真实查询，禁止为每个字段机械建索引。
4. 禁止建立重复索引和被其他联合索引完整覆盖的冗余索引。
5. 低选择性字段不得单独建索引，除非与其他字段组成有效联合索引。
6. 更新频繁的表控制索引数量，避免无收益的写放大。
7. 长字符串索引必须评估前缀长度、唯一性和字符集占用。
8. 上线前使用 `EXPLAIN` 验证核心查询是否命中预期索引。

## 10. Comment 规范

### 10.1 基本格式

1. 表 Comment 写业务对象或职责。
2. 字段 Comment 使用：`业务名称；属性说明；业务规则`。
3. 多段使用中文分号 `；`，同类值或补充条件使用中文逗号 `，`。
4. 只写已确认事实，禁止字段名直译、问号、乱码、路径、凭据和真实业务数据。
5. 无法确认的内容进入待确认，禁止猜测。

### 10.2 主键

```text
用户ID；主键/自增
权限代码；主键
```

### 10.3 枚举、状态和是否字段

1. 统一写：`业务名称；枚举：值=含义，值=含义`。
2. 数字、负数和字符串值必须与数据库实际值一致。
3. 位权限追加：`；按位组合`。
4. 禁止只写 `0/1`、`Y/N` 或无对应含义的值列表。

```text
启用状态；枚举：0=停用，1=启用
审核状态；枚举：-1=驳回，0=待审核，1=通过
订单类型；枚举：normal=普通订单，gift=赠品订单
操作权限；枚举：1=查看，2=新增，4=修改，8=删除；按位组合
```

### 10.4 关联

```text
客户ID；关联 sales_customer.sc_id
父级ID；关联 category.cat_id
区域ID；关联 object.obj_id，限定 object.obj_parent_id=3
业务记录ID；按 business_type 指向对应业务记录主键
```

### 10.5 关联与枚举组合

1. 字段同时包含关联和枚举时，按“业务名称、关联、枚举”的顺序填写。
2. 关联和枚举属于不同属性，使用中文分号 `；` 分隔。
3. 枚举内部各值使用中文逗号 `，` 分隔。
4. Comment 末尾不加分号。

```text
公告类型；关联 gxb_base.gb_type；枚举：gxb=整车公告，dipan=底盘公告
```

### 10.6 单位、格式和时间

```text
订单金额；单位：元
商品净重；单位：千克
完成率；单位：百分比
变更内容；JSON结构
图片列表；JSON数组
附件ID列表；逗号分隔
申请时间；时间戳
回调时间；毫秒时间戳
创建时间
```

1. `INT`、`BIGINT` 保存 Unix 时间时注明“时间戳”。
2. `DATE`、`DATETIME`、`TIMESTAMP` 只写业务时间名称。
3. JSON 对象写“JSON结构”，JSON 数组写“JSON数组”。
4. 数值字段写实际存储单位，不按页面换算后的单位填写。

### 10.7 禁止写法

```text
数据
信息
状态0/1
关联用户表
联合主键字段
创建时间？
user_id
varchar字段
暂时不知道
```

## 11. 建表顺序

字段按以下顺序组织：

1. 主键。
2. 业务唯一标识。
3. 核心业务字段。
4. 关联字段。
5. 类型、状态和标记字段。
6. 扩展字段。
7. 创建、更新、删除等审计字段。
8. 主键、唯一索引、普通索引和外键定义。

SQL 关键字使用大写，表名和字段名使用反引号，缩进统一为两个空格。

## 12. 完整建表示例

```sql
CREATE TABLE `sales_order` (
  `so_id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '订单ID；主键/自增',
  `so_order_no` varchar(32) NOT NULL COMMENT '订单编号',
  `so_customer_id` bigint unsigned NOT NULL COMMENT '客户ID；关联 sales_customer.sc_id',
  `so_status` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '订单状态；枚举：0=待确认，1=已确认，2=已完成，3=已取消',
  `so_total_amount` decimal(18,2) NOT NULL DEFAULT 0.00 COMMENT '订单总金额；单位：元',
  `so_extra_json` json DEFAULT NULL COMMENT '扩展信息；JSON结构',
  `so_sort` int unsigned NOT NULL DEFAULT 0 COMMENT '排序；数值越小越靠前',
  `so_create_user_id` bigint unsigned NOT NULL COMMENT '创建人ID；关联 sys_user.su_id',
  `so_create_date` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `so_update_user_id` bigint unsigned DEFAULT NULL COMMENT '更新人ID；关联 sys_user.su_id',
  `so_update_date` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  `so_is_deleted` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '删除标记；枚举：0=未删除，1=已删除',
  `so_delete_user_id` bigint unsigned DEFAULT NULL COMMENT '删除人ID；关联 sys_user.su_id',
  `so_delete_date` datetime DEFAULT NULL COMMENT '删除时间',
  PRIMARY KEY (`so_id`),
  UNIQUE KEY `uk_sales_order_order_no` (`so_order_no`),
  KEY `idx_sales_order_customer_status` (`so_customer_id`, `so_status`),
  KEY `idx_sales_order_create_date` (`so_create_date`)
) ENGINE=InnoDB
  DEFAULT CHARSET=utf8mb4
  COLLATE=utf8mb4_unicode_ci
  COMMENT='销售订单';
```

## 13. 纯关联表建表示例

```sql
CREATE TABLE `sys_user_role_link` (
  `surl_user_id` bigint unsigned NOT NULL COMMENT '用户ID；关联 sys_user.su_id，联合主键：surl_user_id + surl_role_id',
  `surl_role_id` bigint unsigned NOT NULL COMMENT '角色ID；关联 sys_role.sr_id，联合主键：surl_user_id + surl_role_id',
  `surl_create_date` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  PRIMARY KEY (`surl_user_id`, `surl_role_id`),
  KEY `idx_sys_user_role_link_role_id` (`surl_role_id`)
) ENGINE=InnoDB
  DEFAULT CHARSET=utf8mb4
  COLLATE=utf8mb4_unicode_ci
  COMMENT='用户与角色关联';
```

## 14. DDL 变更规范

1. 禁止直接在生产环境手工修改表结构。
2. 每次变更提供用途、影响表字段、兼容性、预览、执行、回滚和注意事项。
3. 新增非空字段时先评估历史数据；必要时按“新增可空字段、回填、改为非空”分步执行。
4. 删除或重命名字段前先扫描代码、接口、报表、任务和外部系统依赖。
5. 大表变更必须评估锁表时间、磁盘空间、复制延迟和在线 DDL 能力。
6. 修改 Comment 时只允许改变 Comment，不得顺带改变类型、NULL、默认值、字符集、生成表达式和索引。
7. 批量更新或删除前必须先执行相同条件的 `SELECT` 预览影响范围。
8. 所有 DDL 和数据修复 SQL 由人工审核后执行。

## 15. 提交检查清单

1. 数据库、表、字段和索引命名符合本规范。
2. 表短前缀唯一，表内字段前缀一致。
3. 主键策略明确，关联字段类型与目标主键一致。
4. 必填、可空和默认值具有明确业务语义。
5. 金额等精确数值未使用浮点类型。
6. 状态、类型和是否字段含义清晰且 Comment 完整。
7. 表和全部业务字段均已填写 Comment。
8. 联合主键列出全部成员且顺序正确。
9. 唯一性由唯一索引保证，核心查询索引已用 `EXPLAIN` 验证。
10. 不存在重复索引、无意义预留字段和敏感明文。
11. 字符集为 `utf8mb4`，排序规则为 `utf8mb4_unicode_ci`，存储引擎为 `InnoDB`。
12. DDL 已提供预览、执行、回滚和影响说明。
