大家好,我是漁夫子。
在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');
基于這個表,我們來看看這幾個函數查詢出來的結果是什么。
我們通過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 MTest sql := db.ToSQL(func(tx *gorm.DB) *gorm.DB { return tx.First(&row) }) fmt.Printf("接收的sql語句:%s/n", sql)}
通過該程序,可以看到最終的sql語句如下:
接收的sql語句:SELECT * FROM `m_test` ORDER BY `m_test`.`id` LIMIT 1
發現First函數是通過主鍵排序后,只獲取一條數據。我們在通過explain來解釋一下該條語句:
explain SELECT * FROM `m_test` ORDER BY `m_test`.`id` LIMIT 1
其輸出結果如下:
也就是說在查詢的時候也只掃描一行數據。也就是說First函數只掃描一行數據。
同樣,我們還是通過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 []MTest sql := 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語句如下:
接收的sql語句:SELECT * FROM `m_test` ORDER BY `m_test`.`id` DESC LIMIT 1
所以,Take實際上是按主鍵倒序排列,并且只獲取1行數據的一個sql。
我們再看最終獲取的結果rows,雖然是個數組,但也只有一行數據。:
最終結果數據:[{Id:6 Name:}]
所以,Last和First的相同點在于只掃描到表的一條目標數據后就截止了,并賦值給接收變量。不同點在于First是按主鍵正序排列,Last是按主鍵倒序排列。
再來看看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 MTest sql := db.ToSQL(func(tx *gorm.DB) *gorm.DB { return tx.Take(&row) }) fmt.Printf("接收的sql語句:%s/n", sql)}
Take函數執行時最終轉換成的sql語句如下:
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都只能獲取一條數據,即便是給傳遞了一個數組,也只能獲取一行數據,不能獲取多行數據。
再來看看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 MTest sql := db.ToSQL(func(tx *gorm.DB) *gorm.DB { return tx.Find(&row) }) fmt.Printf("接收的sql語句:%s/n", sql)}
轉換成的sql語句如下:
接收的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 []MTest tx.Find(&rows) fmt.Printf("rows:%+v/n", rows)}
這個結果是接收所有查找到的行的數據到rows中。所以大家一定要注意,在使用Find查詢的時候一定要加Where條件和查詢的數量,以避免掃描和查詢全表的數據,尤其是在大數量的表中。
本文主要講解了First、Last、Take和Find查詢函數的不同之處。希望在使用過程中大家根據自己的應用場景選擇合適的函數。
本文鏈接:http://www.tebozhan.com/showinfo-26-14119-0.htmlFind、Take、First和Last函數的區別
聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com