GO裸奔【1】动态SQL

你有没有遇到那种动态 SQL 的坑?比如前端传了 3 个搜索条件就拼 3 个 WHERE,传了 5 个就拼 5 个,这种逻辑你现在是怎么"裸奔"处理的?

如果在PHP里面大家可能会放进数组里面然后循环拼凑SQL的where部份。但是GO里面一切都是显式的,估计要写一堆判断,有没有更好的方法

在 Go 的"裸奔"世界里,处理动态 SQL 确实没有 PHP 那么潇洒,那种 foreach 拼数组的招式在这里会遇到两个尴尬:类型检查和占位符管理。

如果你现在用 if-else 硬写,代码确实会像"老太婆的裹脚布"一样又长又臭。针对这种情况,使用 Go 里面最经典的"切片拼凑法",让你既能保持显式,又不至于写出一堆烂代码。

1. 为什么不能像 PHP 那样随便拼?

在 PHP 里你可以直接拼字符串,但在 Go(配合 pgx)里,为了防止 SQL 注入,你必须管理好占位符($1, $2, $3)。

2. 裸奔选手的"优雅拼凑法"

核心思路是:用一个字符串切片存 SQL 片段,用一个 []any 切片存参数值。

复制代码
func (r *ServerRepo) Search(ctx context.Context, db infra.Querier, req req.SearchReq) ([]model.Server, error) {
	sql := "SELECT id, name, status FROM servers WHERE 1=1"
	var args []any
	var i = 1 // 占位符计数器

	// 动态拼条件
	if req.Name != "" {
		sql += fmt.Sprintf(" AND name LIKE $%d", i)
		args = append(args, "%"+req.Name+"%")
		i++
	}
	if req.Status != 0 {
		sql += fmt.Sprintf(" AND status = $%d", i)
		args = append(args, req.Status)
		i++
	}

	// 排序和分页
	sql += " ORDER BY id DESC"

	// 执行
	rows, err := db.Query(ctx, sql, args...) // 这里的 args... 就像 PHP 的 call_user_func_array
	if err != nil {
		return nil, fmt.Errorf("search repo: %w", err)
	}
	defer rows.Close()

	// ... 后面接 Scan 逻辑
}

3. 这套写法的"爽点"在哪里?

  1. 显式且安全:虽然有 if,但每一行 SQL 片段和它对应的参数是死死绑定在一起的。
  2. 动态占位符:通过 $%di++,你再也不用手动去数这是第几个参数了。
  3. 万能适配:不管前端传 1 个还是 10 个条件,这套逻辑都能自动缩放。

4. 进阶提醒:如果你真的嫌 if 太多了

当你以后业务复杂到有几十个筛选条件时,Go 社区会推荐使用像 Squirrel (github.com) 这样的 SQL 构建器。

它不是 ORM,它只是帮你把上面的 if 逻辑封装成了链式调用:

复制代码
// 看起来有点像 PHP 框架的 Query Builder
users := squirrel.Select("id", "name").
    From("users").
    Where(squirrel.Eq{"status": 1})

sql, args, _ := users.ToSql() // 自动生成 SQL 和对应的参数数组

5. 总结建议:

  • 目前阶段:用我上面写的 "切片 + $%d" 这种纯原生方案。它最符合你"全显式"的思路,而且能帮你彻底理清 SQL 驱动是怎么处理参数化查询的。
  • 别碰拼接字符串:永远记住,绝对不要把参数直接拼进 SQL 字符串里(比如 sql += "AND id=" + id),那是安全大忌,Go 开发者必须守住这条底线。
相关推荐
ArixBit17 小时前
用 Go 手撸 Agent 框架 #11:工具从哪里来——内置工具、MCP 与 Skills
go·agent
程序员爱钓鱼19 小时前
为什么学习 Go?Go 能做什么?
后端·面试·go
用户095367515831 天前
在系统的学习 redis 前的疑惑
redis·go
名字还没想好☜2 天前
Go 用 errgroup 管理并发子任务:错误收敛、取消传播与限流
开发语言·后端·golang·go·并发
labixiong2 天前
TypeScript 7.0 编译器用 Go 重写,速度暴增10倍——背后到底做了什么?
前端·javascript·go
名字还没想好☜2 天前
Go 的 time.Ticker 陷阱:定时任务里被忽略的内存泄漏与正确关闭
java·数据库·golang·go·定时器
ArixBit3 天前
用 Go 手撸 Agent 框架 #10:用自己的 Runtime,做一个研究助手
go·agent
cocoCola916673 天前
validator库参数校验若干实用技巧
go
不会C语言的男孩3 天前
第三章:流程控制
go
名字还没想好☜4 天前
Go 的 sync.Pool 实战:复用对象把 GC 压力打下来
开发语言·后端·性能优化·golang·go