👨‍👩‍👦‍👦

database/sqlパッケージの複数のQueryメソッドについて

2024/03/06に公開

database/sqlパッケージのQuery,QueryContext,QueryRow,QueryRowContextメソッドは、SQLクエリを実行するための異なる関数ですが、それぞれ使用するシナリオが異なることを意外と忘れがちなので、説明を残します。
https://pkg.go.dev/database/sql

予習

この手のメソッドを活かすためにも、contextsql.Rowsが何なのかを理解しておくことが良いと思うので追記しておきます。

context

contextは、Go言語の標準ライブラリで提供されるパッケージで、リクエストやプロセス全体を通してデータを渡したり、実行中の操作をキャンセルしたり、タイムアウトを設定したりするための仕組みを提供します。contextをデータベースクエリに使用するメリットは以下の通りです。

  1. タイムアウト制御:データベース操作に時間がかかる場合、設定したタイムアウト時間が経過すると自動的にその操作を中断させることができる。これにより、システムリソースの無駄遣いを防ぎ、アプリケーションのレスポンス性を向上させることができます。
  2. キャンセル可能:ユーザーからの新たなリクエストや、他のイベントによって、現在実行中の操作が不要になった場合、その操作を途中でキャンセルすることができます。これにより、不要な処理を早期に終了させ、効率的なシステム運用が可能になります。
  3. リクエストスコープの値の受け渡し:リクエストを処理する際に、認証情報やリクエストなどの情報をcontextを通じて関数やメソッド間で簡単に受け渡すことができます。

*sql.Row & *sql.Rows

sql.Rowsは、Scanメソッドを使用してデータベースからのクエリ結果を読み取り、変数として割り当てます。ただし、sql.Rowsは複数行の結果を取り扱うことができる点で、sql.Rowと異なります。そのため、sql.Rowsオブジェクトを使用する際は、通常結果セットを反復処理するループ内でScanメソッドを呼び出します。

Query

Queryメソッドは、1つ以上の結果行を返すSQLクエリを実行するために使用されます。このメソッドは、*sql.Rowsを返し、この結果を通じて複数行を反復処理することができます。

rows, err := db.Query("SELECT id, name FROM users")
if err != nil {
    // エラー処理
}
defer rows.Close()
for rows.Next() {
    // 各行の処理
}

QueryRow

QueryRowメソッドは、単一の行の結果のみを期待するクエリを実行する際に使用されます。このメソッドは、*sql.Rowを返し、この結果から.Scan()を呼び出して単一の結果を取り出すことができます。

var name string
err := db.QueryRow("SELECT name FROM users WHERE id = ?", 1).Scan(&name)
if err != nil {
    // エラー処理
}

QueryRowContextとQueryContext

これらの関数はそれぞれQueryRowQueryのバリアントであり、追加の引数としてcontext.Contextを取ります。Contextを使用することで、リクエストのタイムアウト、キャンセル、データベース操作の実行中に追加の情報を渡すことができます。これは、長い実行時間を持つクエリや、リクエストのライフサイクルがアプリケーションの他の部分と密接に結びついている場合に有効です。

ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
defer cancel()

var name string
err := db.QueryRowContext(ctx, "SELECT name FROM users WHERE id = ?", 1).Scan(&name)
if err != nil {
    // エラー処理
}

まとめ

要約すると、Query,QueryContextは複数行の結果行を処理するために使用され、QueryRow,QueryRowContextは1行のみの結果を期待するクエリにしようされます。contextがあるものは、クエリの実行にコンテキストを適用する能力が追加されています。

Discussion