专栏首页hotqin888的专栏关于gorm多表联合查询(left join)的小记

关于gorm多表联合查询(left join)的小记

Golang很流行,但是有些方面资料很少而且不详实,譬如:gorm的联合查询,当然,也不推荐复杂语句使用orm模型。

现将自己总结的写法和遇到的坑记录如下: Golang要求使用“驼峰命名法”,比如systemId,因为我以前用的是Python,使用Django的orm序列化后返回的参数和数据库表字段一致,基于这个不适合Go的思路,我将表字段也建成了systemId,和struct映射参数相同。(其实表字段应该命名为system_id)

一、下面建两张表,用于联合查询(以left join示例)

MySQL > desc go_system_info;
+——————+——————-+———+——-+——————-+————————+
| Field | Type | Null | Key | Default | Extra |
+——————+——————-+———+——-+——————-+————————+
| id | int(11) | NO | PRI | NULL | auto_increment |
| systemId | varchar(30) | NO | MUL | NULL | |
| systemName | varchar(50) | NO | | defaultNull | |
+——————+——————-+———+——-+——————-+————————+
3 rows in set (0.01 sec)

MySQL > desc go_service_info;
+——————-+——————-+———+——-+——————-+————————+
| Field | Type | Null | Key | Default | Extra |
+——————-+——————-+———+——-+——————-+————————+
| id | int(11) | NO | PRI | NULL | auto_increment |
| systemId | varchar(30) | NO | MUL | NULL | |
| serviceId | varchar(50) | NO | MUL | defaultNull | |
| serviceName | varchar(50) | NO | | defaultNull | |
+——————-+——————-+———+——-+——————-+————————+
4 rows in set (0.00 sec)

MySQL >

二、表建好后,我们来定义表结构体:

type GoSystemInfo struct {
    ID           int    `gorm:"primary_key"`
    SystemId     string `gorm:"column:systemId;type:varchar(30);not null;index:SystemId"`
    SystemName   string `gorm:"column:systemName;type:varchar(50);not null;default:'defaultNull'"`
}
type GoServiceInfo struct {
    ID           int    `gorm:"primary_key"`
    SystemId     string `gorm:"column:systemId;type:varchar(30);not null;index:SystemId"`
    ServiceId    string `gorm:"column:serviceId;type:varchar(50);not null;default:'defaultNull';index:ServiceId"`
    ServiceName  string `gorm:"column:serviceName;type:varchar(50);not null;default:'defaultNull'"`
}

小知识:ORM(Object Relation Mapping),对象关系映射,实际上就是对数据库的操作进行封装,对上层开发人员屏蔽数据操作的细节,开发人员看到的就是一个个对象,大大简化了开发工作,提高了生产效率,也可以避免sql注入等问题。

由于gorm是使用的orm映射,所以需要定义要操作的表的model,在go中需要定义一个struct, struct的名字就是对应数据库中的表名,注意gorm查找struct名对应数据库中的表名的时候会默认把你的struct中的大写字母转换为小写并加上“s”,所以可以加上 db.SingularTable(true) 让gorm转义struct名字的时候不用加上“s”。

golang中,首字母大小写来表示public或者private,因此结构体中字段首字母必须大写。

定义model,即struct时,我们可以只定义我们需要从数据库中取回的特定字段: gorm在转义表名的时候会把struct的大写字母(首字母除外) 替换成“_”,所以下面的”GoSystemInfo”会转义成数据库中对应的“go_system_info”的表名, 对应的字段名的查找会先按照tag里面的名称去里面查找,如果没有定义标签则按照struct定义的字段查找,查找的时候struct字段中的大写会被转义成“_”,如:“SystemId”会去查找表中的system_id字段。

在本例,我们在struct使用如gorm:”column:systemId”,column映射mysql表字段名称。

三、联合查询

单表查询用上面的原表结构体接收数据就可以了, 联合查询涉及两张表中的全部/部分数据,我们定义新的结构体接收取回的特定字段:

type result struct {
    SystemId    string `json:"systemId"`
    SystemName  string `json:"systemName"`
    ServiceId   string `json:"serviceId"`
    ServiceName string `json:"serviceName"`
}

我们从go_service_info取serviceId、serviceName,从go_system_info取对应的systemId、systemName:

db.Table("go_service_info").Select("go_service_info.serviceId as service_id, go_service_info.serviceName as service_name, go_system_info.systemId as system_id, go_system_info.systemName as system_name").Joins("left join go_system_info on go_service_info.systemId = go_system_info.systemId").Scan(&results)

where条件:

db.Table("go_service_info").Select("go_service_info.serviceId as service_id, go_service_info.serviceName as service_name, go_system_info.systemId as system_id, go_system_info.systemName as system_name").Joins("left join go_system_info on go_service_info.systemId = go_system_info.systemId where go_service_info.serviceId <> ? and go_system_info.systemId = ?", "xxx", "xxx").Scan(&results)

注意:这里需要使用别名as system_id,映射返回值结构体,并且因为查找的时候struct字段中的大写会被转义成“_”,所以别名也要将大写转为“_”。

若使用原生语句:

db.Raw("SELECT a.serviceId as service_id,a.serviceName as service_name, b.systemId as system_id, b.systemName as system_name FROM go_service_info a LEFT JOIN go_system_info b ON a.systemId = b.systemId").Scan(&results)

where条件:

db.Raw("SELECT a.serviceId as service_id,a.serviceName as service_name, b.systemId as system_id, b.systemName as system_name FROM go_service_info a LEFT JOIN go_system_info b ON a.systemId = b.systemId where a.serviceId <> ? and b.systemId = ?", "xxx", "xxx").Scan(&results)

结果相同。

避坑建议: 表字段命名为如system_id,默认映射到结构体字段SystemId。当然建表原则上也是用小写和下划线,不过历史表难免会有大写命名的情况,所以新表还是遵照相关规范吧。

源码:

package main
import (
    "fmt"
    "log"
    "encoding/json"
    "github.com/jinzhu/gorm"
    _ "github.com/jinzhu/gorm/dialects/mysql"
)
type GoSystemInfo struct {
    ID           int    `gorm:"primary_key"`
    SystemId     string `gorm:"column:systemId;type:varchar(30);not null;index:SystemId"`
    SystemName   string `gorm:"column:systemName;type:varchar(50);not null;default:'defaultNull'"`
}
type GoServiceInfo struct {
    ID           int    `gorm:"primary_key"`
    SystemId     string `gorm:"column:systemId;type:varchar(30);not null;index:SystemId"`
    ServiceId    string `gorm:"column:serviceId;type:varchar(50);not null;default:'defaultNull';index:ServiceId"`
    ServiceName  string `gorm:"column:serviceName;type:varchar(50);not null;default:'defaultNull'"`
}
type result struct {
    SystemId    string `json:"systemId"`
    SystemName  string `json:"systemName"`
    ServiceId   string `json:"serviceId"`
    ServiceName string `json:"serviceName"`
}
//定义数据库连接
type ConnInfo struct { 
    MyUser   string 
    Password string 
    Host     string 
    Port     int 
    Db       string
}
func dbConn(MyUser, Password, Host, Db string, Port int) *gorm.DB {
    connArgs := fmt.Sprintf("%s:%s@(%s:%d)/%s?charset=utf8&parseTime=True&loc=Local", MyUser,Password, Host, Port, Db )
    db, err := gorm.Open("mysql", connArgs)
    if err != nil {  
        log.Fatal(err) 
    } 
    db.SingularTable(true)
    return db
}
func mapToJson(result interface{}) string {
    // map转 json str
    jsonBytes, _ := json.Marshal(result)
    jsonStr := string(jsonBytes)
    return jsonStr
}
func main() {
    var results []result
    cn := ConnInfo{  
      "xxx",  
      "xxx",  
      "127.0.0.1",  
      3306,  
      "xxx", 
    }  
    db := dbConn(cn.MyUser,cn.Password,cn.Host,cn.Db,cn.Port)
    defer db.Close()
    /*
    // 创建表
    db.AutoMigrate(&GoSystemInfo{})
    product := GoSystemInfo{SystemId:"sysid", SystemName:"sysname"}
    fmt.Println(db.NewRecord(product))
    db.AutoMigrate(&GoServiceInfo{})
    products := GoServiceInfo{SystemId:"sysid", ServiceId:"serid", ServiceName:"sername"}
    fmt.Println(db.NewRecord(products))
    */
    // 联合查询(left join)
    db.Table("go_service_info").Select("go_service_info.serviceId as service_id, go_service_info.serviceName as service_name, go_system_info.systemId as system_id, go_system_info.systemName as system_name").Joins("left join go_system_info on go_service_info.systemId = go_system_info.systemId").Scan(&results)
    fmt.Println(mapToJson(results))
    // where
    db.Table("go_service_info").Select("go_service_info.serviceId as service_id, go_service_info.serviceName as service_name, go_system_info.systemId as system_id, go_system_info.systemName as system_name").Joins("left join go_system_info on go_service_info.systemId = go_system_info.systemId where go_service_info.serviceId <> ? and go_system_info.systemId = ?", "xxx", "xxx").Scan(&results)
    fmt.Println(mapToJson(results))
    // 原生sql
    db.Raw("SELECT a.serviceId as service_id,a.serviceName as service_name, b.systemId as system_id, b.systemName as system_name FROM go_service_info a LEFT JOIN go_system_info b ON a.systemId = b.systemId").Scan(&results)
    fmt.Println(mapToJson(results))
    // where
    db.Raw("SELECT a.serviceId as service_id,a.serviceName as service_name, b.systemId as system_id, b.systemName as system_name FROM go_service_info a LEFT JOIN go_system_info b ON a.systemId = b.systemId where a.serviceId <> ? and b.systemId = ?", "xxx", "xxx").Scan(&results)
    fmt.Println(mapToJson(results))
}

示例结果:

参考:https://www.jb51.net/article/151051.htm

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • gorm多条数据级联查询关联查询gorm连接池gorm事务

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 ...

    hotqin888
  • gorm查询嵌套结构体,嵌套预加载preload,关联,外键foreignkey,引用references

    一直想用gorm查询到嵌套结构体,可惜自定义嵌套结构体好像不支持?下次按这个思路再试试,可能也行哦。

    hotqin888
  • Go开源ORM——GORM

    可以通过定义嵌套gorm.Model这个结构体的类型来定义实体类,gorm.Model中定义了数据库表的一些常用基本字段

    歪歪梯
  • Go 数据存储篇(七):GORM 使用入门

    我们已经成功存储数据到数据表,但是所有操作都要自行编写代码,很多编程语言和框架会引入 ORM 来解决模型类与数据表记录的映射关系,ORM 架起了 SQL 语句和...

    学院君
  • gorm聚合查询group结合join和count

    场景:engineercms的文章article放在了成果product下面,成果有作者user,以及所属的目录project

    hotqin888
  • 图解 SQL 里的各种 JOIN

    从业以来主要在做客户端,用到的数据库都是表结构比较简单的 SQLite,以我那还给老师一大半的 SQL 水平倒也能对付。现在偶尔需要到后台的 SQL Serve...

    芋道源码
  • MySQL分组统计与多表联合查询的基本知识归纳总结

    having 对分组后的数据进行条件筛选,与where相似,但是只针对分组后的数据,where无法筛选聚合函数

    Java帮帮
  • 写出好的Join语句,前提你得懂这些

    因为驱动结果集越大,意味着需要循环的次数越多,也就是说在被驱动结果集上面所 需要执行的查询检索次数会越多。

    用户4172423
  • inner join、outer join、right join、left join 之间的区别

    一、sql的left join 、right join 、inner join之间的区别   left join(左联接) 返回包括左表中的所有记录和右表中联...

    静谧的小码农
  • SQL 内连接,外连接(左外连接、右外连接)

    语句:select * from a_table a inner join b_table bon a.a_id = b.b_id;

    birdskyws
  • 对mysql left join 出现的重复结果去重

    简单说明问题出现的原因: MySQL left join 语句格式为: A LEFT JOIN B ON 条件表达式

    马哥Python
  • 企业面试题|最常问的MySQL面试题集合(二)

    嵌套查询 用一条SQL语句得结果作为另外一条SQL语句得条件,效率不好把握 SELECT * FROM A WHERE id IN (SELECT id FRO...

    民工哥
  • 图解数据库内连接、外连接、左连接、右连接、全连接等

    用两个表(a_table、b_table),关联字段a_table.a_id和b_table.b_id来演示一下MySQL的内连接、外连接( 左(外)连接、右(...

    浩Coding
  • mysql 如何优化left join

    今天遇到一个left join优化的问题,搞了一下午,中间查了不少资料,对MySQL的查询计划还有查询优化有了更进一步的了解,做一个简单的记录:  selec...

    凯哥Java
  • Apache-Flink深度解析-JOIN 算子

    在《Apache Flink 漫谈系列 - SQL概览》中我对JOIN算子有过简单的介绍,这里我们以具体实例的方式让大家对JOIN算子加深印象。JOIN的本质是...

    王知无-import_bigdata
  • 图解 SQL 里的各种 JOIN

    在各种问答社区里谈及 SQL 里的各种 JOIN 之间的区别时,最被广为引用的是 CodeProject 上 C.L. Moffatt 的文章 Visual R...

    周三不加班
  • mysql数据库多表各种join用法

    在数据库查询中,往往会需要查询多个表的数据,比如查询会员信息同时查询关于这个会员的订单信息,如果分语句查询的话,效率会很低,就需要用到join关键字来连表查询了

    仙士可
  • Apache-Flink深度解析-JOIN 算子

    在《SQL概览》中我们介绍了JOIN算子的语义和基本的使用方式,介绍过程中大家发现Apache Flink在语法语义上是遵循ANSI-SQL标准的,那么再深思一...

    王知无-import_bigdata
  • 故障分析 | MySQL 派生表优化

    这是一个统计类的 SQL,直接执行跑了好几个小时都没有结束,所以暂时不知道实际耗时,因为实在是太久了~

    爱可生开源社区

扫码关注云+社区

领取腾讯云代金券