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 开发者必须守住这条底线。
相关推荐
审判长烧鸡11 小时前
【Go工具】go-playground是什么组织?官方的?
开发语言·安全·go
别样的感动12 小时前
我写了一个 Go 框架:用 DSL 替代 ORM,代码体积减半,开发效率翻倍
go
明月_清风16 小时前
Go语言空接口与类型断言完全指南:从"万能容器"到"类型还原"
后端·go
蓝宝石的傻话19 小时前
security-collector-exporter:用Prometheus 解决 Linux 的安全审计
go
tyung20 小时前
Go 手写二叉堆优先队列:避开 container/heap 的性能陷阱
数据结构·后端·go
审判长烧鸡2 天前
【PHPer转Go】fmt vs log/slog
go·php
漓漾li2 天前
每日面试题(2026-05-20)- GO AI agent全栈
后端·架构·go
.魚肉2 天前
Raft 共识算法 · 演示系统(多终端)
算法·go·raft·分布式系统
审判长烧鸡2 天前
【Go工具】go-playground除了validator还有哪些常用的库
go·web
审判长烧鸡2 天前
Go 新版核心知识点合集(适配 Go1.18+ 含泛型 + 断言 + 接口 + 指针接收者全套)
go