time 包 和 gorm 执行的问题
知识点
我们都知道,使用 golang 的 time 包可以对时间格式进行转换,比如:
- time.xxx.Format() 可以将 time.time 格式转换为 string 格式
- time.Parse 可以将 string 格式转换为 time.time 格式
坑点
time.Parse 默认转出来是 +0000 UTC 时间,不是中国东八区的 +0800 CST 时间,传给数据库执行的时候,会出现时间推迟 8 小时的情况,且不好排查(因为 SQL debug 打印出来的看不出异常,拿着语句直接去数据库上可以执行)。
在尝试了多种 SQL 姿势之后,还是一样的效果,最后连上 MySQL 分析实际执行的语句才发现异常。
到 MySQL 上查看执行的查询语句的方法:
pager grep [ip] # 设置过滤出执行脚本的机器 IP
show full processlist # 查看 MYSQL 现在正在执行的语句
解决
需要用 time.ParseInLocation 方法替换 time.Parse ,这样转换出来的时间就是 +0800 CST 时间
执行原生 SQL 问题
知识点
在执行一些复杂语句或者排查问题的时候,会直接使用 原生 SQL ,这样可能会直观一些;
坑点
原生 SQL 执行查询时,默认只能选出一行数据来,需要查询多行时,得多一步处理
https://jasperxu.github.io/gorm-zh/advanced.html#sb
解决
var userInfo []*UserInfo
// 原生SQL
rs := "SELECT name,age,sum(val) as num FROM `t_user_stat_20221026` WHERE (uid = xxx and dt > '2022-10-26 00:20:00' and dt <= '2022-10-26 09:40:00' and op_code = 'xxx') GROUP BY name,age"
rows, _ := db.Debug().Raw(rs).Rows()
defer rows.Close()
for rows.Next() {
var uu UserInfo
db.ScanRows(rows, &uu)
// 需要一行一行的 scan 解析出来,然后再 append 到最后返回的数据里
userInfo = append(userInfo, &uu)
}
fmt.Println(UserInfo)
for _, u := range UserInfo {
fmt.Println(*u)
}
传入和返回值问题
注意点
传入接收结果集的变量只能为 Struct 类型或 Slice 类型;
- 当传入变量为 Slice 类型时,任何条件下均不会抛出 ErrRecordNotFound 错误;
- 当传入变量为 Struc 类型时,如果检索出来的数据为 0 条,会抛出 ErrRecordNotFound 错误。
传入接收结果集的变量为 Slice 时的写法:
func funcName(xxx) ([]*structA,error) {
var aa []*structA
query := db.Table(xx).Select(xxx).Where(xxx).Find(&aa)
if query.Error != nil{
return nil,query.Error
}
if query.RecordNotFound() {
return nil,nil
}
return aa, nil
}
传入接收结果集的变量为 struct 时的写法:
func funcName(xxx) (*structA,error) {
var aa structA
query := db.Table(xx).Select(xxx).Where(xxx).Find(&aa)
if query.Error != nil{
return nil,query.Error
}
if query.RecordNotFound() {
return nil,nil
}
return &aa, nil
}
注意:传入值为 struct 的写法,容易写错,很容易报 unsupported destination, should be slice or struct 错误
https://juejin.cn/post/6844903971501375496
https://www.cnblogs.com/taoshihan/p/16044386.html