SQL 構建器
原生 SQL
原生查詢 SQL 和 Scan
type Result struct { ID int Name string Age int } var result Result db.Raw("SELECT id, name, age FROM users WHERE id = ?", 3).Scan(&result) var age int db.Raw("select sum(age) from users where role = ?", "admin").Scan(&age)
Exec
原生 SQL
db.Exec("DROP TABLE users") db.Exec("UPDATE orders SET shipped_at=? WHERE id IN ?", time.Now(), []int64{1,2,3}) // Exec SQL 表達式 db.Exec("update users set money=? where name = ?", gorm.Expr("money * ? + ?", 10000, 1), "jinzhu")
注意 GORM 允許緩存預編譯 SQL 語句來提高性能,查看 性能 獲取詳情
命名參數
GORM 支持 sql.NamedArg
、map[string]interface{}{}
或 struct 形式的命名參數,例如:
db.Where("name1 = @name OR name2 = @name", sql.Named("name", "jinzhu")).Find(&user) // SELECT * FROM `users` WHERE name1 = "jinzhu" OR name2 = "jinzhu" db.Where("name1 = @name OR name2 = @name", map[string]interface{}{"name": "jinzhu2"}).First(&result3) // SELECT * FROM `users` WHERE name1 = "jinzhu2" OR name2 = "jinzhu2" ORDER BY `users`.`id` LIMIT 1 // 原生 SQL 及命名參數 db.Raw("SELECT * FROM users WHERE name1 = @name OR name2 = @name2 OR name3 = @name", sql.Named("name", "jinzhu1"), sql.Named("name2", "jinzhu2")).Find(&user) // SELECT * FROM users WHERE name1 = "jinzhu1" OR name2 = "jinzhu2" OR name3 = "jinzhu1" db.Exec("UPDATE users SET name1 = @name, name2 = @name2, name3 = @name", sql.Named("name", "jinzhunew"), sql.Named("name2", "jinzhunew2")) // UPDATE users SET name1 = "jinzhunew", name2 = "jinzhunew2", name3 = "jinzhunew" db.Raw("SELECT * FROM users WHERE (name1 = @name AND name3 = @name) AND name2 = @name2", map[string]interface{}{"name": "jinzhu", "name2": "jinzhu2"}).Find(&user) // SELECT * FROM users WHERE (name1 = "jinzhu" AND name3 = "jinzhu") AND name2 = "jinzhu2" type NamedArgument struct { Name string Name2 string } db.Raw("SELECT * FROM users WHERE (name1 = @Name AND name3 = @Name) AND name2 = @Name2", NamedArgument{Name: "jinzhu", Name2: "jinzhu2"}).Find(&user) // SELECT * FROM users WHERE (name1 = "jinzhu" AND name3 = "jinzhu") AND name2 = "jinzhu2"
DryRun 模式
在不執行的情況下生成 SQL
,可以用於准備或測試生成的 SQL,詳情請參考 Session
stmt := db.Session(&Session{DryRun: true}).First(&user, 1).Statement stmt.SQL.String() //=> SELECT * FROM `users` WHERE `id` = $1 ORDER BY `id` stmt.Vars //=> []interface{}{1}
Row
& Rows
獲取 *sql.Row
結果
// 使用 GORM API 構建 SQL row := db.Table("users").Where("name = ?", "jinzhu").Select("name", "age").Row() row.Scan(&name, &age) // 使用原生 SQL row := db.Raw("select name, age, email from users where name = ?", "jinzhu").Row() row.Scan(&name, &age, &email)
獲取 *sql.Rows
結果
// 使用 GORM API 構建 SQL rows, err := db.Model(&User{}).Where("name = ?", "jinzhu").Select("name, age, email").Rows() defer rows.Close() for rows.Next() { rows.Scan(&name, &age, &email) // 業務邏輯... } // 原生 SQL rows, err := db.Raw("select name, age, email from users where name = ?", "jinzhu").Rows() defer rows.Close() for rows.Next() { rows.Scan(&name, &age, &email) // 業務邏輯... }
轉到 FindInBatches 獲取如何在批量中查詢和處理記錄的信息, 轉到 Group 條件 獲取如何構建復雜 SQL 查詢的信息
將 sql.Rows
掃描至 model
使用 ScanRows
將一行記錄掃描至 struct,例如:
rows, err := db.Model(&User{}).Where("name = ?", "jinzhu").Select("name, age, email").Rows() // (*sql.Rows, error) defer rows.Close() var user User for rows.Next() { // ScanRows 將一行掃描至 user db.ScanRows(rows, &user) // 業務邏輯... }
高級
子句(Clause)
GORM 內部使用 SQL builder 生成 SQL。對於每個操作,GORM 都會創建一個 *gorm.Statement
對象,所有的 GORM API 都是在為 statement
添加/修改 Clause
,最后,GORM 會根據這些 Clause 生成 SQL
例如,當通過 First
進行查詢時,它會在 Statement
中添加以下 Clause
clause.Select{Columns: "*"} clause.From{Tables: clause.CurrentTable} clause.Limit{Limit: 1} clause.OrderByColumn{ Column: clause.Column{Table: clause.CurrentTable, Name: clause.PrimaryKey}, }
然后 GORM 在 Query
callback 中構建最終的查詢 SQL,像這樣:
Statement.Build("SELECT", "FROM", "WHERE", "GROUP BY", "ORDER BY", "LIMIT", "FOR")
生成 SQL:
SELECT * FROM `users` ORDER BY `users`.`id` LIMIT 1
您可以自定義 Clause
並與 GORM 一起使用,這需要實現 Interface 接口
可以參考 示例
子句構造器
不同的數據庫, Clause 可能會生成不同的 SQL,例如:
db.Offset(10).Limit(5).Find(&users) // SQL Server 會生成 // SELECT * FROM "users" OFFSET 10 ROW FETCH NEXT 5 ROWS ONLY // MySQL 會生成 // SELECT * FROM `users` LIMIT 5 OFFSET 10
子句選項之所以支持 Clause,是因為 GORM 允許數據庫驅動程序通過注冊 Clause Builder 來取代默認值,這兒有一個 Limit 的示例
GORM 定義了很多 Clause,其中一些 Clause 提供了你可能會用到的選項
盡管很少會用到它們,但如果你發現 GORM API 與你的預期不符合。這可能可以很好地檢查它們,例如
db.Clauses(clause.Insert{Modifier: "IGNORE"}).Create(&user) // INSERT IGNORE INTO users (name,age...) VALUES ("jinzhu",18...);
GORM 提供了 StatementModifier 接口,允許您修改語句,使其符合您的要求,這兒有一個 Hint 示例StatementModifier
import "gorm.io/hints" db.Clauses(hints.New("hint")).Find(&User{}) // SELECT * /*+ hint */ FROM `users`