学习gorm:彻底弄懂Find、Take、First和Last函数的区别

2023-10-30 06:36

本文主要是介绍学习gorm:彻底弄懂Find、Take、First和Last函数的区别,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

在gorm中,要想从数据库中查找数据有多种方法,可以通过Find、Take和First来查找。但它们之间又有一些不同。本文就详细介绍下他们之间的不同。

一、准备工作

首先我们有一个m_tests表,其中id字段是自增的主键,同时该表里有3条数据。如下:

CREATE TABLE `m_tests` (`id` bigint(20) NOT NULL AUTO_INCREMENT,`name` varchar(100) NOT NULL DEFAULT '' COMMENT '姓名',PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4;INSERT INTO test01.m_test (id,name) VALUES (1,'John'), (2,'Jack'),(3,'David');

基于这个表,我们来看看这几个函数查询出来的结果是什么。

二、First函数

我们通过ToSql函数将First函数转成对应的sql语句来看。如下:

func main() {dsn := "username:password@tcp(127.0.0.1:3306)/test01?charset=utf8mb4&parseTime=True&loc=Local&timeout=1000ms"config := &gorm.Config{NamingStrategy: schema.NamingStrategy{SingularTable: true, // 禁用表名复数}}db, _ := gorm.Open(mysql.Open(dsn), config)var row MTestsql := db.ToSQL(func(tx *gorm.DB) *gorm.DB {return tx.First(&row)})fmt.Printf("接收的sql语句:%s\n", sql)
}

通过该程序,可以看到最终的sql语句如下:
powershell复制代码接收的sql语句:SELECT * FROM m_test ORDER BY m_test.id LIMIT 1

发现First函数是通过主键排序后,只获取一条数据。我们在通过explain来解释一下该条语句:
powershell复制代码explain SELECT * FROM m_test ORDER BY m_test.id LIMIT 1

其输出结果如下:
也就是说在查询的时候也只扫描一行数据。也就是说First函数只扫描一行数据。

三、Last函数

同样,我们还是通过ToSQL来讲Last函数转化的sql语句打印出来:

func main() {dsn := "username:password@tcp(127.0.0.1:3306)/test01?charset=utf8mb4&parseTime=True&loc=Local&timeout=1000ms"config := &gorm.Config{NamingStrategy: schema.NamingStrategy{SingularTable: true, // 禁用表名复数}}db, _ := gorm.Open(mysql.Open(dsn), config)var rows []MTestsql := db.ToSQL(func(tx *gorm.DB) *gorm.DB {return tx.Last(&rows)})fmt.Printf("接收的sql语句:%s\n", sql)db.Last(&rows)fmt.Printf("最终接收:%+v\n", rows)
}

我们看到Last转换成的sql语句如下:
powershell复制代码接收的sql语句:SELECT * FROM m_test ORDER BY m_test.id DESC LIMIT 1

所以,Take实际上是按主键倒序排列,并且只获取1行数据的一个sql。
我们再看最终获取的结果rows,虽然是个数组,但也只有一行数据。:
powershell复制代码最终结果数据:[{Id:6 Name:}]

所以,Last和First的相同点在于只扫描到表的一条目标数据后就截止了,并赋值给接收变量。不同点在于First是按主键正序排列,Last是按主键倒序排列。

四、Take函数

再来看看Take函数的执行过程。如下:

func main() {dsn := "username:password@tcp(127.0.0.1:3306)/test01?charset=utf8mb4&parseTime=True&loc=Local&timeout=1000ms"config := &gorm.Config{NamingStrategy: schema.NamingStrategy{SingularTable: true, // 禁用表名复数}}db, _ := gorm.Open(mysql.Open(dsn), config)var row MTestsql := db.ToSQL(func(tx *gorm.DB) *gorm.DB {return tx.Take(&row)})fmt.Printf("接收的sql语句:%s\n", sql)
}

Take函数执行时最终转换成的sql语句如下:
powershell复制代码SELECT * FROM m_test LIMIT 1

也是只获取一行数据,但和First不同的是缺少了Order BY m_test.id``。
我们再通过explain来解释下该条语句,如下, type列是ALL,rows列是3,因为我们表里只有3行数据。是全表扫描,然后再随机获取一行数据。如下:

mysql> explain SELECT * FROM `m_test` LIMIT 1;
+----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------+
| id | select_type | table  | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra |
+----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------+
|  1 | SIMPLE      | m_test | NULL       | ALL  | NULL          | NULL | NULL    | NULL |    3 |   100.00 | NULL  |
+----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------+
1 row in set, 1 warning (0.09 sec)

所以,Take函数是扫描全表,并随机获取一条数据。所以,Take函数要比First函数性能差。
同时,我们注意到,因为在sql语句中可以看到都有LIMIT 1的限制,所以Take和First都只能获取一条数据,即便是给传递了一个数组,也只能获取一行数据,不能获取多行数据。

五、Find函数

再来看看Take函数的执行过程。我们首先给Find函数传递一个普通的非切片变量,如下:

func main() {dsn := "username:password@tcp(127.0.0.1:3306)/test01?charset=utf8mb4&parseTime=True&loc=Local&timeout=1000ms"config := &gorm.Config{NamingStrategy: schema.NamingStrategy{SingularTable: true, // 禁用表名复数}}db, _ := gorm.Open(mysql.Open(dsn), config)var row MTestsql := db.ToSQL(func(tx *gorm.DB) *gorm.DB {return tx.Find(&row)})fmt.Printf("接收的sql语句:%s\n", sql)
}

转换成的sql语句如下:
powershell复制代码接收的sql语句:SELECT * FROM m_test

和First和Take相比,缺少了Order子句和Limit子句。扫描的是整个表,获取的也是表的所有数据,但因为接收者是一个非切片变量,所以最终只接收了一行数据到row中。
我们再来看看给Find传递一个切片变量来接收的情况

func main() {dsn := "username:password@tcp(127.0.0.1:3306)/test01?charset=utf8mb4&parseTime=True&loc=Local&timeout=1000ms"config := &gorm.Config{NamingStrategy: schema.NamingStrategy{SingularTable: true, // 禁用表名复数}}db, _ := gorm.Open(mysql.Open(dsn), config)var rows []MTesttx.Find(&rows)fmt.Printf("rows:%+v\n", rows)
}

这个结果是接收所有查找到的行的数据到rows中。所以大家一定要注意,在使用Find查询的时候一定要加Where条件和查询的数量,以避免扫描和查询全表的数据,尤其是在大数量的表中。
六、总结
本文主要讲解了First、Last、Take和Find查询函数的不同之处。希望在使用过程中大家根据自己的应用场景选择合适的函数。

这篇关于学习gorm:彻底弄懂Find、Take、First和Last函数的区别的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/306221

相关文章

sky-take-out项目中Redis的使用示例详解

《sky-take-out项目中Redis的使用示例详解》SpringCache是Spring的缓存抽象层,通过注解简化缓存管理,支持Redis等提供者,适用于方法结果缓存、更新和删除操作,但无法实现... 目录Spring Cache主要特性核心注解1.@Cacheable2.@CachePut3.@Ca

GO语言中函数命名返回值的使用

《GO语言中函数命名返回值的使用》在Go语言中,函数可以为其返回值指定名称,这被称为命名返回值或命名返回参数,这种特性可以使代码更清晰,特别是在返回多个值时,感兴趣的可以了解一下... 目录基本语法函数命名返回特点代码示例命名特点基本语法func functionName(parameters) (nam

Python Counter 函数使用案例

《PythonCounter函数使用案例》Counter是collections模块中的一个类,专门用于对可迭代对象中的元素进行计数,接下来通过本文给大家介绍PythonCounter函数使用案例... 目录一、Counter函数概述二、基本使用案例(一)列表元素计数(二)字符串字符计数(三)元组计数三、C

Go之errors.New和fmt.Errorf 的区别小结

《Go之errors.New和fmt.Errorf的区别小结》本文主要介绍了Go之errors.New和fmt.Errorf的区别,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考... 目录error的基本用法1. 获取错误信息2. 在条件判断中使用基本区别1.函数签名2.使用场景详细对

Redis中哨兵机制和集群的区别及说明

《Redis中哨兵机制和集群的区别及说明》Redis哨兵通过主从复制实现高可用,适用于中小规模数据;集群采用分布式分片,支持动态扩展,适合大规模数据,哨兵管理简单但扩展性弱,集群性能更强但架构复杂,根... 目录一、架构设计与节点角色1. 哨兵机制(Sentinel)2. 集群(Cluster)二、数据分片

Unity新手入门学习殿堂级知识详细讲解(图文)

《Unity新手入门学习殿堂级知识详细讲解(图文)》Unity是一款跨平台游戏引擎,支持2D/3D及VR/AR开发,核心功能模块包括图形、音频、物理等,通过可视化编辑器与脚本扩展实现开发,项目结构含A... 目录入门概述什么是 UnityUnity引擎基础认知编辑器核心操作Unity 编辑器项目模式分类工程

Python中的filter() 函数的工作原理及应用技巧

《Python中的filter()函数的工作原理及应用技巧》Python的filter()函数用于筛选序列元素,返回迭代器,适合函数式编程,相比列表推导式,内存更优,尤其适用于大数据集,结合lamb... 目录前言一、基本概念基本语法二、使用方式1. 使用 lambda 函数2. 使用普通函数3. 使用 N

MySQL中REPLACE函数与语句举例详解

《MySQL中REPLACE函数与语句举例详解》在MySQL中REPLACE函数是一个用于处理字符串的强大工具,它的主要功能是替换字符串中的某些子字符串,:本文主要介绍MySQL中REPLACE函... 目录一、REPLACE()函数语法:参数说明:功能说明:示例:二、REPLACE INTO语句语法:参数

python中update()函数的用法和一些例子

《python中update()函数的用法和一些例子》update()方法是字典对象的方法,用于将一个字典中的键值对更新到另一个字典中,:本文主要介绍python中update()函数的用法和一些... 目录前言用法注意事项示例示例 1: 使用另一个字典来更新示例 2: 使用可迭代对象来更新示例 3: 使用

一文带你迅速搞懂路由器/交换机/光猫三者概念区别

《一文带你迅速搞懂路由器/交换机/光猫三者概念区别》讨论网络设备时,常提及路由器、交换机及光猫等词汇,日常生活、工作中,这些设备至关重要,居家上网、企业内部沟通乃至互联网冲浪皆无法脱离其影响力,本文将... 当谈论网络设备时,我们常常会听到路由器、交换机和光猫这几个名词。它们是构建现代网络基础设施的关键组成