SQLビルダー

Raw SQL

Scanを使ったRaw SQLクエリ

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)

Raw SQLを使ったExec

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")

注記 GORMは、プリペアドステートメントをキャッシュしてパフォーマンスを向上させることができます。詳細はパフォーマンスをご覧ください。

名前付き引数

GORMは、sql.NamedArgmap[string]interface{}{}、または構造体を使った名前付き引数をサポートしています。例:

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"

DryRunモード

SQLとその引数を実行せずに生成します。生成されたSQLの準備やテストに使用できます。詳細はセッションをご覧ください。

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}

ToSQL

実行せずに生成されたSQLを返します。

GORMは、database/sqlの引数プレースホルダーを使用して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 := db.Table("users").Where("name = ?", "jinzhu").Select("name", "age").Row()
row.Scan(&name, &age)

// Use Raw SQL
row := db.Raw("select name, age, email from users where name = ?", "jinzhu").Row()
row.Scan(&name, &age, &email)

結果を*sql.Rowsとして取得します。

// Use GORM API build 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)

// do something
}

// Raw 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クエリを構築する方法については、グループ条件をご覧ください。

*sql.Rowsを構造体にスキャン

例として、ScanRowsを使用して行を構造体にスキャンします。

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はStatementClauseを追加または変更します。最後に、GORMはこれらの句に基づいてSQLを生成します。

たとえば、Firstでクエリすると、次の句が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を定義してGORMで使用できます。そのためには、インターフェースを実装する必要があります。

参考として、をご覧ください。

句ビルダー

データベースによって句が生成する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

これは、GORMがデータベースドライバによる句ビルダーの登録を許可し、デフォルトの句ビルダーを置き換えることができるためサポートされています。Limitを例としてご覧ください。

句オプション

GORMは多くの句を定義しており、一部の句はアプリケーションで使用できる高度なオプションを提供しています。

ほとんどはめったに使用されませんが、GORMの公開APIが要件に合わない場合は、それらを調べてみるのが良いでしょう。例:

db.Clauses(clause.Insert{Modifier: "IGNORE"}).Create(&user)
// INSERT IGNORE INTO users (name,age...) VALUES ("jinzhu",18...);

StatementModifier

GORMはインターフェースStatementModifierを提供しており、これによりステートメントを変更して要件を満たすことができます。ヒントを例としてご覧ください。

import "gorm.io/hints"

db.Clauses(hints.New("hint")).Find(&User{})
// SELECT * /*+ hint */ FROM `users`

プラチナスポンサー

ゴールドスポンサー

プラチナスポンサー

ゴールドスポンサー