горм наступает на яму отмечает 1

Go

задний план

В последнее время gorm используется для запроса последней записи среди всех записей, которые соответствуют определенному условию. Код может относиться к следующему примеру. Конкретный оператор запроса выглядит следующим образом. Цель состоит в том, чтобы запросить все записи, которые соответствуют условию host_id=1. в обратном хронологическом порядке, а затем вернуть последнюю запись.

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

полное дело

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 по порядку приведет к прямому возврату первой записи.В следующем коде я открываю дебаг горма, и меняю порядок порядок и первый для сравнения.

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().Order("time desc").Find(&hostStat, "host_id = ?", 1).Limit(1).Error; err != nil {
        log.Errorf("gorm get hostStat err:%s", err)
        return
}
log.Println(hostStat.Message)

Выходные результаты показывают, что оба метода возвращают первую запись, как видно из отладочной информации.

  • Что касается синтаксиса First().Order("time desc"), то gorm напрямую игнорирует синтаксис Order
  • Для синтаксиса Order("time desc").First(), gorm добавляет ограничение WHERE("id" = '1'). Эта операция потрясла меня и напрямую добавила условное ограничение id=1. В этом случае , Запись с id=2 не будет найдена.
$ go run main.go
INFO[0001] {1 1 first 2020-02-25 15:38:30.467259 +0800 +0800}
INFO[0001] {2 1 second 2020-02-25 15:38:31.470624 +0800 +0800}

(/Users/zhangyi/go/src/goscripts/gorm/main.go:52)
[2020-02-25 15:38:31]  [0.34ms]  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:38:31]  [0.26ms]  SELECT  * FROM "host_stats"  WHERE ("id" = '1') AND ((host_id = '1')) ORDER BY time desc
INFO[0001] first

решение

Лучшее решение для такого рода проблем - написать 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)

Выходные результаты следующие: Из результатов видно, что операторы 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