単一行のクエリ

QueryRow は、ユニークIDでデータを検索したいときなど、最大で単一のデータベース行を取得します。クエリによって複数の行が返された場合、Scan メソッドは最初の行以外をすべて破棄します。

QueryRowContextQueryRow のように動作しますが、context.Context 引数を持ちます。詳細については、進行中の操作のキャンセルを参照してください。

以下の例では、購入をサポートするのに十分な在庫があるかどうかを確認するためにクエリを使用します。SQL文は、十分な在庫がある場合は true を、そうでない場合は false を返します。Row.Scan は、ブール値の戻り値をポインタを介して enough 変数にコピーします。

  1. func canPurchase(id int, quantity int) (bool, error) {
  2. var enough bool
  3. // Query for a value based on a single row.
  4. if err := db.QueryRow("SELECT (quantity >= ?) from album where id = ?",
  5. quantity, id).Scan(&enough); err != nil {
  6. if err == sql.ErrNoRows {
  7. return false, fmt.Errorf("canPurchase %d: unknown album", id)
  8. }
  9. return false, fmt.Errorf("canPurchase %d: %v", id, err)
  10. }
  11. return enough, nil
  12. }

注意: 準備されたステートメントのパラメータプレースホルダーは、使用している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 を呼び出して列の値を変数にコピーします。

QueryContextQuery のように動作しますが、context.Context 引数を持ちます。詳細については、進行中の操作のキャンセルを参照してください。

以下の例では、指定されたアーティストによるアルバムを返すクエリを実行します。アルバムは sql.Rows に返されます。コードは、Rows.Scan を使用して、ポインタで表される変数に列の値をコピーします。

  1. func albumsByArtist(artist string) ([]Album, error) {
  2. rows, err := db.Query("SELECT * FROM album WHERE artist = ?", artist)
  3. if err != nil {
  4. return nil, err
  5. }
  6. defer rows.Close()
  7. // An album slice to hold data from returned rows.
  8. var albums []Album
  9. // Loop through rows, using Scan to assign column data to struct fields.
  10. for rows.Next() {
  11. var alb Album
  12. if err := rows.Scan(&alb.ID, &alb.Title, &alb.Artist,
  13. &alb.Price, &alb.Quantity); err != nil {
  14. return albums, err
  15. }
  16. albums = append(albums, alb)
  17. }
  18. if err = rows.Err(); err != nil {
  19. return albums, err
  20. }
  21. return albums, nil
  22. }

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 フィールドと、そうであればその値を保持するフィールドが含まれています。

以下の例のコードは、顧客名をクエリします。名前の値がヌルの場合、コードはアプリケーションで使用するために別の値を代入します。

  1. var s sql.NullString
  2. err := db.QueryRow("SELECT name FROM customer WHERE id = ?", id).Scan(&s)
  3. if err != nil {
  4. log.Fatal(err)
  5. }
  6. // Find customer name, using placeholder if not present.
  7. name := "Valued Customer"
  8. if s.Valid {
  9. name = s.String
  10. }

各型の詳細については、sql パッケージリファレンスを参照してください:

列からデータを取得する

クエリによって返された行をループする際、Scan を使用して行の列値をGoの値にコピーします。これは、Rows.Scan リファレンスで説明されています。

すべてのドライバでサポートされている基本的なデータ変換のセットがあり、たとえばSQL INT をGo int に変換します。一部のドライバはこの変換セットを拡張します。詳細については、各ドライバのドキュメントを参照してください。

予想通り、Scan は列型から類似のGo型に変換します。たとえば、Scan はSQL CHARVARCHARTEXT をGo string に変換します。ただし、Scan は、列値に適した別のGo型への変換も行います。たとえば、列が常に数値を含む VARCHAR の場合、数値のGo型(int など)を指定して値を受け取ることができ、Scanstrconv.Atoi を使用してそれを変換します。

Scan 関数によって行われる変換の詳細については、Rows.Scan リファレンスを参照してください。

複数の結果セットの処理

データベース操作が複数の結果セットを返す可能性がある場合、Rows.NextResultSet を使用してそれらを取得できます。これは、複数のテーブルを別々にクエリするSQLを送信する場合などに便利です。

Rows.NextResultSet は次の結果セットを準備し、Rows.Next を呼び出すとその次のセットから最初の行を取得します。次の結果セットが存在するかどうかを示すブール値を返します。

以下の例のコードは、DB.Query を使用して2つのSQL文を実行します。最初の結果セットは、手続き内の最初のクエリからのもので、album テーブルのすべての行を取得します。次の結果セットは、2番目のクエリからのもので、song テーブルの行を取得します。

  1. rows, err := db.Query("SELECT * from album; SELECT * from song;")
  2. if err != nil {
  3. log.Fatal(err)
  4. }
  5. defer rows.Close()
  6. // Loop through the first result set.
  7. for rows.Next() {
  8. // Handle result set.
  9. }
  10. // Advance to next result set.
  11. rows.NextResultSet()
  12. // Loop through the second result set.
  13. for rows.Next() {
  14. // Handle second set.
  15. }
  16. // Check for any error in either result set.
  17. if err := rows.Err(); err != nil {
  18. log.Fatal(err)
  19. }