postgresql中created_at和updated_at应设default now(),update需before触发器无条件赋值new.updated_at := now()并return new;copy须加defaults选项,orm需防字段显式展开。

PostgreSQL INSERT时自动填充created_at和updated_at字段
直接用 DEFAULT 配合函数就能实现,但要注意时区、事务一致性以及是否真能覆盖所有写入路径。最稳妥的方式是给时间字段设 DEFAULT NOW() 或 DEFAULT CURRENT_TIMESTAMP,它们在 PostgreSQL 中行为一致,都取语句开始执行时的事务时间戳。
示例建表语句:
CREATE TABLE users ( id SERIAL PRIMARY KEY, name TEXT, created_at TIMESTAMPTZ DEFAULT NOW(), updated_at TIMESTAMPTZ DEFAULT NOW() );
-
NOW()和CURRENT_TIMESTAMP是等价的,返回带时区的TIMESTAMPTZ,推荐优先用NOW()—— 更短,语义更直白 - 不要用
CLOCK_TIMESTAMP()做默认值,它会随语句内多次调用而变化,破坏事务内时间一致性 - 如果业务要求严格 UTC,确保数据库
timezone设为'UTC',或显式用(NOW() AT TIME ZONE 'UTC')(但需注意类型转换后是TIMESTAMP,非TIMESTAMPTZ)
如何让updated_at在UPDATE时也自动更新
DEFAULT 只在 INSERT 时生效,UPDATE 不触发。必须配合触发器或生成列(v12+),但生成列不支持函数表达式,所以实际只能选触发器。
常见错误是把触发器逻辑写成“只要字段没显式赋值就更新”,这不可靠 —— 因为 UPDATE ... SET name='a' 显式没提 updated_at,但 PostgreSQL 默认会把它设为 NULL(如果字段允许 NULL)或报错(如果 NOT NULL 且无 DEFAULT)。所以正确做法是:触发器无条件更新 updated_at。
- 触发器函数要返回
NEW,否则 UPDATE 会失败 - 函数体里写
NEW.updated_at := NOW(); RETURN NEW;,别漏掉RETURN - 触发器类型必须是
BEFORE UPDATE,AFTER无法修改行内容 - 如果表有多个时间审计字段(如
created_by、updated_by),都在同一个触发器里统一赋值,避免多触发器互相干扰
INSERT ... SELECT 场景下Default是否生效
生效。只要目标字段在 INSERT 列表中未出现,哪怕源是 SELECT,DEFAULT 依然触发。
例如:
INSERT INTO users (name) SELECT 'alice'; -- created_at/updated_at 自动填入 NOW()
但以下写法会绕过 Default:
INSERT INTO users (name, created_at) SELECT 'alice', NULL; -- created_at 被显式设为 NULL
- 显式列出字段并传
NULL或DEFAULT字面量,都会跳过默认值逻辑 -
INSERT ... DEFAULT VALUES完全依赖 Default,适合只填审计字段的初始化场景 - ORM(如 SQLAlchemy、Prisma)可能隐式展开字段列表,导致 Default 失效,需检查生成 SQL 或强制使用
exclude_unset=True类选项
Default值在COPY和pg_restore中是否起作用
不起作用 —— COPY 和逻辑备份恢复默认跳过 Default 计算,除非显式指定 WITH (DEFAULTS) 选项。
执行 COPY 时务必加:
COPY users (name) FROM '/tmp/data.csv' WITH (FORMAT CSV, DEFAULTS);
- 缺
DEFAULTS,所有未在列列表中出现的字段会被设为 NULL(即使定义了 NOT NULL + DEFAULT) -
pg_restore -d db --use-set-session-authorization不影响 Default 行为,但若备份含INSERT语句而非COPY,则取决于语句是否显式列出字段 - 批量导入脚本建议统一走
COPY ... DEFAULTS,比逐条 INSERT 快一个数量级,且语义可控
Default 看似简单,但真正落地时最容易栽在 COPY 导入遗漏 DEFAULTS、触发器忘记 RETURN NEW、或 ORM 自动生成字段列表这三处。尤其当审计字段被设为 NOT NULL 后,这些疏漏会直接导致插入失败,而不是静默降级。










