データのクエリ

データを返す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メソッドは最初の行以外をすべて破棄します。

QueryRowContextQueryRowと同様に動作しますが、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を呼び出して列の値を変数にコピーします。

QueryContextQueryと同様に動作しますが、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.Query
DB.QueryContext
クエリを単独で実行します。
Tx.Query
Tx.QueryContext
より大きなトランザクション内でクエリを実行します。詳細については、トランザクションの実行を参照してください。
Stmt.Query
Stmt.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 CHARVARCHARTEXTからGo stringに変換します。しかし、Scanは列の値に適した別のGoの型への変換も実行します。例えば、列が常に数値を格納するVARCHARである場合、値を受け取るためにintのような数値Go型を指定でき、Scanstrconv.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)
}