# 04 — 数据库表设计规则

> **适用**：所有涉及建表、改表、索引设计的 AI 开发任务。

---

## 命名规则

| 元素       | 规则            | 示例（正确 → 错误）                                              |
| -------- | ------------- | -------------------------------------------------------- |
| **表名**   | 小写，下划线分隔，单数名词 | `task_apply` → ❌ `TaskApply` `task_applies`              |
| **字段名**  | 小写，下划线分隔      | `create_time` `task_id` → ❌ `createdAt` `taskId`          |
| **主键**   | 统一用 `id`      | `id` → ❌ `task_id` `pk_id`                               |
| **外键**   | `关联表名_id`     | `task_id` `user_id` → ❌ `tid` `uid`                      |
| **布尔字段** | `del_flag` 或明确业务含义的 `is_` 前缀      | `del_flag` `is_active` → ❌ `deleted` `status`          |
| **时间字段** | `_time` 后缀      | `create_time` `update_time` → ❌ `created_at` `updateTime` |
| **金额字段** | `_amount` 后缀  | `reward_amount` → ❌ `reward` `price`                     |
| **索引名**  | `idx_表名_字段`   | `idx_task_status` → ❌ `index1` `task_status_idx`         |
| **唯一索引** | `uk_表名_字段`    | `uk_user_phone` → ❌ `uq_phone`                           |
| **关联表** | 两表名用下划线连接 | `task_apply` `user_role` → ❌ `apply_task` |

### 长度控制

表名超过 3 个单词或 30 个字符时缩写。前缀也要缩成一个单词。

| 完整 | 缩写 | JeecgBoot 实际案例 |
|------|------|-------------------|
| department | dept | `sys_depart_role_permission` → `sys_dept_role_perm` |
| permission | perm | 同上 |
| announcement | notice | `sys_announcement_send` → `sys_notice_send` |
| message | msg | `sys_message_template` → `sys_msg_template` |

```sql
-- ❌ JeecgBoot 原版 — 太长
sys_depart_role_permission         -- 26 字符
sys_permission_data_rule           -- 24 字符

-- ✅ 缩写后
sys_dept_role_perm                 -- 19 字符
sys_perm_data_rule                 -- 20 字符
```

---

## 字段类型规范

| 场景 | 类型 | 说明 |
|------|------|------|
| 主键 | `BIGINT` 自增 或 `VARCHAR(32)` | 推荐 BIGINT 自增；分布式用雪花ID |
| 短文本 | `VARCHAR(N)` | 姓名(50)、标题(200)、URL(500) |
| 长文本 | `TEXT` / `LONGTEXT` | 文章、JSON、描述 |
| 金额 | `DECIMAL(12,2)` | **禁止**用 FLOAT/DOUBLE |
| 状态/类型 | `VARCHAR(20)` 或 `TINYINT` | 枚举值，必须有注释说明 |
| 布尔 | `TINYINT(1)` | 0=否 1=是，注释必须写清含义 |
| 时间 | `DATETIME` | **禁止**用 TIMESTAMP（2038 问题） |
| 日期 | `DATE` | 生日、截止日期 |
| 数量 | `INT` 或 `BIGINT` | |

---

## 每张表必须有的字段

```sql
CREATE TABLE xxx (
    id          BIGINT PRIMARY KEY AUTO_INCREMENT  COMMENT '主键',
    -- [业务字段]
    create_by    VARCHAR(32) NULL DEFAULT NULL COMMENT '创建人',
    create_time  DATETIME    NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
    update_by    VARCHAR(32) NULL DEFAULT NULL COMMENT '更新人',
    update_time  DATETIME    NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
    del_flag     TINYINT(1)  NOT NULL DEFAULT 0        COMMENT '逻辑删除：0-正常 1-已删除',
    INDEX idx_xxx_create_time (create_time)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='表说明';
```

**强制要求**：
- `create_by` — 创建人，建议保留；系统自动填充时可为空
- `create_time` — 创建时间，必须有
- `update_by` — 更新人，建议保留；系统自动填充时可为空
- `update_time` — 更新时间，必须有（`ON UPDATE CURRENT_TIMESTAMP` 自动更新）
- `del_flag` — 逻辑删除标记，除非明确不需要软删除
- 每张表必须有 `COMMENT`
- 引擎统一 `InnoDB`，字符集 `utf8mb4`

---

## 索引规则

| 规则 | 说明 |
|------|------|
| **主键** | 每张表必须有，推荐自增 BIGINT |
| **外键** | 必须加索引（MySQL 自动加，但显式声明更清晰） |
| **查询条件** | WHERE 条件字段必须加索引 |
| **排序字段** | ORDER BY 字段考虑加索引 |
| **联合索引** | 遵循最左前缀原则，区分度高的在前 |
| **唯一约束** | 业务唯一字段必须加唯一索引 |
| **禁止** | ❌ 不加索引的 WHERE / JOIN / ORDER BY |
| **禁止** | ❌ 在大字段（TEXT/BLOB）上建索引 |
| **禁止** | ❌ 过多索引（单表建议 ≤ 5 个） |

### 索引命名

```sql
INDEX       idx_表名_字段            -- 普通索引
UNIQUE      uk_表名_字段             -- 唯一索引  
INDEX       idx_表名_字段1_字段2     -- 联合索引
```

---

## 字段约束规则

| 规则 | 示例 |
|------|------|
| 主键 | `PRIMARY KEY` 或 `NOT NULL AUTO_INCREMENT` |
| 非空 | 业务必填字段 `NOT NULL` |
| 默认值 | 有默认值的字段 `DEFAULT xxx`，**禁止**依赖代码设默认值 |
| 唯一 | 业务唯一字段 `UNIQUE` |
| 外键 | 尽量用逻辑外键（代码维护），**不推荐**物理外键（`FOREIGN KEY`） |

---

## 禁止清单

| ❌ 禁止 | 原因 |
|---------|------|
| 表名/字段名用大写或驼峰 | 跨平台兼容 |
| 金额用 FLOAT/DOUBLE | 精度丢失 |
| 时间用 TIMESTAMP | 2038 年溢出 |
| 用物理外键 | 分库分表/数据迁移困难 |
| 不加注释 | 无人知道字段含义 |
| 字符串代替布尔 | 空间浪费，索引效率低 |
| 大表无索引 | 性能灾难 |
| 字段用 NULL 代替默认值 | 查询需额外处理 `IS NULL` |
| 在代码里设默认值 | 数据一致性依赖应用层 |

---

## 修改表规则

```sql
-- ✅ 正确：显式命名约束
ALTER TABLE task ADD COLUMN priority TINYINT NOT NULL DEFAULT 0 COMMENT '优先级：0-普通 1-紧急';
ALTER TABLE task ADD INDEX idx_task_priority (priority);

-- ❌ 禁止：不写 COMMENT
ALTER TABLE task ADD COLUMN priority TINYINT;

-- ❌ 禁止：不写默认值导致存量数据为 NULL
ALTER TABLE task ADD COLUMN priority TINYINT NOT NULL;
```

**修改表必须**：
- 新字段有 `COMMENT`
- 非空字段有 `DEFAULT`
- 考虑对存量数据的影响
- 附带回滚 SQL

---

## 检查表

每涉及建表/改表，逐项自检：

- [ ] 表名、字段名全小写+下划线
- [ ] 有 `create_by` `create_time` `update_by` `update_time`
- [ ] 软删除字段 `del_flag`（如需要）
- [ ] 每张表有 `COMMENT`，每个字段有 `COMMENT`
- [ ] 金额用 `DECIMAL`，时间用 `DATETIME`
- [ ] 主键 + 外键 + WHERE 条件字段有索引
- [ ] 非空字段有 `NOT NULL DEFAULT`
- [ ] 无物理外键
- [ ] 索引命名 `idx_表名_字段` / `uk_表名_字段`
- [ ] 附带回滚 SQL
- [ ] 引擎 InnoDB，字符集 utf8mb4
