你写 SQL 时有没有遇到过这种情况:明明在表里给某列设了默认值,INSERT 的时候没写这一列,结果数据插进去却是 NULL,而不是你预期的默认值?其实,不同数据库对“默认值触发”的规则差异还挺大的,一个小细节没注意,就可能掉坑里。

简单来说,MySQL 和 PostgreSQL 都支持在 INSERT 时自动使用列的默认值,但行为细节和触发条件不同;而 SQLite 则只在显式指定 DEFAULT 或省略字段时才生效,不会“自动填充”缺失字段——关键就看你是不是真的把字段给省略了。
INSERT 中省略字段 vs 显式写 NULL:行为完全不同
数据库只对“完全不提”的字段应用默认值;一旦写了 NULL 或空字符串,它就老老实实按字面值插入,哪怕该列明明定义了 DEFAULT。这里有个容易踩的坑:很多人调试时发现默认值没生效,回头一看,INSERT 语句里不经意地写了 NULL,或者建表时忘了加 DEFAULT 关键字。
INSERT INTO users (name) VALUES ('Alice');→ 如果age列定义了DEFAULT 18,会自动填入18INSERT INTO users (name, age) VALUES ('Alice', NULL);→ 即使age有默认值,也会插入NULL(除非定义了NOT NULL DEFAULT ...)- PostgreSQL 对
NOT NULL列更严格:如果省略且无默认值,直接报错ERROR: null value in column "age" violates not-null constraint
MySQL 的 STRICT 模式决定是否静默补默认值
MySQL 在非 STRICT 模式下,对没有默认值又允许 NULL 的字段会直接填 NULL;但一旦开启 STRICT 模式,如果字段既无默认值又不允许 NULL,省略它就会报错 Field 'age' doesn't ha ve a default value。所以,到底会不会自动填充,完全取决于当前 SQL 模式设置。
- 检查当前模式:
SELECT @@sql_mode;,如果包含STRICT_TRANS_TABLES或STRICT_ALL_TABLES,就是 STRICT 模式 - 临时关闭(仅调试用):
SET sql_mode = ''; - 最稳妥的做法:建表时显式声明
age INT NOT NULL DEFAULT 18,这样无论模式如何都安全
PostgreSQL 必须显式声明 DEFAULT 才能省略字段
PostgreSQL 在这方面态度非常明确:不会为未声明 DEFAULT 的字段做任何填充——哪怕它是 NOT NULL。省略字段的前提是它必须有 DEFAULT 子句(可以是常量、函数如 NOW(),或表达式)。
- 正确示例:
CREATE TABLE logs (id SERIAL, msg TEXT, ts TIMESTAMP DEFAULT NOW());→INSERT INTO logs (msg) VALUES ('start');会自动填入当前时间 - 错误写法:
ts TIMESTAMP NOT NULL但没写DEFAULT→ 省略ts必报错 - 注意:
DEFAULT表达式在每次插入时重新求值(比如NOW()是实时的),而不是建表时只计算一次
总结一下:所谓“自动填充”,本质上是“省略字段 + 列定义包含 DEFAULT + 数据库引擎按规则执行”三者缺一不可。很多人调了半天发现没生效,十有八九是 INSERT 语句里悄悄塞了 NULL,或者建表时漏了 DEFAULT 关键字。下次排查时,先检查这两个地方,问题往往就能迎刃而解。
