データのクエリ
データを返すSQLステートメントを実行する場合は、database/sqlパッケージで提供されているQueryメソッドのいずれかを使用します。これらはそれぞれRowまたはRowsを返し、そのデータをScanメソッドを使用して変数にコピーできます。例えば、SELECTステートメントを実行する際にこれらのメソッドを使用します。
データを返さないステートメントを実行する場合は、代わりにExecまたはExecContextメソッドを使用できます。詳細については、データを返さないステートメントの実行を参照してください。
database/sqlパッケージは、結果のクエリを実行する2つの方法を提供します。
- 単一行のクエリ –
QueryRowはデータベースから最大で1つのRowを返します。詳細については、単一行のクエリを参照してください。 - 複数行のクエリ –
Queryは一致するすべての行をRows構造体として返し、コードでループ処理できます。詳細については、複数行のクエリを参照してください。
コードが同じSQLステートメントを繰り返し実行する場合は、プリペアドステートメントの使用を検討してください。詳細については、プリペアドステートメントの使用を参照してください。
注意: SQLステートメントを組み立てるためにfmt.Sprintfのような文字列フォーマット関数を使用しないでください!SQLインジェクションのリスクを招く可能性があります。詳細については、SQLインジェクションリスクの回避を参照してください。
単一行のクエリ
QueryRowは、一意のIDでデータを検索したい場合など、最大で1つのデータベース行を取得します。クエリによって複数の行が返された場合、Scanメソッドは最初の行以外をすべて破棄します。
QueryRowContextはQueryRowと同様に動作しますが、context.Context引数を持ちます。詳細については、進行中の操作のキャンセルを参照してください。
次の例では、クエリを使用して購入をサポートするのに十分な在庫があるかどうかを調べます。十分な在庫がある場合はSQLステートメントはtrueを返し、ない場合はfalseを返します。Row.Scanは、ポインタを介してブール値の戻り値をenough変数にコピーします。
func canPurchase(id int, quantity int) (bool, error) {
var enough bool
// Query for a value based on a single row.
if err := db.QueryRow("SELECT (quantity >= ?) from album where id = ?",
quantity, id).Scan(&enough); err != nil {
if err == sql.ErrNoRows {
return false, fmt.Errorf("canPurchase %d: unknown album", id)
}
return false, fmt.Errorf("canPurchase %d: %v", id, err)
}
return enough, nil
}
注意: プリペアドステートメントのパラメータープレースホルダーは、使用しているDBMSおよびドライバによって異なります。例えば、Postgres用のpqドライバは、?の代わりに$1のようなプレースホルダーを必要とします。
エラーの処理
QueryRow自体はエラーを返しません。代わりに、Scanは検索とスキャンの組み合わせからのエラーを報告します。クエリが行を見つけられなかった場合、sql.ErrNoRowsを返します。
単一行を返す関数
| 関数 | 説明 |
|---|---|
DB.QueryRowDB.QueryRowContext
|
単一行クエリを単独で実行します。 |
Tx.QueryRowTx.QueryRowContext
|
より大きなトランザクション内で単一行クエリを実行します。詳細については、トランザクションの実行を参照してください。 |
Stmt.QueryRowStmt.QueryRowContext
|
すでに準備されたステートメントを使用して単一行クエリを実行します。詳細については、プリペアドステートメントの使用を参照してください。 |
Conn.QueryRowContext
|
予約済み接続での使用。詳細については、接続の管理を参照してください。 |
複数行のクエリ
QueryまたはQueryContextを使用して複数行をクエリできます。これらはクエリ結果を表すRowsを返します。コードは、Rows.Nextを使用して返された行を反復処理します。各反復はScanを呼び出して列の値を変数にコピーします。
QueryContextはQueryと同様に動作しますが、context.Context引数を持ちます。詳細については、進行中の操作のキャンセルを参照してください。
次の例では、指定されたアーティストのアルバムを返すクエリを実行します。アルバムはsql.Rowsで返されます。コードは、ポインタによって表される変数に列の値をコピーするためにRows.Scanを使用します。
func albumsByArtist(artist string) ([]Album, error) {
rows, err := db.Query("SELECT * FROM album WHERE artist = ?", artist)
if err != nil {
return nil, err
}
defer rows.Close()
// An album slice to hold data from returned rows.
var albums []Album
// Loop through rows, using Scan to assign column data to struct fields.
for rows.Next() {
var alb Album
if err := rows.Scan(&alb.ID, &alb.Title, &alb.Artist,
&alb.Price, &alb.Quantity); err != nil {
return albums, err
}
albums = append(albums, alb)
}
if err = rows.Err(); err != nil {
return albums, err
}
return albums, nil
}
rows.Closeへの遅延呼び出しに注意してください。これは、関数がどのように返されても、行が保持するすべてのリソースを解放します。行を最後までループ処理することでも暗黙的に閉じられますが、何があってもrowsが閉じられるようにdeferを使用する方が良いでしょう。
注意: プリペアドステートメントのパラメータープレースホルダーは、使用しているDBMSおよびドライバによって異なります。例えば、Postgres用のpqドライバは、?の代わりに$1のようなプレースホルダーを必要とします。
エラーの処理
クエリ結果をループ処理した後、sql.Rowsからのエラーを必ず確認してください。クエリが失敗した場合、これがコードがそれを知る方法です。
複数行を返す関数
| 関数 | 説明 |
|---|---|
DB.QueryDB.QueryContext
|
クエリを単独で実行します。 |
Tx.QueryTx.QueryContext
|
より大きなトランザクション内でクエリを実行します。詳細については、トランザクションの実行を参照してください。 |
Stmt.QueryStmt.QueryContext
|
すでに準備されたステートメントを使用してクエリを実行します。詳細については、プリペアドステートメントの使用を参照してください。 |
Conn.QueryContext
|
予約済み接続での使用。詳細については、接続の管理を参照してください。 |
NULL許容列値の処理
database/sqlパッケージは、列の値がNULLになる可能性がある場合にScan関数の引数として使用できるいくつかの特殊な型を提供します。それぞれには、値が非NULLであるかどうかを報告するValidフィールドと、そうである場合に値を保持するフィールドが含まれています。
次の例のコードは、顧客名をクエリします。名前の値がNULLの場合、コードはアプリケーションで使用するために別の値に置き換えます。
var s sql.NullString
err := db.QueryRow("SELECT name FROM customer WHERE id = ?", id).Scan(&s)
if err != nil {
log.Fatal(err)
}
// Find customer name, using placeholder if not present.
name := "Valued Customer"
if s.Valid {
name = s.String
}
sqlパッケージのリファレンスで各型について詳しく見る
列からのデータの取得
クエリによって返された行をループ処理する場合、Rows.Scanリファレンスで説明されているように、Scanを使用して行の列の値をGoの値にコピーします。
すべてのドライバでサポートされている基本的なデータ変換セットがあります。例えば、SQL INTからGo intへの変換です。一部のドライバはこの変換セットを拡張しています。詳細については、各ドライバのドキュメントを参照してください。
ご想像のとおり、Scanは列の型から類似したGoの型に変換します。例えば、ScanはSQL CHAR、VARCHAR、TEXTからGo stringに変換します。しかし、Scanは列の値に適した別のGoの型への変換も実行します。例えば、列が常に数値を格納するVARCHARである場合、値を受け取るためにintのような数値Go型を指定でき、Scanはstrconv.Atoiを使用して変換を行います。
Scan関数によって行われる変換の詳細については、Rows.Scanリファレンスを参照してください。
複数の結果セットの処理
データベース操作が複数の結果セットを返す可能性がある場合、Rows.NextResultSetを使用してそれらを取得できます。これは、例えば、複数のテーブルを個別にクエリし、それぞれに結果セットを返すSQLを送信する場合に役立ちます。
Rows.NextResultSetは次の結果セットを準備し、Rows.Nextの呼び出しがその次のセットの最初の行を取得するようにします。次の結果セットがまったく存在するかどうかを示すブール値を返します。
次の例のコードは、DB.Queryを使用して2つのSQLステートメントを実行します。最初の結果セットは、プロシージャの最初のクエリからのもので、albumテーブルのすべての行を取得します。次の結果セットは、2番目のクエリからのもので、songテーブルから行を取得します。
rows, err := db.Query("SELECT * from album; SELECT * from song;")
if err != nil {
log.Fatal(err)
}
defer rows.Close()
// Loop through the first result set.
for rows.Next() {
// Handle result set.
}
// Advance to next result set.
rows.NextResultSet()
// Loop through the second result set.
for rows.Next() {
// Handle second set.
}
// Check for any error in either result set.
if err := rows.Err(); err != nil {
log.Fatal(err)
}