Beego ORM 的 Raw 查询必须显式调用 Exec、QueryRow 或 QueryRows 等方法才会执行,仅 o.Raw("...") 仅返回未执行的 RawSeter;Values 系列将结果转为 map[string]string,NULL 值为空字符串;IN 查询需手动拼占位符或改用 Filter("__in");SetArgs 可安全复用 RawSeter。

Beego ORM 的 Raw 查询不是“写完就跑”,必须显式调用 Exec、QueryRow 或 QueryRows 等方法才会真正执行 SQL —— 这是绝大多数人踩坑的第一步。
Raw 查询必须配对调用执行方法
直接 o.Raw("SELECT * FROM user WHERE id = ?", 1) 只返回一个 RawSeter 接口,不发 SQL,也不报错。它只是个“待执行的查询描述”,后续必须接具体动作:
-
Exec():用于INSERT/UPDATE/DELETE,不能查数据;返回受影响行数和错误 -
QueryRow(&v):查单行,v必须是指针,且字段名/顺序需与 SELECT 列严格匹配(大小写敏感);beego 1.1.0+已不支持一次查多个 struct -
QueryRows(&slice):查多行,slice类型必须是[]YourStruct,不能是[]*YourStruct;结构体字段需有对应数据库列名或别名
Values / ValuesList / ValuesFlat:查出来是 map[string]string
当不想定义结构体、或列名动态、或含 NULL 值时,优先用 Values 系列。它们把每行转成 map[string]string,所有值都是字符串,NULL 字段为 ""(空字符串,不是 "NULL"):
-
Values(*[]orm.Params, string):最常用,orm.Params就是map[string]string的别名 -
ValuesList(*[]orm.ParamsList, string):返回二维切片,如[["1","alice"],["2","bob"]],适合前端表格直出 -
ValuesFlat(*orm.ParamsList, string):扁平化一维切片,如["1","alice","2","bob"],慎用,丢失行列结构
示例:var list []orm.Params; o.Raw("SELECT id, name FROM user").Values(&list) → list[0]["id"] 和 list[0]["name"] 都是 string 类型。
IN 查询传参要拆开占位符,不能直接传 slice
Raw("WHERE id IN (?)", []int{1,2,3}) 会报错或查不到结果 —— beego 不自动展开 slice。必须手动拼占位符:
- 固定长度可写死:
Raw("WHERE id IN (?, ?, ?)", 1, 2, 3) - 动态长度需构造:
placeholders := make([]string, len(ids)); for i := range ids { placeholders[i] = "?" }; sql := fmt.Sprintf("WHERE id IN (%s)", strings.Join(placeholders, ",")),再o.Raw(sql, ids...) - 更安全的做法是改用
Values+ 循环过滤,或先用 ORM 的QueryTable().Filter("id__in", ids)
SetArgs 是复用 RawSeter 的唯一可靠方式
如果你有一条高频 SQL(比如日志统计),不要每次 o.Raw(...) 新建对象,而是:
- 先创建一次:
r := o.Raw("SELECT COUNT(*) FROM log WHERE level = ? AND created > ?") - 后续用
r.SetArgs("error", "2026-10-01").QueryRow(&count)或r.SetArgs("warn", "2026-10-20").QueryRow(&count) -
SetArgs返回新RawSeter,原r不变,线程安全 - 注意:它不校验参数个数,传少会 panic,传多则忽略多余参数
最后提醒一句:所有 Raw 查询默认不走 ORM 缓存,也不触发 PreInsert/PostRead 钩子 —— 如果你依赖这些机制,得自己补逻辑。











