gorm踩坑筆記1

背景

最近在用gorm查詢滿足某個條件的所有記錄中最新的記錄,代碼可參考下面的例子,具體查詢語句如下所示,目的是查詢滿足條件host_id=1的所有記錄中按時間倒序,然後返回最新的記錄。

orm.First(&hostStat, "host_id = ?", 1).Order("time desc").Error

完整的case

package main

import (
	"os"
	log "github.com/Sirupsen/logrus"
	"github.com/jinzhu/gorm"
	_ "github.com/mattn/go-sqlite3"
	"time"
)

type HostStat struct {
	Id      int
	HostId  int
	Message string
	Time    time.Time
}

func main() {
	if _,err:=os.Stat("test.db");err==nil{
		os.Remove("test.db")
	}
	orm, err := gorm.Open("sqlite3", "test.db")
	if err != nil {
		log.Errorf("gorm open err:%s", err)
		return
	}
	orm.AutoMigrate(&HostStat{})
	orm.Save(&HostStat{
		HostId:  1,
		Message: "first",
		Time:    time.Now(),
	})
	time.Sleep(1 * time.Second)
	orm.Save(&HostStat{
		HostId:  1,
		Message: "second",
		Time:    time.Now(),
	})
	var hostStatList []HostStat
	if err := orm.Find(&hostStatList).Error; err != nil {
		log.Errorf("gorm find host stat list error:%s", err)
		return
	}

	for _,host:=range hostStatList{
		log.Println(host)
	}

	var hostStat HostStat
	if err := orm.First(&hostStat, "host_id = ?", 1).Order("time desc").Error; err != nil {
		log.Errorf("gorm get hostStat err:%s", err)
		return
	}
	log.Println(hostStat.Message)
}

執行結果,從執行結果中發現,返回了第一條記錄,顯然不是想要的結果,預期返回的應該是第二條記錄,而實際返回的是第一條記錄。

INFO[0001] {1 1 first 2020-02-25 11:55:35.855536 +0800 +0800}
INFO[0001] {2 1 second 2020-02-25 11:55:36.860022 +0800 +0800}
INFO[0001] first

解決過程

排查過程

首先需要分析一下,爲什麼先first,在order會導致直接返回第一條記錄,在下面的代碼中,我打開的gorm的debug,並將order和first的順序換了一下,做對比。

var hostStat1 HostStat

if err := orm.Debug().First(&hostStat1, "host_id = ?", 1).Order("time desc").Error; err != nil {
		log.Errorf("gorm get hostStat err:%s", err)
		return
}
log.Println(hostStat1.Message)
	
var hostStat2 HostStat

if err := orm.Debug().Order("time desc").First(&hostStat2, "host_id = ?", 1).Error; err != nil {
		log.Errorf("gorm get hostStat err:%s", err)
		return
}

log.Println(hostStat2.Message)

輸出結果顯示第二種做法返回了第二條記錄,符合預期。從debug信息裏看到

  • 對於First().Order(“time desc”)的語法,gorm直接忽略了Order的語法
  • 對於Order(“time desc”).First()的語法,結果符合預期。
$ go run main.go
INFO[0001] {1 1 first 2020-02-26 10:57:04.420285 +0800 +0800} 
INFO[0001] {2 1 second 2020-02-26 10:57:05.42642 +0800 +0800} 

(/Users/zhangyi/go/src/goscripts/gorm/main.go:52) 
[2020-02-26 10:57:05]  [0.33ms]  SELECT  * FROM "host_stats"  WHERE (host_id = '1') ORDER BY "host_stats"."id" ASC LIMIT 1
INFO[0001] first                                        

(/Users/zhangyi/go/src/goscripts/gorm/main.go:60) 
[2020-02-26 10:57:05]  [0.32ms]  SELECT  * FROM "host_stats"  WHERE (host_id = '1') ORDER BY time desc,"host_stats"."id" ASC LIMIT 1
INFO[0001] second   

解決方案

對於這種問題最好的解決方案就是手動拼寫sql,使用gorm.Raw(),下面給出了兩種對比的做法。

if err := orm.Debug().First(&hostStat, "host_id = ?", 1).Order("time desc").Error; err != nil {
		log.Errorf("gorm get hostStat err:%s", err)
		return
	}
	log.Println(hostStat.Message)

	if err := orm.Debug().Raw("select * from host_stats where host_id = ? order by time desc limit 1", 1).Scan(&hostStat).Error; err != nil {
		log.Errorf("gorm get hostStat err:%s", err)
		return
	}
	log.Println(hostStat.Message)

輸出結果如下所示,從結果中可以看出使用Raw方法的sql語句和結果符合預期。

$ go run main.go
INFO[0001] {1 1 first 2020-02-25 15:48:17.075904 +0800 +0800}
INFO[0001] {2 1 second 2020-02-25 15:48:18.076935 +0800 +0800}

(/Users/zhangyi/go/src/goscripts/gorm/main.go:52)
[2020-02-25 15:48:18]  [0.33ms]  SELECT  * FROM "host_stats"  WHERE (host_id = '1') ORDER BY "host_stats"."id" ASC LIMIT 1
INFO[0001] first

(/Users/zhangyi/go/src/goscripts/gorm/main.go:58)
[2020-02-25 15:48:18]  [0.27ms]  select * from host_stats where host_id = '1' order by time desc limit 1
INFO[0001] second
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章