请教一下 Gorm 的 Count 问题

2019-09-04 18:43:23 +08:00
 Ansen

Count 方法这里怎么会多出一个 and 条件,导致结果不正确呢,我代码有问题么?

go 新手 求指点

代码片段

util 中部分定义:

package util

type Result struct {
	Code uint        `json:"code"`
	Data interface{} `json:"data"`
}

// NewResult creates a result with Code=0, Msg="", Data=nil.
func NewResult() *Result {
	return &Result{
		Code: 200,
		Data: nil,
	}
}
type JobRequestParam struct {
	ID      []uint `form:"id" binding:"required"`
	OpsID   uint   `form:"ops_id" binding:"required"`
	RunTime string `form:"run_time"`
}

查询方法

package service

import (
	"OpsSimon/util"
	"log"
	"sync"
)

// AssetsService 资产对象
type AssetsService struct {
	mutex *sync.Mutex
}

// Assets 资产实例
var Assets = &AssetsService{
	mutex: &sync.Mutex{},
}
// GetList 列表查询
func (srv *AssetsService) GetList(result interface{}, id []uint) (total int) {
	err := db.Where(id).Find(result).Count(&total).Error
	if err != nil {
		log.Println(err.Error())
	}
	return total
}

执行

func doJob(c *gin.Context) {
	result := util.NewResult()
	defer c.JSON( http.StatusOK, result)

	var arg util.JobRequestParam
	err := c.BindJSON(&arg)
	if err != nil {
		result.Code = http.StatusBadRequest
		result.Data = err.Error()
		return
	}
	log.Println(arg.ID)
	log.Println(arg.OpsID)
	log.Println("len:", len(arg.ID))

	var host model.AssetsHosts
	total := service.Assets.GetList(&host, arg.ID)
	log.Println("total:", total)

	if total != len(arg.ID) {
		result.Code = http.StatusNotFound
		result.Data = "Some host not found!"
		return
	}

}
5975 次点击
所在节点    Go 编程语言
12 条回复
seaguest
2019-09-04 19:10:44 +08:00
建议提问题的时候简明扼要一点,这里无关的东西太多了。
我的印象中 Find,和 Count 应该分开用吧,你这样想一下查两个值应该有点问题。
开一下 orm 的 log,测试两下不就出来了么。
seaguest
2019-09-04 19:15:07 +08:00
验证了一下,这么用也是对的,gorm 拆分成两个 sql 语句了。
问题应该在与你的 db 对象,前面已经加了条件了。
JimmyTinsley
2019-09-04 19:23:55 +08:00
红框里的应该是 Where(id)这一句导致的吧
Ansen
2019-09-04 19:28:16 +08:00
@seaguest 并不行,分开使用一样不行,我是尝试过才来提问的,没有拿来主义
Ansen
2019-09-04 19:30:37 +08:00
@JimmyTinsley 我看官方文档是可以这样用的

@seaguest db 对象没有加条件,下面的代码片段可以看到,出了 midel 其它相关基本都贴出来了
ZSeptember
2019-09-04 22:14:54 +08:00
把 SQL 打印一下,看看。
Ansen
2019-09-05 09:24:52 +08:00
@ZSeptember #6 图里面有,然后我再贴一下多出 and 的那条语句

```sql

SELECT count(*) FROM `ops_assets_hosts` WHERE `ops_assets_hosts`.`deleted_at` IS NULL AND `ops_assets_hosts`.`id` = 3 AND ((`ops_assets_hosts`.`id` IN (1,2,3)))
```
JimmyTinsley
2019-09-05 09:25:31 +08:00
@Ansen #5 用 goland 帮你 debug 了一下
如果按照你的写法 `db.Where(id).Find(result).Count(&total).Error` 会产生两次 sql 查询, 就像你日志里面打印的两条一样. Find(result)这个方法本身会 `select * from xxx where xxx in xxx` 查询一遍结果, 然后当你链式调用到 Count(&total)的时候, 实际上这个时候的 DB 对象本身是含有 Value 了的, Value.ID 是根据你 where 条件的查询得到结果的最后一条的 ID.
没有再深入看 gorm 里面的代码, 我猜里面是有一个循环赋值的过程, 当你用 Count(&total)进行查询的时候, DB 对象已经被 Find(result)循环赋值完, 成为 Find(result)结果的最后一条, 所以会多出来一个 and 条件. 如果你试试把 arg.ID 换成[1,2,3,4,5], 那这个 where 条件应该会变成 and id = 5.
表述的可能不太清楚, 你可以自己试试 debug, 把断点打在 Count 方法的`return s.NewScope(s.Value).count(value).db
`这一行就可以了~
Ansen
2019-09-05 09:39:28 +08:00
@JimmyTinsley #8 非常感谢,我去验证了一下,就是你说的这个问题

如果我先 Find 再 Count 必定会多出一个 and id = arg.ID [-1] 的条件出来

我将代码改成下面这样,结果和 sql 语句就是正确的
err := db.Model(result).Where(id).Count(&total).Error
err = db.Where(id).Find(result).Error
JimmyTinsley
2019-09-05 09:52:24 +08:00
@Ansen #9 哈哈解决问题了就好
Ansen
2019-09-05 09:57:10 +08:00
@JimmyTinsley #10
官方文档里面说可以像我之前那个用

http://gorm.io/docs/query.html#Count

感觉应该是 gorm 的 bug,我提了个 issues 看看官方杂说

https://github.com/jinzhu/gorm/issues/2647
JimmyTinsley
2019-09-05 10:28:21 +08:00
@Ansen #11 看文档里面确实是可以这样用, subscribe 了这个 issue 看看开发者怎么说

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/597974

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX