単一行のクエリ
QueryRow
は、ユニークIDでデータを検索したいときなど、最大で単一のデータベース行を取得します。クエリによって複数の行が返された場合、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.QueryRow DB.QueryRowContext |
単一行のクエリを孤立して実行します。 |
Tx.QueryRow Tx.QueryRowContext |
より大きなトランザクション内で単一行のクエリを実行します。詳細については、 トランザクションの実行を参照してください。 |
Stmt.QueryRow Stmt.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
への遅延呼び出しに注意してください。これにより、関数がどのように戻っても、行によって保持されているリソースが解放されます。すべての行をループすることも暗黙的にそれを閉じますが、defer
を使用して rows
が必ず閉じられるようにする方が良いです。
注意: 準備されたステートメントのパラメータプレースホルダーは、使用しているDBMSおよびドライバによって異なります。たとえば、Postgres用のpqドライバは、$1
のようなプレースホルダーを必要とし、?
は必要ありません。
エラー処理
sql.Rows
のクエリ結果をループした後は、エラーを確認することを忘れないでください。クエリが失敗した場合、これがコードが知る方法です。
複数行を返すための関数
関数 | 説明 |
---|---|
DB.Query DB.QueryContext |
クエリを孤立して実行します。 |
Tx.Query Tx.QueryContext |
より大きなトランザクション内でクエリを実行します。詳細については、 トランザクションの実行を参照してください。 |
Stmt.Query Stmt.QueryContext |
すでに準備されたステートメントを使用してクエリを実行します。詳細については、 準備されたステートメントの使用を参照してください。 |
Conn.QueryContext |
予約された接続で使用します。詳細については、 接続の管理を参照してください。 |
ヌル可能な列値の処理
database/sql
パッケージは、列の値がヌルである可能性がある場合に Scan
関数の引数として使用できるいくつかの特別な型を提供します。各型には、値がヌルでないかどうかを報告する Valid
フィールドと、そうであればその値を保持するフィールドが含まれています。
以下の例のコードは、顧客名をクエリします。名前の値がヌルの場合、コードはアプリケーションで使用するために別の値を代入します。
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
パッケージリファレンスを参照してください:
列からデータを取得する
クエリによって返された行をループする際、Scan
を使用して行の列値をGoの値にコピーします。これは、Rows.Scan
リファレンスで説明されています。
すべてのドライバでサポートされている基本的なデータ変換のセットがあり、たとえばSQL INT
をGo int
に変換します。一部のドライバはこの変換セットを拡張します。詳細については、各ドライバのドキュメントを参照してください。
予想通り、Scan
は列型から類似のGo型に変換します。たとえば、Scan
はSQL CHAR
、VARCHAR
、TEXT
をGo string
に変換します。ただし、Scan
は、列値に適した別のGo型への変換も行います。たとえば、列が常に数値を含む VARCHAR
の場合、数値のGo型(int
など)を指定して値を受け取ることができ、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)
}