最直接忽略插入字段的方式是在结构体字段的gorm tag中使用“-”,GORM在Create()时完全跳过该字段,不读取值也不生成SQL列;运行时动态控制则用Select()指定列名。

插入时用 - 标签忽略字段
最直接的方式是在结构体字段的 gorm tag 里加 -,GORM 遇到这个标记会在 Create() 时完全跳过该字段,既不读取值,也不生成对应 SQL 列。
-
-是硬忽略:哪怕你显式赋了值,也不会进 INSERT 语句 - 适用于「永远不该由应用层写入」的字段,比如数据库自动生成的审计字段(
created_by由触发器填)、或只读元信息 - 注意它和
default、omitempty无关——后两者影响默认值或零值行为,而-是彻底移除字段参与
示例:
type User struct {
ID uint `gorm:"primaryKey"`
Name string
Email string `gorm:"uniqueIndex"`
Password string `gorm:"-"` // 插入/更新都不带 password 字段
}
执行 db.Create(&User{Name: "a", Email: "a@b.com", Password: "123"}),实际 SQL 是 INSERT INTO users (name, email) VALUES (?, ?) —— Password 字段彻底消失。
用 Select() 动态控制插入字段
如果忽略逻辑是运行时决定的(比如某些 API 调用要跳过敏感字段,另一些则需要),就不能靠结构体 tag 硬编码,得用 Select() 显式指定要插入的列。
-
Select()必须在Create()前链式调用,且只对当前操作生效 - 传入字段名字符串(不是结构体字段名,而是数据库列名),大小写需与表定义一致
- 未被
Select()包含的字段,即使有值也不会出现在 INSERT 中;零值字段也不会被补默认值
示例:
u := User{Name: "b", Email: "b@c.com", Password: "xxx"}
db.Select("name", "email").Create(&u) // 只插 name 和 email
等价于手动拼 SQL 的 INSERT INTO users (name, email) VALUES (?, ?)。注意:此时 u.ID 不会被设为新主键值(因为没插入),需后续查或重载。
别把 sql.NullString 当成跳过字段的手段
有人误以为用 sql.NullString 并设 Valid=false 就能跳过字段,其实不行。
- GORM 仍会把该字段纳入 INSERT,只是值为
NULL(前提是列允许 NULL) - 如果字段有
NOT NULL约束且无默认值,就会报错Column 'xxx' cannot be null - 这属于「插入 NULL」,不是「跳过字段」;前者依赖数据库约束容忍,后者是彻底不发该列
所以想跳过,就别走 NullXxx 这条路,老实用 - 或 Select()。
批量插入时 Select() 依然有效但要注意性能
对 CreateInBatches() 同样支持 Select(),但要注意:GORM 会为每批数据生成独立的 INSERT 语句(带相同列清单),不会合并成单条多值 INSERT。
- 如果你跳过的是高频变化字段(如
updated_at),用Select()能避免每次都要计算时间戳 - 但如果跳过的是固定字段,且批次很大,手写原生
INSERT ... VALUES (...), (...)通常比 GORM 批量更省网络和解析开销 - 另外,
Select()不影响AutoMigrate()行为——被-标记的字段仍会建表,只是插入时不出现
真正容易被忽略的是:当你在事务里混合用了 Create() 和 CreateInBatches(),且都依赖同一套 Select() 逻辑,得确保字段列表完全一致,否则可能因列数不匹配导致 MySQL 报错 Column count doesn't match value count。











