记录gorm union和软删除一起使用遇到的一个奇怪问题
Posted YuNansen
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了记录gorm union和软删除一起使用遇到的一个奇怪问题相关的知识,希望对你有一定的参考价值。
众所周知,我们在用go写web服务的时候,gorm是我们最常用的一个orm框架。最近我却遇到了一个奇怪的问题,当我在使用union的时候,如果触发了gorm的软删除的话,最终gorm帮我们执行的sql会有语法错误。
首先在这里我先介绍下gorm的软删除:gorm文档
软删除
如果一个 model 有 DeletedAt 字段,他将自动获得软删除的功能! 当调用 Delete 方法时, 记录不会真正的从数据库中被删除, 只会将DeletedAt 字段的值会被设置为当前时间。
db.Delete(&user)
UPDATE users SET deleted_at="2013-10-29 10:23" WHERE id = 111;
// 批量删除
db.Where("age = ?", 20).Delete(&User)
UPDATE users SET deleted_at="2013-10-29 10:23" WHERE age = 20;
// 查询记录时会忽略被软删除的记录(这里如果我使用了union,那么就会有奇怪的语法错误,见下文)
db.Where("age = 20").Find(&user)
SELECT * FROM users WHERE age = 20 AND deleted_at IS NULL;
// Unscoped 方法可以查询被软删除的记录
db.Unscoped().Where("age = 20").Find(&users)
SELECT * FROM users WHERE age = 20;
物理删除
// Unscoped 方法可以物理删除记录
db.Unscoped().Delete(&order)
DELETE FROM orders WHERE id=10;
我的代码
首先在我操作的表里有deleted_at字段,那么就会触发gorm的软删除。
deleted_at timestamp null,
我的sql语句如下:
err := sqlDB.tiDb.Raw("(select `id`, `name`, `real_name`, `register_ip`, `created_at`, `last_login_ip`, `last_login_at`, `top_id` from `members` where `real_name` = ? and `id` != ?)"+
" union "+
"(select `id`, `name`, `real_name`, `register_ip`, `created_at`, `last_login_ip`, `last_login_at`, `top_id` from `members` where `register_ip` = ? and `id` != ?) "+
"order by `created_at` desc, `id` desc "+
"limit ? offset ?",
member.RealName, member.ID, member.RegisterIp, member.ID, pageSize, (page-1)*pageSize).Find(&members).Error
当在我的生产环境执行这个sql的时候,报了如下错误:
Error 1064: You have an error in your SQL syntax; check the manual that corresponds to your TiDB version for the right syntax to use line 1 column 6 near \\"WHERE members.deleted_at IS NULL AND (((select id, name, real_name, register_ip, created_at, last_login_ip, last_login_at, top_id from members where real_name = ? and id != ?) union (select id, name, real_name, register_ip, created_at, last_login_ip, last_login_at, top_id from members where register_ip = ? and id != ?) order by created_at desc, id desc limit ? offset ?)\\
这里可以看出,gorm自动为我拼接了(((,这里明显括号的数量是不正确的(多了一个括号),所以触发了SQL syntax,无法执行。那么既然这是个gorm的小错误,我们可以通过源码修复或者向gorm官方提出bugfix。也可以避免软删除(前提是我的业务不需要用到软删除)。
那么我的改动就更简单了:
glog.Info("start GetRiskUnion")
err := sqlDB.tiDb.Unscoped().Raw("(select `id`, `name`, `real_name`, `register_ip`, `created_at`, `last_login_ip`, `last_login_at`, `top_id` from `members` where `real_name` = ? and `id` != ?)"+
" union "+
"(select `id`, `name`, `real_name`, `register_ip`, `created_at`, `last_login_ip`, `last_login_at`, `top_id` from `members` where `register_ip` = ? and `id` != ?) "+
"order by `created_at` desc, `id` desc "+
"limit ? offset ?",
member.RealName, member.ID, member.RegisterIp, member.ID, pageSize, (page-1)*pageSize).Find(&members).Error
这里也就是我避免了使用软删除,也就是如上面文档列出的Unscoped()。这样子,gorm当然不会为我们拼接如下sql了:
WHERE members.deleted_at IS NULL
这里还要强调一点,当时我的测试环境是mysql,并没有报语法错误。但是生产环境是tidb,报了这个语法错误。这也说明tidb对于sql语法问题检验的更严格吧。
我已经给gorm官方提了issue,当然这个问题应用的场景也比较少(毕竟是软删除和union同时在gorm中使用),希望他们能有空能帮忙修改下吧。这是我的issue地址
PS:我推荐一个学习golang的视频,感觉讲的还是很好的(需fanqiang):带你21周搞定Go语言
以上是关于记录gorm union和软删除一起使用遇到的一个奇怪问题的主要内容,如果未能解决你的问题,请参考以下文章