V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
Ansen
V2EX  ›  Go 编程语言

请教一下 Gorm 的 Count 问题

  •  
  •   Ansen · 2019-09-04 18:43:23 +08:00 · 5967 次点击
    这是一个创建于 1689 天前的主题,其中的信息可能已经有所发展或是发生改变。

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

    @seaguest db 对象没有加条件,下面的代码片段可以看到,出了 midel 其它相关基本都贴出来了
    ZSeptember
        6
    ZSeptember  
       2019-09-04 22:14:54 +08:00
    把 SQL 打印一下,看看。
    Ansen
        7
    Ansen  
    OP
       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
        8
    JimmyTinsley  
       2019-09-05 09:25:31 +08:00   ❤️ 1
    @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
        9
    Ansen  
    OP
       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
        10
    JimmyTinsley  
       2019-09-05 09:52:24 +08:00
    @Ansen #9 哈哈解决问题了就好
    Ansen
        11
    Ansen  
    OP
       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
        12
    JimmyTinsley  
       2019-09-05 10:28:21 +08:00
    @Ansen #11 看文档里面确实是可以这样用, subscribe 了这个 issue 看看开发者怎么说
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   1117 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 23:34 · PVG 07:34 · LAX 16:34 · JFK 19:34
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.