一句话定义
DDL(Data Definition Language)是定义数据库结构的 SQL 子语言:建库建表(CREATE)、改结构(ALTER)、删对象(DROP),其核心是选对数据类型并用约束把业务规则固化进模式。
为什么重要
- 类型与约束是「免费的正确性」:
NOT NULL、UNIQUE、外键在数据库层兜底,比应用层校验更难被绕过。 - 类型选错是长期成本:金额用浮点、时间用字符串、状态用无约束文本,都会在数月后变成数据治理灾难。
- DDL 是模式演化的载体:加列、改类型、建索引的语义与代价差异(是否锁表、是否重建)直接决定上线风险。
前置知识
kp-002(关系、候选键、域的概念)。
核心概念
- 数据类型三族:数值(INTEGER/BIGINT/DECIMAL)、字符(VARCHAR/CHAR/TEXT)、时间(DATE/TIMESTAMP/TIMESTAMPTZ)。
- 主键约束(PRIMARY KEY):唯一 + 非空,每表一个;唯一约束(UNIQUE):允许 NULL(视方言而定)。
- 非空约束(NOT NULL)、检查约束(CHECK)、默认值(DEFAULT)。
- 外键约束(FOREIGN KEY)与参照动作:
ON DELETE RESTRICT / CASCADE / SET NULL。 - 标识列:MySQL
AUTO_INCREMENT、PostgreSQLGENERATED ... AS IDENTITY。
原理与机制
约束在写入路径上被强制执行:唯一约束靠唯一索引实现,插入时索引查重失败即报错;外键在子表写入时查父表、在父表删除时按参照动作处理子行(CASCADE 级联删除、RESTRICT 拒绝、SET NULL 置空),每步都是额外 IO,这也是高并发场景评估外键取舍的依据。类型决定存储与比较语义:DECIMAL 按十进制精确存储,适合金额;FLOAT/DOUBLE 是二进制浮点,0.1 + 0.2 ≠ 0.3;TIMESTAMP 建议存储 UTC 并由会话时区渲染,避免跨时区歧义。ALTER 的代价取决于「元数据变更还是数据重写」:PostgreSQL 多数加列(带常量默认值)是瞬时元数据操作,而改列类型通常重写全表。
实例或案例
建一张规范订单表:
CREATE TABLE orders (
order_id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
user_id BIGINT NOT NULL,
amount DECIMAL(12,2) NOT NULL CHECK (amount >= 0),
status VARCHAR(16) NOT NULL DEFAULT 'created',
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
CONSTRAINT fk_orders_user FOREIGN KEY (user_id) REFERENCES users(user_id),
CONSTRAINT uq_orders_user_created UNIQUE (user_id, created_at)
);
它体现了四个习惯:金额用 DECIMAL、状态有默认值、时间带时区、业务唯一性用复合唯一约束表达。
常见误区
- 用 FLOAT 存金额:二进制浮点有舍入误差,对账必然出错,须用 DECIMAL 或以最小货币单位的整数存储。
- 滥用可空列:列默认可空导致查询到处写
IS NULL分支;应逐列判断「缺失」是否是合法状态。 - 在外键上无脑 CASCADE:级联删除可能在一次误删中清空多张表,生产环境慎用。
自测题
- 唯一约束和主键约束的区别是什么? 答:主键隐含 NOT NULL 且每表只能一个;唯一约束允许多个,且多数数据库允许其中出现 NULL(语义按方言确认)。
- 为什么外键约束会影响写入吞吐? 答:每次子表写入都要到父表校验引用存在性(额外索引查找),父表删除还要按参照动作处理子行,都会增加锁与 IO。
公式或模型
本节不适用:本知识点是语法与工程规则,无定量公式。
图示
本节不适用:约束作用点用上文建表示例已可直接对照,画图收益低。
直观类比
模式像合同条款,约束就是违约条款:类型定死「只能写什么」,CHECK 定「数值范围」,外键定「引用必须真实存在」;违约当场拒绝,而不是等审计时才发现。
与其他知识点的关系
kp-004、kp-005 在这些表上展开查询;kp-006 把「该建哪些约束」上升为建模方法;kp-011 说明唯一约束与索引共享存储结构。
延伸阅读
- 《数据库系统概念》(Abraham Silberschatz 等)第 3 章
- 《高性能 MySQL》(Baron Schwartz 等)数据类型一章