Query Builder
概述
數據庫查詢構造器提供了一個方便的接口來創建和執行數據庫查詢。 它可以用於執行應用程序中的大部分數據庫操作,並且可以在所有支持的數據庫系統上工作。
查詢構造器使用參數綁定來保護您的應用程序免受 SQL 注入攻擊。 您無需清理或轉義傳遞給查詢構造器的字符串。
運行查詢
框架提供了各種查詢方法,您可以查詢、創建、更新和刪除數據庫中的數據。 請注意,當您想將數據綁定到 struct
或 模型 時,需要為字段添加 db
標籤:
type User struct {
ID string `db:"id"`
Name string `db:"name"`
}
type User struct {
orm.BaseModel
orm.NullableSoftDeletes
Name string `db:"name"`
}
檢索所有行
您可以使用 facades.DB()
提供的 table
方法開始查詢。 table
方法為指定的表返回一個可鏈式查詢的構造器實例,允許在查詢上鏈接更多約束,最後使用 Get
方法檢索查詢結果:
var users []User
err := facades.DB().Table("users").Get(&users)
檢索單行或單列
如果您只需要從數據庫表中檢索單行數據,可以使用 First
方法。
var user User
err := facades.DB().Table("users").Where("id", 1).First(&user)
您可以使用 Value
方法來檢索單個列的值:
var name string
err := facades.DB().Table("users").Where("id", 1).Value("name", &name)
您可以使用 Find
方法通過傳遞 id
來檢索單行數據:
var user User
err := facades.DB().Table("users").Find(&user, 1)
// 您還可以傳入一組 `id` 來檢索多行數據
var users []User
err := facades.DB().Table("users").Find(&users, []int{1, 2, 3})
// Find 默認將表的主鍵設置為 `id`,如果表的主鍵不是 `id`,可以傳遞 `id` 字段名稱
var user User
err := facades.DB().Table("users").Find(&users, "uuid", "1")
您可以使用 FindOrFail
或 FirstOrFail
方法,如果未找到記錄,將引發 sql.ErrNoRows
錯誤:
var user User
err := facades.DB().Table("users").FindOrFail(&user, 1)
您可以使用 FindOr
或 FirstOr
方法,如果未找到記錄,將執行閉包函數:
var user *User
user, err = facades.DB().Table("users").Where("name", "John").FirstOr(&user, func() error {
return errors.New("no rows")
})
檢索單列的值
如果您想檢索包含單個列值的記錄列表,可以使用 Pluck
方法:
var emails []string
err := facades.DB().Table("users").Pluck("email", &emails)
遍歷結果
您可以使用 Each
方法遍歷所有結果:
var products []Product
err := facades.DB().Table("products").Each(func(rows []db.Row) error {
for _, row := range rows {
var product Product
err := row.Scan(&product)
s.NoError(err)
products = append(products, product)
}
return nil
})
分塊結果
如果您需要處理數千條數據庫記錄,建議使用 Chunk
方法。 該方法一次檢索一小塊結果並將每塊傳遞給閉包函數進行處理:
var products []Product
err := facades.DB().Table("products").Chunk(2, func(rows []db.Row) error {
for _, row := range rows {
var product Product
err := row.Scan(&product)
s.NoError(err)
products = append(products, product)
}
return nil
})
注意:在 Chunk 的回調中修改記錄時,可能導致記錄未包含在分塊結果中。
光標
游標可用於處理大量數據,而無需一次性加載所有數據到內存中。 它一條一條地處理數據,而不是一次性加載所有數據。
rows, err := facades.DB().Table("products").Cursor()
var products []Product
for row := range rows {
var product Product
err = row.Scan(&product)
s.NoError(err)
s.True(product.ID > 0)
products = append(products, product)
}
聚合
查詢構造器提供了聚合方法: Count
Sum
。
count, err := facades.DB().Table("users").Count()
sum, err := facades.DB().Table("users").Sum("age")
檢查記錄是否存在
您可以使用 Exists
和 DoesntExist
方法來確定查詢條件的結果是否存在:
exists, err := facades.DB().Table("users").Where("votes", ">", 100).Exists()
exists, err := facades.DB().Table("users").Where("votes", ">", 100).DoesntExist()
分頁
您可以使用 Paginate
方法對查詢結果進行分頁:
var (
users []User
total int64
)
err := facades.DB().Table("users").Where("name", "John").Paginate(1, 10, &users, &total)
選擇
您可能不總是希望從數據庫表中檢索所有列。 使用 Select
方法自定義一個 "select" 查詢語句來查詢指定的字段:
var users []User
err := facades.DB().Table("users").Select("name", "email as user_email").Get(&users)
Distinct
方法會強制讓查詢返回不重複的結果:
var users []User
err := facades.DB().Table("users").Distinct().Select("name").Get(&users)
原生表達式
有時您可能需要在查詢中使用原生表達式。 您可以使用 db.Raw
方法來創建原生表達式:
import "github.com/goravel/framework/database/db"
facades.DB().Model(&user).Update("age", db.Raw("age - ?", 1))
選擇
指定一個 Select 子句
當然,您可能不總是希望從數據庫表中檢索所有列。 使用 Select
方法為您的查詢指定自定義的 select 子句:
// 選擇特定字段
facades.DB().Select("name", "age").Get(&users)
// 使用子查詢
facades.DB().Select("name", db.Raw("(SELECT COUNT(*) FROM posts WHERE users.id = posts.user_id) as post_count")).Get(&users)
唯一的
Distinct
方法會強制讓查詢返回不重複的結果:
var users []models.User
facades.DB().Distinct("name").Find(&users)
原生方法
WhereRaw / OrWhereRaw
WhereRaw
和 OrWhereRaw
方法可用於將原始 "where" 子句注入您的查詢。 這些方法接受一個可選的綁定數組作為它們的第二個參數:
var users []User
err := facades.DB().WhereRaw("age = ? or age = ?", []any{25, 30}).Get(&users)
err := facades.DB().OrWhereRaw("age = ? or age = ?", []any{25, 30}).Get(&users)
OrderByRaw
OrderByRaw
方法可用於將原生字符串設置為「order by」子句的值:
var users []User
err := facades.DB().OrderByRaw("age DESC, id ASC").Get(&users)
聯接
內聯接
查詢構造器可用於編寫聯接語句。 要執行基本的 SQL "內聯接",您可以在查詢構造器實例上使用 Join
方法:
var users []User
err := facades.DB().Table("users").Join("posts as p ON users.id = p.user_id AND p.id = ?", 1).Where("age", 25).Get(&users)
左聯接 / 右聯接
如果您想執行 "左聯接" 或 "右聯接",可以使用 LeftJoin
或 RightJoin
方法:
var users []User
err := facades.DB().Table("users").LeftJoin("posts as p ON users.id = p.user_id AND p.id = ?", 1).Where("age", 25).Get(&users)
err = facades.DB().Table("users").RightJoin("posts as p ON users.id = p.user_id AND p.id = ?", 1).Where("age", 25).Get(&users)
交叉聯接
CrossJoin
方法可用於執行「交叉聯接」:
var users []User
err := facades.DB().Table("users").CrossJoin("posts as p ON users.id = p.user_id AND p.id = ?", 1).Where("age", 25).Get(&users)
基本的 Where 子句
Where / OrWhere
您可以在查詢構造器實例上使用 Where
方法向查詢添加 where 子句。
import "github.com/goravel/framework/contracts/database/db"
var users []User
err := facades.DB().Where("votes", 100).Get(&users)
err := facades.DB().Where("votes >= ?", 100).Get(&users)
err := facades.DB().Where("votes LIKE ?", "%goravel%").Get(&users)
err := facades.DB().Where("votes", []int{1, 2, 3}).Get(&users)
err := facades.DB().Where(func(query db.Query) db.Query {
return query.Where("age", []int{25, 30}).Where("name", "Tom")
}).OrWhere("name", "John").Get(&users)
WhereNot / OrWhereNot
WhereNot
和 OrWhereNot
方法可用於否定一組給定的查詢條件。
import "github.com/goravel/framework/contracts/database/db"
var users []User
err := facades.DB().WhereNot("votes", 100).Get(&users)
err := facades.DB().WhereNot("votes >= ?", 100).Get(&users)
err := facades.DB().WhereNot("votes LIKE ?", "%goravel%").Get(&users)
err := facades.DB().WhereNot("votes", []int{1, 2, 3}).Get(&users)
err := facades.DB().WhereNot(func(query db.Query) db.Query {
return query.Where("age", []int{25, 30}).Where("name", "Tom")
}).OrWhereNot("name", "John").Get(&users)
WhereExists / WhereNotExists
WhereExists
方法允許你編寫 exists SQL 子句:
var users []User
err := facades.DB().Table("users").Where("name", "John").WhereExists(func() db.Query {
return facades.DB().Table("posts").WhereColumn("posts.user_id", "users.id")
}).Get(&users)
其他 Where 语句
WhereBetween / OrWhereBetween
WhereBetween
方法驗證字段值是否在給定的兩個值之間:
facades.DB().Table("users").WhereBetween("votes", 1, 100)
WhereNotBetween / OrWhereNotBetween
WhereNotBetween
方法驗證字段值是否在給定的兩個值之外:
facades.DB().Table("users").WhereNotBetween("votes", 1, 100)
WhereIn / WhereNotIn / OrWhereIn / OrWhereNotIn
WhereIn
方法驗證字段值必須存在於指定的數組中:
facades.DB().Table("users").WhereIn("id", []any{1, 2, 3})
WhereNull / WhereNotNull / OrWhereNull / OrWhereNotNull
WhereNull
方法驗證指定的字段必須是 NULL
:
facades.DB().Table("users").WhereNull("updated_at")
WhereLike / WhereNotLike / OrWhereLike / OrWhereNotLike
WhereLike
方法驗證字段值是否包含給定的值:
facades.DB().Table("users").WhereLike("name", "%goravel%")
WhereColumn / OrWhereColumn
WhereColumn
方法驗證兩個字段是否相等:
facades.DB().Table("users").WhereColumn("first_name", "last_name")
邏輯分組
有時你可能需要將幾個「where」子句包裹在括號內,以獲得查詢所需的邏輯分組。
facades.DB().Table("users").Where("age", 25).Where(func(query db.Query) db.Query {
return query.Where("votes", 100).OrWhere("votes", 200)
})
排序、分組、限制和偏移
排序
OrderBy / OrderByDesc
facades.DB().OrderBy("name")
facades.DB().OrderByDesc("name")
最新
Latest
方法可以使你輕鬆地通過日期對結果進行排序。 默認情況下,結果將根據 created_at
列進行排序: 默認情況下,結果將根據 created_at
列進行排序:
facades.DB().Table("users").Latest().First(&user)
facades.DB().Table("users").Latest("updated_at").First(&user)
隨機排序
InRandomOrder
方法被用來將結果隨機排序:
facades.DB().Table("users").InRandomOrder().First(&user)
分組
GroupBy
和 Having
方法可以用來分組結果:
err := facades.DB().Table("users").Where("age", 25).GroupBy("name").Having("name = ?", "John").OrderBy("name").Get(&users)
限制和偏移
你可以使用 Limit
和 Offset
方法來限制結果的數量,或者在查詢中跳過指定數量的結果:
err := facades.DB().Table("users").Offset(10).Limit(5).Get(&users)
條件子句
有時你可能希望在給定的條件為真的時候才執行某個子句。 舉例來說,你只想在給定的值存在於請求中時才應用某個 where 子句。 這可以通過使用 When
方法來完成:
import "github.com/goravel/framework/contracts/database/db"
facades.DB().Table("users").When(1 == 1, func(query db.Query) db.Query {
return query.Where("age", 25)
}).First(&user)
你還可以將另一個閉包作為第三個參數傳遞給 When
方法。 如果第一個參數的結果為 false,這個閉包將會執行:
facades.DB().Table("users").When(1 != 1, func(query db.Query) db.Query {
return query.OrderBy("name")
}, func(query db.Query) db.Query {
return query.OrderBy("id")
}).First(&user)
插入
查詢構造器提供了 Insert
方法用於插入記錄到數據庫中:
// 根據結構插入
result, err := facades.DB().Table("products").Insert(Product{
Name: "goravel",
})
// 根據切片結構插入
result, err := facades.DB().Table("products").Insert([]Product{
{
Name: "goravel",
},
{
Name: "go",
},
})
// 根據映射插入
result, err := facades.DB().Table("products").Insert(map[string]any{
"name": "goravel",
"created_at": time.Now(),
"updated_at": time.Now(),
})
// 根據切片映射插入
result, err := facades.DB().Table("products").Insert([]map[string]any{
{
"name": "goravel",
"created_at": time.Now(),
"updated_at": time.Now(),
},
{
"name": "go",
"created_at": time.Now(),
"updated_at": time.Now(),
},
})
自增 ID
如果表的主鍵是自增的,你可以使用 LastInsertID
方法獲取自增 ID,僅支持 mysql
和 sqlite
數據庫:
id, err := facades.DB().Table("products").InsertGetID(Product{
Name: "goravel",
})
更新
查詢構造器提供了 Update
方法用於更新數據庫中存在的記錄:
// 根據字段名稱更新
result, err := facades.DB().Table("products").Where("id", 1).Update("phone", "1234567890")
// 根據結構更新
result, err := facades.DB().Table("products").Where("id", 1).Update(Product{
Name: "goravel",
})
// 根據映射更新
result, err := facades.DB().Table("products").Where("id", 1).Update(map[string]any{
"name": "goravel",
"created_at": time.Now(),
"updated_at": time.Now(),
})
更新 JSON 欄位
facades.DB().Table("users").Where("id", 1).Update("options->enabled", true)
facades.DB().Table("users").Where("id", 1).Update("options->languages[0]", "en")
facades.DB().Table("users").Where("id", 1).Update("options->languages", []string{"en", "de"})
facades.DB().Table("users").Where("id", 1).Update(map[string]any{
"preferences->dining->meal": "salad",
"options->languages[0]": "en",
"options->enabled": true,
})
更新或插入
有時你可能希望更新數據庫中的記錄,但如果指定的記錄不存在,則創建它。 這可以使用 UpdateOrInsert
方法來完成。 UpdateOrInsert
方法接受兩個參數:一個用於尋找記錄的條件,以及一個包含要更新記錄的值的鍵值對。
UpdateOrInsert
方法將嘗試使用第一個參數的列名和值來定位匹配的數據庫記錄。 如果記錄存在,則使用第二個參數更新其值。 如果未找到匹配記錄,則會創建記錄,並從兩個參數中合併其值:
// 使用結構
result, err := facades.DB().Table("users").Where("id", 1).UpdateOrInsert(TestUser{
Email: "john@example.com",
}, TestUser{
Phone: "1234567890",
})
// 使用映射
result, err := facades.DB().Table("users").Where("id", 1).UpdateOrInsert(map[string]any{
"email": "john@example.com",
}, map[string]any{
"phone": "1234567890",
})
自增與自減
Increment
和 Decrement
方法可以用於自增或自減指定字段的值:
err := facades.DB().Table("users").Where("id", 1).Increment("votes")
err := facades.DB().Table("users").Where("id", 1).Increment("votes", 2)
err := facades.DB().Table("users").Where("id", 1).Decrement("votes")
err := facades.DB().Table("users").Where("id", 1).Decrement("votes", 2)
刪除
查詢構造器還包括一些函數,可以幫助你在 select
語句中實現「悲觀鎖定」:
result, err := facades.DB().Table("users").Where("id", 1).Delete()
悲觀鎖定
查詢構造器還包括一些函數,可以幫助你在 select
語句中實現「悲觀鎖定」:
若要使用「共享鎖」,你可以使用 SharedLock
方法。 共享鎖可防止選中的行被篡改,直到事務被提交為止:
facades.DB().Table("users").Where("votes", ">", 100).SharedLock().Get(&users)
你也可以使用 LockForUpdate
方法。 使用「更新」鎖可避免行被其它共享鎖修改或選取:
facades.DB().Table("users").Where("votes", ">", 100).LockForUpdate().Get(&users)
調試
你可以使用 ToSQL
和 ToRawSql
方法來獲取當前查詢綁定和 SQL。
帶佔位符的 SQL:
facades.DB().Table("users").Where("id", 1).ToSql().Get(models.User{})
帶綁定值的 SQL:
facades.DB().Table("users").Where("id", 1).ToRawSql().Get(models.User{})
ToSql
與 ToRawSql
後可以調用的方法:Count
, Insert
, Delete
, First
, Get
, Pluck
, Update
。