squirrel.builder 不能直接执行,因其仅拼装 sql 和参数,不持连接也不调用 query/exec;须调 tosql() 获取 sql 与 args 后交 db.query 等执行,或用实现 squirrel.runner 的 db(如 sqlx.db)调 builder.queryrow(db)。

squirrel.Builder 为什么不能直接执行
因为 squirrel.Builder 只负责拼 SQL 和参数,不持有数据库连接,也不调用 Query 或 Exec。你拿到的只是 sqlx.Stmt 或原始字符串+参数切片,必须手动交给 db.Query、db.QueryRow 或 sqlx.NamedExec 才能跑起来。
常见错误现象:cannot use builder (type squirrel.SelectBuilder) as type *sql.Stmt —— 说明你试图把 builder 当成已编译语句用了。
- 正确做法:调用
builder.ToSql()拿到query string和args []interface{},再传给db.Query(query, args...) - 更省事:用
squirrel.Runner(比如sqlx.DB实现了squirrel.Runner接口),直接builder.QueryRow(db) - 注意
ToSql()可能 panic,如果 builder 内部有未设置的 required 字段(如没写From()就调Select())
WHERE 条件里嵌套 AND/OR 容易漏括号
squirrel 默认扁平化 AND,不会自动加括号。当你混合 AND 和 OR 时,逻辑优先级靠你自己控制,否则生成的 SQL 会和预期不符。
使用场景:查“状态是 active 或 pending,且创建时间在最近 7 天内”——这里 OR 必须先算,再整体 AND 时间条件。
- 错误写法:
squirrel.Select("*").From("users").Where("status = ?", "active").Where("status = ?", "pending").Where("created_at > ?", weekAgo)→ 实际是status='active' AND status='pending' AND created_at > ?,永远为 false - 正确写法:用
squirrel.Or和squirrel.And显式分组:squirrel.Or{squirrel.Eq{"status": "active"}, squirrel.Eq{"status": "pending"}},再包进And - 参数差异:
squirrel.Eq是 map,squirrel.Gt/squirrel.Like是 key-value 形式,别混用类型
IN 查询传 slice 参数必须用 squirrel.PlaceholderFormat
PostgreSQL 和 MySQL 对 IN (?, ?, ?) 的占位符格式要求不同,squirrel 默认用 风格(PostgreSQL),直接丢给 MySQL 会报错 ERROR 1064: You have an error in your SQL syntax。
性能影响:不用 PlaceholderFormat 强制适配,ToSql() 生成的 SQL 在目标 DB 上根本无法 prepare。
- MySQL 场景:构建 builder 后立刻调
builder.PlaceholderFormat(squirrel.Question) - SQLite 同样要用
squirrel.Question;PostgreSQL 保持默认squirrel.Dollar即可 - 别在全局改
squirrel.DefaultPlaceholderFormat,容易污染其他 builder
UPDATE/DELETE 不带 WHERE 是高危操作
squirrel 不做安全拦截,Update("users").SetMap(...) 如果忘了 Where(),生成的就是无条件更新全表——线上环境可能瞬间误伤。
容易踩的坑:测试时用小数据集看不出问题,上线后数据量大了才暴露;或者 WHERE 条件变量为 nil / 空 map,squirrel.Where(nil) 不报错也不生效。
- 建议开发期加断言:
if len(builder.WhereClause()) == 0 { log.Fatal("missing WHERE in UPDATE") } - 用
squirrel.Eq{"id": id}而不是squirrel.Eq{"id": nil},后者会被忽略 - DELETE 尤其敏感,建议所有 DELETE 操作强制走二次确认流程(比如日志打出来 + 人工 review)
最麻烦的不是语法写不对,而是 WHERE 条件动态拼接时分支遗漏——比如 status 为空时跳过 WHERE,但业务上它本该是必填项。这种逻辑得靠单元测试卡住,squirrel 本身不救。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











