PostgreSQL中UPDATE语句支持直接使用DEFAULT关键字重置列值,前提是该列已通过CREATE TABLE或ALTER TABLE明确定义默认值;DEFAULT在UPDATE中会重新求值(如NOW()取当前时间),且仅作用于SET子句中显式指定的列,不可嵌套于表达式或CASE分支中。

UPDATE 中直接写 DEFAULT 是合法且有效的
只要目标列在表定义中明确设置了默认值(通过 CREATE TABLE ... DEFAULT 或 ALTER TABLE ... SET DEFAULT),你就可以在 UPDATE 语句的 SET 子句里直接写 column = DEFAULT,PostgreSQL 会立即代入该列定义的默认表达式(比如 NOW()、'active'、uuid_generate_v4() 等)。
常见错误现象是误以为 DEFAULT 只在 INSERT 时起作用 —— 实际上它在 UPDATE 中同样生效,且每次执行都会重新求值(例如 NOW() 会取当前时间点,不是建表时的时间)。
-
DEFAULT必须单独作为赋值右值,不能嵌套在表达式里(如col = COALESCE(DEFAULT, 'fallback')是语法错误) - 如果列没定义默认值,写
= DEFAULT会报错:ERROR: column "xxx" has no default value - 对
serial或GENERATED ALWAYS AS IDENTITY列,DEFAULT不会触发新序列值生成(它们只响应INSERT或显式NEXTVAL())
用 CASE 表达式动态决定是否填默认值
当需要“满足某条件时重置为默认值,否则保留原值或改其他值”,就得把 DEFAULT 放进 CASE 分支里 —— 但注意:DEFAULT 关键字本身不能出现在 CASE 的任意分支表达式中(语法不支持)。正确做法是把默认值显式写出,或用子查询查出。
例如,你想让状态为 'archived' 的用户 last_active 重置为当前时间(该列默认值是 NOW()),其他用户保持不变:
UPDATE users SET last_active = CASE WHEN status = 'archived' THEN NOW() ELSE last_active END WHERE id IN (101, 102, 103);
- 不要写
CASE WHEN ... THEN DEFAULT ELSE ... END,这会报错syntax error at or near "DEFAULT" - 若默认值是常量(如
'pending'),直接写进去最安全 - 若默认值是函数(如
uuid_generate_v4()),确保该函数已在目标 schema 中可用,且调用权限已授
批量更新多列并混合使用 DEFAULT 和表达式
PostgreSQL 支持用行构造器一次性更新多个字段,其中部分列可设为 DEFAULT,其余用表达式计算:
UPDATE orders
SET (status, shipped_at, tracking_number) =
(CASE WHEN paid THEN 'shipped' ELSE 'pending' END,
CASE WHEN paid THEN NOW() ELSE DEFAULT END,
CASE WHEN paid THEN generate_tracking() ELSE DEFAULT END)
WHERE order_id = 12345;
这里 shipped_at 和 tracking_number 在 paid = false 时会各自代入其列定义的默认值(注意:不是整行回退,而是逐列独立应用)。
- 所有被括号包裹的列必须在同一张表中,且数量、顺序、类型要与右边元组严格匹配
- 右边元组中每个元素可以是表达式、
DEFAULT,但不能混用(比如(DEFAULT, now(), ...)合法,(DEFAULT + 1, ...)非法) - 性能上,这种写法比多次单列
UPDATE更高效,也避免了中间状态不一致
容易忽略的坑:默认值在 UPDATE 中不自动“补全”未列出的列
这是最常被误解的一点:UPDATE 语句中没出现的列,无论有没有默认值,一律保持原值 —— DEFAULT 不会对它们起任何作用。它只响应你明确写在 SET 子句里的那一列。
例如,有表 products (id, name, category, created_at DEFAULT NOW()),执行:
UPDATE products SET name = 'New Name' WHERE id = 1;
created_at 不会变,哪怕它有默认值。它不会因为这次更新“被重新初始化”。
- 想重置某列就一定要显式写出来:
SET created_at = DEFAULT - 没有
WHERE条件的UPDATE危险性极高,尤其搭配DEFAULT时可能批量覆盖有效数据 - 用
RETURNING *可以确认哪些列真被改了,避免误判










