21 NOT NULL 与 DEFAULT
NOT NULL 要求必须有值,并为新行提供有用的默认值。NOT NULL 和 DEFAULT 是最有用的两种列级规则。NOT NULL 表示该列始终有值——每次 INSERT 和 UPDATE 都必须提供一个值(或依赖默认值)。省略该列时,DEFAULT expr 会提供这个值。两者结合后,模式更容易理解:如果列设为 NOT NULL,应用程序代码就不必到处写 IF column IS NULL。
本章要点
- 使用
NOT NULL强制要求有值 - 使用
DEFAULT expr提供默认值 - 易变默认值与常量默认值的区别,以及这对大规模场景意味着什么
- 组合使用
NOT NULL和DEFAULT,确保“始终有值” - 以后删除或修改默认值
示例
NOT NULL 是代价最低、影响最大的约束之一——它消除了一整类错误(忘记检查空值、聚合结果错误、连接结果出人意料)。只要应用逻辑保证某列一定有值,就应将它标记为 NOT NULL。
DEFAULT 在插入时求值,而不是在创建表时求值。DEFAULT current_timestamp 会针对每个插入的行运行一次 current_timestamp,返回事务开始时间。DEFAULT gen_random_uuid() 会为每一行生成一个新的 UUID。
向已有表添加常量默认值(DEFAULT 0、DEFAULT 'pending')速度很快——PostgreSQL 11+ 会把这个常量默认值存入元数据,而无需重写表。包括 STABLE 表达式 current_timestamp 在内的非易变默认值也可以使用快速路径。random() 和 clock_timestamp() 等易变默认值需要为每一行已有数据提供一个值,因此会重写表。
运行示例
运行方式:
常见陷阱
- 把
''(空字符串)当成“缺失”——请选择一种约定,并始终保持一致。大多数团队用NULL表示“没有值”,把''留给“有意留空”的情况。混用两者,会让查询永远处于半好半坏的状态。 - 默认值不会在
UPDATE时自动重新应用。要“重置为默认值”,请使用UPDATE t SET col = DEFAULT WHERE ...。 - 向已有列添加
NOT NULL时,如果该列中可能已有空值,操作会失败。先回填该列,再执行SET NOT NULL。 - 当每一列都有默认值时,
INSERT INTO t DEFAULT VALUES很有用——它让数据库填充所有值。
常见模式是将 created_at timestamptz NOT NULL DEFAULT current_timestamp 与 updated_at timestamptz NOT NULL DEFAULT current_timestamp 配对使用。created_at 在 INSERT 时设置,此后不再变化;updated_at 则需要一个触发器(后文会讲)在每次 UPDATE 时将其更新。
尝试执行 INSERT INTO tasks_example (id) VALUES (3);——title 列的 NOT NULL 应该拒绝这次插入。然后执行 INSERT INTO tasks_example (id, title, priority) VALUES (3, 'X', 9);,观察 CHECK 如何拒绝超出范围的优先级值。
下载
SQL 文件:not-null-and-default.sql · 全部示例文件 · 准备工作