
在 PostgreSQL 中,sqlx.DB.Exec() 无法返回插入后的行数据;必须使用 RETURNING 子句配合 QueryRowx() 和 StructScan() 才能安全、原子地获取完整新记录。
在 postgresql 中,`sqlx.db.exec()` 无法返回插入后的行数据;必须使用 `returning` 子句配合 `queryrowx()` 和 `structscan()` 才能安全、原子地获取完整新记录。
PostgreSQL 不支持 LastInsertId()(该方法在 MySQL 中可用),因此不能依赖 sql.Result 的 LastInsertId() 获取主键或整行数据。正确做法是:*在 INSERT 语句末尾显式添加 `RETURNING (或指定字段列表)**,再通过sqlx.QueryRowx()执行,并用StructScan()` 将结果直接映射到 Go 结构体中。
以下是一个典型示例,展示如何插入用户并立即获取包含自动生成字段(如 id、created_at)的完整行:
type User struct {
ID int `db:"id"`
Name string `db:"name"`
Email string `db:"email"`
CreatedAt time.Time `db:"created_at"`
}
func CreateUser(db *sqlx.DB, name, email string) (*User, error) {
var user User
err := db.QueryRowx(
"INSERT INTO users (name, email, created_at) VALUES ($1, $2, NOW()) RETURNING *",
name, email,
).StructScan(&user)
if err != nil {
return nil, fmt.Errorf("failed to insert and retrieve user: %w", err)
}
return &user, nil
}
✅ 关键要点:
-
RETURNING *是 PostgreSQL 特有语法,确保返回插入后数据库实际写入的完整行(含默认值、序列生成的id、触发器更新字段等); - 必须使用
QueryRowx()(而非Exec()),因为它可处理单行结果集; -
StructScan()自动按db标签匹配字段,要求结构体字段与表列名/别名严格对应(大小写敏感,取决于数据库配置); - 若仅需主键,可简化为
RETURNING id并用Scan(&id);但RETURNING *更健壮,避免因结构体字段变更导致静默失败。
⚠️ 注意事项:
- 在事务中执行时(如需关联插入多张表),务必使用
*sqlx.Tx的QueryRowx(),并在出错时调用Rollback(); -
RETURNING仅对当前INSERT生效,不适用于批量插入(INSERT ... SELECT或VALUES (...), (...)多行需额外处理); - 确保结构体字段类型与数据库列类型兼容(例如
sql.NullString对应可能为 NULL 的文本列)。
总之,不要将应用层 user 结构体视为“唯一真相”——数据库可能通过默认值、触发器或生成列修改数据。始终以 RETURNING 返回的实际行作为最终状态,才能保证数据一致性与可追溯性。










