pages/ja_JP/docs/sql_builder.md
Scan を使用して素のSQLでレコードを取得することができます。
type Result struct {
ID int
Name string
Age int
}
// Scan into a single result
result, err := gorm.G[Result](db).Raw("SELECT id, name, age FROM users WHERE id = ?", 3).Find(context.Background())
// Scan into a primitive type
age, err := gorm.G[int](db).Raw("SELECT SUM(age) FROM users WHERE role = ?", "admin").Find(context.Background())
// Scan into a slice
users, err := gorm.G[User](db).Raw("UPDATE users SET name = ? WHERE age = ? RETURNING id, name", "jinzhu", 20).Find(context.Background())
素のSQLで Exec を実行することも可能です。
// Execute raw SQL
result := gorm.WithResult()
err := gorm.G[any](db, result).Exec(context.Background(), "DROP TABLE users")
// Execute with parameters
err = gorm.G[any](db).Exec(context.Background(), "UPDATE orders SET shipped_at = ? WHERE id IN ?", time.Now(), []int64{1, 2, 3})
// Exec with SQL Expression
err = gorm.G[any](db).Exec(context.Background(), "UPDATE users SET money = ? WHERE name = ?", gorm.Expr("money * ? + ?", 10000, 1), "jinzhu")
Query Raw SQL with 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)
db.Raw("SELECT id, name, age FROM users WHERE name = ?", "jinzhu").Scan(&result)
var age int
db.Raw("SELECT SUM(age) FROM users WHERE role = ?", "admin").Scan(&age)
var users []User
db.Raw("UPDATE users SET name = ? WHERE age = ? RETURNING id, name", "jinzhu", 20).Scan(&users)
Exec with Raw SQL
db.Exec("DROP TABLE users")
db.Exec("UPDATE orders SET shipped_at = ? WHERE id IN ?", time.Now(), []int64{1, 2, 3})
// Exec with SQL Expression
db.Exec("UPDATE users SET money = ? WHERE name = ?", gorm.Expr("money * ? + ?", 10000, 1), "jinzhu")
{% note warn %} 注 GORMはパフォーマンスを向上のためにプリペアードステートメントをキャッシュすることができます。詳細は Performance を参照してください。 {% endnote %}
GORMは sql.NamedArg や map[string]interface{}{} 、構造体を使用した名前付き引数をサポートしています。例:
users, err := gorm.G[User](db).Where("name1 = @name OR name2 = @name", sql.Named("name", "jinzhu")).Find(context.Background())
// SELECT * FROM `users` WHERE name1 = "jinzhu" OR name2 = "jinzhu"
result3, err := gorm.G[User](db).Where("name1 = @name OR name2 = @name", map[string]interface{}{"name": "jinzhu2"}).First(context.Background())
// SELECT * FROM `users` WHERE name1 = "jinzhu2" OR name2 = "jinzhu2" ORDER BY `users`.`id` LIMIT 1
// Named Argument with Raw SQL
users, err := gorm.G[User](db).Raw("SELECT * FROM users WHERE name1 = @name OR name2 = @name2 OR name3 = @name",
sql.Named("name", "jinzhu1"), sql.Named("name2", "jinzhu2")).Find(context.Background())
// SELECT * FROM users WHERE name1 = "jinzhu1" OR name2 = "jinzhu2" OR name3 = "jinzhu1"
err := gorm.G[any](db).Exec(context.Background(), "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"
users, err := gorm.G[User](db).Raw("SELECT * FROM users WHERE (name1 = @name AND name3 = @name) AND name2 = @name2",
map[string]interface{}{"name": "jinzhu", "name2": "jinzhu2"}).Find(context.Background())
// SELECT * FROM users WHERE (name1 = "jinzhu" AND name3 = "jinzhu") AND name2 = "jinzhu2"
type NamedArgument struct {
Name string
Name2 string
}
users, err := gorm.G[User](db).Raw("SELECT * FROM users WHERE (name1 = @Name AND name3 = @Name) AND name2 = @Name2",
NamedArgument{Name: "jinzhu", Name2: "jinzhu2"}).Find(context.Background())
// SELECT * FROM users WHERE (name1 = "jinzhu" AND name3 = "jinzhu") AND name2 = "jinzhu2"
GORM supports named arguments with sql.NamedArg, map[string]interface{}{} or struct, for example:
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
// Named Argument with Raw 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"
実行せずに、SQL とその引数の生成だけを行います。生成されたSQLの確認やテストを行えます。詳細についてはSession を確認してください。
stmt := db.Session(&gorm.Session{DryRun: true}).First(&user, 1).Statement
stmt.SQL.String() //=> SELECT * FROM `users` WHERE `id` = $1 ORDER BY `id`
stmt.Vars //=> []interface{}{1}
実行はせずに、生成された SQL を返します。
GORMはSQL文を構築するために database/sql のプレースホルダ引数を使用します。これにより、引数を自動的にエスケープし、SQLインジェクションを防ぐことができます。 しかし、生成されたSQLが安全であるという保証はしていないため、デバッグにのみ使用するようにしてください。
sql := db.ToSQL(func(tx *gorm.DB) *gorm.DB {
return tx.Model(&User{}).Where("id = ?", 100).Limit(10).Order("age desc").Find(&[]User{})
})
sql //=> SELECT * FROM "users" WHERE id = 100 AND "users"."deleted_at" IS NULL ORDER BY age desc LIMIT 10
Row & Rows結果を *sql.Row として取得します。
// Use GORM API build SQL
row := gorm.G[any](db).Table("users").Where("name = ?", "jinzhu").Select("name", "age").Row(context.Background())
row.Scan(&name, &age)
// Use Raw SQL
row := gorm.G[any](db).Raw("select name, age, email from users where name = ?", "jinzhu").Row(context.Background())
row.Scan(&name, &age, &email)
結果を *sql.Rows として取得します。
// Use GORM API build SQL
rows, err := gorm.G[User](db).Where("name = ?", "jinzhu").Select("name, age, email").Rows(context.Background())
defer rows.Close()
for rows.Next() {
rows.Scan(&name, &age, &email)
// do something
}
// Raw SQL
rows, err := gorm.G[any](db).Raw("select name, age, email from users where name = ?", "jinzhu").Rows(context.Background())
defer rows.Close()
for rows.Next() {
rows.Scan(&name, &age, &email)
// do something
}
Get result as *sql.Row
// GORMのAPIを利用
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)
Get result as *sql.Rows
// SQLの組み立てにGORM APIを使用
rows, err := db.Model(&User{}).Where("name = ?", "jinzhu").Select("name, age, email").Rows()
defer rows.Close()
for rows.Next() {
rows.Scan(&name, &age, &email)
// do something
}
// 素の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)
// do something
}
バッチ処理でのレコード取得やレコード処理の方法については、FindInBatches を参照してください。また、複雑なSQLクエリの構築方法については、Group Conditions を参照してください。
*sql.Rows を構造体へ ScanScanRows を使用して、取得した行データを構造体にScanすることができます。例:
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 scan a row into user
db.ScanRows(rows, &user)
// do something
}
同じtcp接続で複数のSQLを実行することができます(トランザクションを意味するものではありません)
db.Connection(func(tx *gorm.DB) error {
tx.Exec("SET my.role = ?", "admin")
tx.First(&User{})
})
GORMは内部的にSQLビルダーを使用してSQLを生成します。各操作に対し、GORMは *gorm.Statement オブジェクトを作成し、すべてのGORM APIで StatementにClause を追加/変更し、最終的にこれらの Clause にもとづいてSQLを生成します。
例えば First でレコードを取得する場合、First は内部的に、以下の Clauses を Statement に追加します。
var limit = 1
clause.Select{Columns: []clause.Column{{Name: "*"}}}
clause.From{Tables: []clause.Table{{Name: clause.CurrentTable}}}
clause.Limit{Limit: &limit}
clause.OrderBy{Columns: []clause.OrderByColumn{
{
Column: clause.Column{
Table: clause.CurrentTable,
Name: clause.PrimaryKey,
},
},
}}
その後、GORMは Query コールバックで最終的に実行されるSQLクエリを組み立てます。
Statement.Build("SELECT", "FROM", "WHERE", "GROUP BY", "ORDER BY", "LIMIT", "FOR")
生成されるSQLは以下のようになります。
SELECT * FROM `users` ORDER BY `users`.`id` LIMIT 1
独自の Clause を定義して、それを利用することも可能です。その際は Interface を実装する必要があります。
詳細については examples を参照してください。
データベースの種別に応じて、Clausesはそれぞれ異なるSQLを生成します。例:
db.Offset(10).Limit(5).Find(&users)
// Generated for SQL Server
// SELECT * FROM "users" OFFSET 10 ROW FETCH NEXT 5 ROWS ONLY
// Generated for MySQL
// SELECT * FROM `users` LIMIT 5 OFFSET 10
データベースドライバがClause Builderを登録することで、デフォルトのものを置き換えることが可能になっているため、これが可能となっています。 例として Limit を参照してみるとよいでしょう。
GORMは 多くのClauses を定義しています。いくつかのClausesは高度なオプションを提供し、アプリケーションで使用することができます。
ほとんど使われることはないかもしれませんが、もしGORMのAPIがアプリケーションの要求にマッチしない場合は、それらを調べてみてもよいでしょう。例:
db.Clauses(clause.Insert{Modifier: "IGNORE"}).Create(&user)
// INSERT IGNORE INTO users (name,age...) VALUES ("jinzhu",18...);
GORMは StatementModifier インターフェイスを提供しており、これを利用することでアプリケーションの要求に合うようにstatementを修正することが可能になります。例として Hints を参照するとよいでしょう。
import "gorm.io/hints"
db.Clauses(hints.New("hint")).Find(&User{})
// SELECT * /*+ hint */ FROM `users`