You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

能否用sqlc实现灵活动态查询?含过滤、分页与PATCH需求

使用sqlc实现高级查询:过滤、分页与PATCH操作

我正在开发一个项目,打算使用sqlc。我很喜欢这个工具,但找不到实现基础CRUD之外的高级查询示例。我需要实现带WHERE子句的过滤查询、分页参数传递,以及PATCH更新操作。

之前用sqlx实现的方式(如下)写起来繁琐,测试麻烦,多个实体复用逻辑会有大量冗余代码,还容易出错,希望通过sqlc的代码生成来封装这些逻辑。

原sqlx实现方式

1. 过滤查询

var args []string
var vals []interface{}
count := 1

if filter.Name != nil {
    args = append(args, fmt.Sprintf("name=$%d", count))
    vals = append(vals, &filter.Name)
    count++
}
if filter.OrgType != nil {
    args = append(args, fmt.Sprintf("org_type=$%d", count))
    vals = append(vals, &filter.OrgType)
    count++
}
if filter.DeliveryArea != nil {
    args = append(args, fmt.Sprintf("delivery_area=$%d", count))
    vals = append(vals, &filter.DeliveryArea)
    count++
}
if filter.Status != nil {
    args = append(args, fmt.Sprintf("status=$%d", count))
    vals = append(vals, &filter.Status)
    count++
}

query := "select * from contractors"
if count != 1 {
    query = fmt.Sprintf("%s where %s", query, strings.Join(args, " and "))
}
// 后续添加limit和offset...

2. PATCH更新操作

var args []string
var vals []interface{}
count := 1
if payload.Name != nil {
    args = append(args, fmt.Sprintf("name=$%d", count))
    vals = append(vals, payload.Name)
    count++
}
if payload.ContractNum != nil {
    args = append(args, fmt.Sprintf("contract_num=$%d", count))
    vals = append(vals, payload.ContractNum)
    count++
}
if payload.DeliveryArea != nil {
    args = append(args, fmt.Sprintf("delivery_area=$%d", count))
    vals = append(vals, payload.DeliveryArea)
    count++
}

if count == 1 {
    log.Ctx(ctx).Error().Msg("nothing to update")
    return fmt.Errorf("nothing to update")
}

query := fmt.Sprintf("update contractors set %s where id=$%d", strings.Join(args, ", "), id)
// 执行查询...

sqlc实现方案

sqlc支持通过SQL模板和条件参数实现动态查询/更新,完全替代手动SQL拼接,同时保证类型安全和代码复用。

1. 带过滤与分页的查询

在你的SQL文件中定义如下查询:

-- name: ListContractors :many
SELECT * FROM contractors
WHERE 
  (sqlc.narg('name') IS NULL OR name = sqlc.arg('name'))
  AND (sqlc.narg('org_type') IS NULL OR org_type = sqlc.arg('org_type'))
  AND (sqlc.narg('delivery_area') IS NULL OR delivery_area = sqlc.arg('delivery_area'))
  AND (sqlc.narg('status') IS NULL OR status = sqlc.arg('status'))
LIMIT sqlc.arg('limit') OFFSET sqlc.arg('offset');

sqlc会自动生成对应的Go结构体和函数:

type ListContractorsParams struct {
    Name          *string
    OrgType       *string
    DeliveryArea  *string
    Status        *string
    Limit         int32
    Offset        int32
}

func (q *Queries) ListContractors(ctx context.Context, params ListContractorsParams) ([]Contractor, error) {
    // 自动生成的参数绑定与查询执行逻辑
}

使用方式:只需传入有值的过滤字段和分页参数,sqlc会自动忽略NULL参数对应的条件:

// 辅助函数:生成指针类型
func ptr[T any](v T) *T {
    return &v
}

// 调用示例
params := ListContractorsParams{
    Name:     ptr("张三"),
    Limit:    10,
    Offset:   0,
}
contractors, err := db.ListContractors(ctx, params)

2. PATCH动态更新操作

同样在SQL文件中定义更新语句,利用COALESCE保留原有字段值:

-- name: UpdateContractor :execrows
UPDATE contractors
SET
  name = COALESCE(sqlc.narg('name'), name),
  contract_num = COALESCE(sqlc.narg('contract_num'), contract_num),
  delivery_area = COALESCE(sqlc.narg('delivery_area'), delivery_area)
WHERE id = sqlc.arg('id');

sqlc生成的Go代码:

type UpdateContractorParams struct {
    ID            int64
    Name          *string
    ContractNum   *string
    DeliveryArea  *string
}

func (q *Queries) UpdateContractor(ctx context.Context, params UpdateContractorParams) (int64, error) {
    // 自动生成的更新逻辑
}

使用方式:传入要更新的字段和ID,未传入的字段会保留原值。如果没有任何字段更新,执行后影响行数为0,可在业务代码中判断:

params := UpdateContractorParams{
    ID:            123,
    DeliveryArea:  ptr("朝阳区"),
}
rowsAffected, err := db.UpdateContractor(ctx, params)
if err != nil {
    return err
}
if rowsAffected == 0 {
    return fmt.Errorf("nothing to update")
}

关键优势

  • 无需手动拼接SQL,彻底避免SQL注入风险
  • 类型安全:生成的结构体与数据库字段严格对应
  • 代码复用:多个实体可复用相同的SQL模板逻辑
  • 测试简化:只需验证业务参数,无需测试SQL拼接逻辑

内容的提问来源于stack exchange,提问作者nrgx

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 20:07:02