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 开发者必须守住这条底线。
相关推荐
审判长烧鸡17 小时前
GO时区【2】跨时区应用
go·存储·时区
审判长烧鸡18 小时前
Go结构体与指针【2】接收者应该怎么用
go·指针·结构体·接收者
王中阳Go1 天前
2026年了,还在纠结后端转AI要不要死磕Python?试试Go吧
后端·go·ai编程
审判长烧鸡1 天前
GO结构体与指针【1】什么时候用指针
go·指针·结构体
审判长烧鸡1 天前
GO错误处理【2】os.Exit(1)/panic/返回err的应用场景
go·异常处理·panic
审判长烧鸡1 天前
GO时区【4】PostgreSQL时区
postgresql·go
审判长烧鸡1 天前
GO时区【3】字段与连接设置
postgresql·go
审判长烧鸡1 天前
GO错误处理【1】不用try-catch用什么?
go·异常处理·try-catch·panic·fatal·os.exit
Go_error3 天前
Go database/sql 基于临时 channel 传递连接
后端·go