sqlc 可空查询参数怎么生成指针或 Null 类型
来源:17golang原创
时间:2026-11-05 12:56:17 440浏览 收藏
sqlc 生成可空查询参数时,关键不是把整个参数结构体改成指针,而是先在 SQL 中使用 sqlc.narg() 明确参数可为空。默认配置通常会得到 sql.NullString、sql.NullInt64 这类 Null 类型;在支持的 PostgreSQL pgx/v4、pgx/v5 或 SQLite 场景中,再打开 emit_pointers_for_null_types: true,才可以让可空类型表现为 *string、*int64。
官方地址:https://docs.sqlc.dev/en/stable/
下面用一个“只更新传入字段”的查询做对照,重点看 SQL 标记、生成配置、Params 字段和调用方式四个位置。
先把可空参数写在 SQL 里
sqlc 会根据 SQL 上下文推断参数是否可空。如果希望一个参数无论上下文如何都按可空参数生成,就使用 sqlc.narg。它和 sqlc.arg 一样可以给参数命名,但额外覆盖了可空性推断。
-- name: UpdateAuthor :one
UPDATE authors
SET
name = COALESCE(sqlc.narg('name'), name),
bio = COALESCE(sqlc.narg('bio'), bio)
WHERE id = sqlc.arg('id')
RETURNING *;
这里的 name 和 bio 是可空参数,id 仍然是普通的非空参数。COALESCE 只是这个更新场景的示例,文章关注的是生成出来的参数类型,不讨论数据库字段本身的 NULL 设计。
先分清三个容易混淆的设置
生成类型之前,先把三个配置分开。它们作用在不同层级:
| 配置或写法 | 改变什么 | 不会改变什么 |
|---|---|---|
sqlc.narg() | 把 SQL 参数标为 nullable | 不会直接决定使用指针还是 Null 包装 |
emit_pointers_for_null_types | 让支持的可空类型生成 *T | 不会把普通非空字段改成指针 |
emit_params_struct_pointers | 让调用方法接收 *UpdateAuthorParams | 不会把其中的字段改成 *string |
因此,看到 *UpdateAuthorParams 时不要误以为 Name 一定是 *string。参数结构体的指针和结构体字段的可空类型是两条独立配置链。

默认 Null 类型:调用时看 Valid
不打开指针选项时,常见生成结果如下。以 database/sql 的字符串为例,字段通过 Valid 区分“有值”和“应传 NULL”。
type UpdateAuthorParams struct {
// Valid=false 时由驱动传递 NULL,避免把空字符串误当成缺省值。
Name sql.NullString
// 同一个规则适用于第二个可选字段。
Bio sql.NullString
// id 由 sqlc.arg 生成普通的非空参数。
ID int64
}
params := UpdateAuthorParams{
// Valid=true 才会把 String 作为参数值传入。
Name: sql.NullString{String: "Ada", Valid: true},
// Valid=false 表示本次不更新 bio。
Bio: sql.NullString{Valid: false},
ID: 7,
}
author, err := queries.UpdateAuthor(ctx, params)
if err != nil {
// 保留生成方法返回的数据库错误,交给上层处理。
return err
}
_ = author
默认 Null 类型的优点是语义明确:值和有效性放在同一个字段里。代价是调用方需要构造包装类型,不能直接把普通字符串赋给 Name。
支持的场景下改成指针类型
如果项目使用 PostgreSQL 的 pgx/v4、pgx/v5,或使用 SQLite,可以在 Go 生成配置中打开指针选项。配置重点如下:
version: "2"
sql:
- engine: postgresql
schema: db/schema.sql
queries: db/query.sql
gen:
go:
package: db
out: internal/db
sql_package: pgx/v5
# 让可空字符串等类型生成 *string,而不是 Null 包装类型。
emit_pointers_for_null_types: true
在这个支持范围内,前面的可空参数可以表现为类似下面的结构:
type UpdateAuthorParams struct {
// nil 表示本次参数为 NULL;非 nil 指向要传入的字符串。
Name *string
// nil 让调用方不必额外构造 NullString。
Bio *string
// 非空参数仍然是普通值类型。
ID int64
}
name := "Ada"
params := UpdateAuthorParams{
// 传地址表示有值,传 nil 表示 NULL。
Name: &name,
Bio: nil,
ID: 7,
}
_, err := queries.UpdateAuthor(ctx, params)
if err != nil {
// 生成方法的错误仍按普通 Go 错误处理。
return err
}
不要把这个选项当成所有数据库和驱动的通用开关。sqlc 当前文档对支持范围有明确限制;如果驱动不在支持范围内,优先接受默认 Null 类型,或改用明确的类型覆盖。
需要业务类型时用 nullable override
如果你想用自己的类型表达可空值,配置 overrides 时要明确写 nullable: true。如果目标类型需要指针,再在 go_type 中写 pointer: true。只写非空覆盖不会自动覆盖可空参数。
version: "2"
sql:
- engine: postgresql
schema: db/schema.sql
queries: db/query.sql
gen:
go:
package: db
out: internal/db
sql_package: pgx/v5
overrides:
# 非空 uuid 和可空 uuid 要分别声明覆盖规则。
- db_type: uuid
nullable: true
go_type:
import: github.com/google/uuid
type: UUID
pointer: true
这个方案适合团队已经统一使用某个值对象的项目。它的注意点是:db_type 覆盖默认只匹配一种可空性;同一数据库类型如果既有非空字段又有可空字段,通常需要分别写两条覆盖规则。
调用前按生成结果做一次核对
配置保存后,重点不是盯着配置面板,而是打开生成文件确认 Params 结构体。可以按下面顺序核对:
- SQL 中可选参数是否确实使用了
sqlc.narg。 - 指针模式下字段是否为
*T,默认模式下是否为对应的sql.NullT。 - 只有
emit_params_struct_pointers时,方法参数是否是*UpdateAuthorParams,字段类型是否保持原配置。 - 调用代码是否把
nil或Valid=false当成同一个“传 NULL”语义,而不是把空字符串当成缺省值。

几个常见误区
把 emit_params_struct_pointers 当成字段指针开关。它只影响参数结构体的传递方式,不能替代 emit_pointers_for_null_types 或 nullable override。
只写 sqlc.arg 期待得到可空参数。sqlc.arg 主要负责命名和参数标记;需要强制 nullable 时用 sqlc.narg。
在不支持的驱动上强行期待 *T。先看 sqlc 配置文档对引擎和 sql_package 的限制;不满足时,sql.NullString 等默认类型往往是更可靠的生成结果。
override 漏写 nullable。同一个数据库类型的非空和可空映射不是一条规则自动覆盖,必须按可空性分别配置。
结论
想让 sqlc 生成可空查询参数,先用 sqlc.narg 表达参数语义,再根据驱动选择类型表达:默认使用 sql.NullT,支持的 PostgreSQL pgx 或 SQLite 可用 emit_pointers_for_null_types 生成 *T,特殊业务类型则用带 nullable: true 的 override。最后只检查生成的 Params 字段和调用代码,就能避免把结构体指针、字段指针和 Null 包装混为一谈。
相关问题
sqlc.narg 和 sqlc.arg 有什么区别?前者会强制把参数标记为 nullable,后者主要用于命名和普通参数标记。
为什么打开 emit_params_struct_pointers 后仍然是 sql.NullString?因为它只改变 Params 结构体本身的传参形式,不改变字段的可空类型。
指针模式下如何表达“不更新这个字段”?对支持的生成类型传入 nil;默认 Null 模式则让对应字段的 Valid 为 false。
-
187 收藏
-
323 收藏
-
172 收藏
-
130 收藏
-
320 收藏
-
272 收藏
-
388 收藏
-
238 收藏
-
498 收藏
-
302 收藏
-
186 收藏
-
327 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习