Go言語でのdb.QueryとSELECT文を用いたデータ読み取りを徹底解説

Go言語を使用したアプリケーション開発では、データベースとの連携が重要な役割を果たします。その中でも、データの読み取りは、ユーザーインターフェースに情報を表示したり、ロジックの判断材料とする上で欠かせません。本記事では、Go言語の標準ライブラリdatabase/sqlを使用して、データベースから効率的にデータを取得する方法を解説します。特に、db.Queryメソッドを用いたSELECT文の活用に焦点を当て、基本的な使い方からエラーハンドリング、セキュリティ対策、応用例までを詳しく掘り下げます。Go言語を使って実践的なデータベース操作を学びたい方にとって、役立つ内容となっています。

目次

Go言語でのデータベース操作の概要


Go言語でデータベースと連携する際には、標準ライブラリであるdatabase/sqlパッケージを使用します。このパッケージは、SQLデータベースへの接続、クエリの実行、トランザクションの管理など、データベース操作の基本機能を提供します。

データベースドライバの選択


database/sql自体は抽象的なインターフェースを提供するため、具体的なデータベースドライバが必要です。例えば、以下のようなドライバがあります。

  • MySQL用: github.com/go-sql-driver/mysql
  • PostgreSQL用: github.com/lib/pq
  • SQLite用: modernc.org/sqlite

データベース接続の基本手順


Goでデータベースに接続する際の手順は以下の通りです。

  1. ドライバのインポート:
    ドライバをインポートしてdatabase/sqlに登録します。
import (
    "database/sql"
    _ "github.com/go-sql-driver/mysql" // MySQL用
)
  1. 接続設定:
    sql.Open関数でデータベースに接続します。
db, err := sql.Open("mysql", "user:password@tcp(127.0.0.1:3306)/dbname")
if err != nil {
    log.Fatal(err)
}
defer db.Close()
  1. 接続確認:
    db.Pingで接続が有効か確認します。
if err := db.Ping(); err != nil {
    log.Fatal(err)
}

データベース操作の種類


database/sqlを使用すると、以下の操作が可能です。

  • クエリの実行 (db.Query, db.QueryRow)
  • データの挿入 (db.Exec)
  • トランザクション処理 (db.Begin, tx.Commit, tx.Rollback)

これらを組み合わせることで、ほとんどのデータベース操作をカバーできます。次に、具体的なクエリ操作の手法に進んでいきます。

`db.Query`メソッドの基本的な使い方


db.Queryは、Go言語でデータベースに対してSQLクエリを実行し、複数行の結果を取得するために使用されるメソッドです。このメソッドは特に、SELECT文を用いたデータの読み取りに適しています。

`db.Query`の概要


db.Queryは、SQLクエリを受け取り、結果を行ごとに処理できる*sql.Rows型を返します。この*sql.Rowsを使うことで、複数行のデータを一行ずつ処理することが可能です。

基本的なコード例


以下は、db.Queryを使用してデータベースからデータを取得する基本的な例です。

package main

import (
    "database/sql"
    "fmt"
    "log"
    _ "github.com/go-sql-driver/mysql" // MySQL用ドライバ
)

func main() {
    // データベース接続
    db, err := sql.Open("mysql", "user:password@tcp(127.0.0.1:3306)/dbname")
    if err != nil {
        log.Fatal(err)
    }
    defer db.Close()

    // クエリ実行
    rows, err := db.Query("SELECT id, name FROM users")
    if err != nil {
        log.Fatal(err)
    }
    defer rows.Close()

    // 結果の処理
    for rows.Next() {
        var id int
        var name string
        if err := rows.Scan(&id, &name); err != nil {
            log.Fatal(err)
        }
        fmt.Printf("ID: %d, Name: %s\n", id, name)
    }

    // エラー確認
    if err := rows.Err(); err != nil {
        log.Fatal(err)
    }
}

コードのポイント

  1. db.Queryの実行:
    クエリを文字列として渡します。結果が返される場合、rowsオブジェクトを受け取ります。
  2. rows.Scanでデータをマッピング:
    各行のデータを変数に読み込むためにScanメソッドを使用します。引数としてポインタを渡します。
  3. エラーハンドリング:
    rows.Nextrows.Scanでエラーが発生する場合に備え、rows.Errを確認します。

使用時の注意点

  • リソースの解放:
    rows.Closeを忘れずに呼び出して、取得した結果セットを解放します。
  • SQLインジェクション対策:
    クエリに動的な値を含める場合は、プレースホルダを利用することでセキュリティを確保します(これについては後述します)。

このように、db.Queryはデータベースから複数行のデータを取得するために非常に有用なメソッドです。次章では、取得したデータの詳細な処理方法について解説します。

データのスキャンと処理


データベースから取得した結果を正しく処理するためには、db.Queryが返す*sql.Rowsを用いて、データを変数にマッピングする必要があります。このプロセスにはScanメソッドを使用します。

`Scan`メソッドの概要


Scanメソッドは、クエリの結果から取得した列の値を、Goの変数に割り当てるために使用されます。この方法によって、データベースの列とGoのプログラム内のデータ型をリンクさせることができます。

基本的なコード例


以下のコードは、Scanを使用してデータベースの結果を変数に格納する例です。

package main

import (
    "database/sql"
    "fmt"
    "log"
    _ "github.com/go-sql-driver/mysql"
)

func main() {
    // データベース接続
    db, err := sql.Open("mysql", "user:password@tcp(127.0.0.1:3306)/dbname")
    if err != nil {
        log.Fatal(err)
    }
    defer db.Close()

    // クエリ実行
    rows, err := db.Query("SELECT id, name, age FROM users")
    if err != nil {
        log.Fatal(err)
    }
    defer rows.Close()

    // 結果をスキャン
    for rows.Next() {
        var id int
        var name string
        var age int
        if err := rows.Scan(&id, &name, &age); err != nil {
            log.Fatal(err)
        }
        fmt.Printf("ID: %d, Name: %s, Age: %d\n", id, name, age)
    }

    // エラーチェック
    if err := rows.Err(); err != nil {
        log.Fatal(err)
    }
}

コードのポイント

  1. カラム順序の確認:
    Scanメソッドの引数は、SQLクエリで選択した列の順序と一致している必要があります。順序が異なるとエラーが発生します。
  2. データ型の一致:
    データベースの列の型とGoの変数の型を一致させる必要があります。例えば、SQLのVARCHAR型の列はGoのstring型で受け取ります。
  3. NULL値の取り扱い:
    データベースの列にNULL値が含まれている場合、Goの標準型では直接受け取ることができません。sql.NullStringsql.NullInt64といった特殊型を使用する必要があります。

NULL値を扱う例

import "database/sql"

// NULL値に対応するための変数型
var name sql.NullString
var age sql.NullInt64

if err := rows.Scan(&name, &age); err != nil {
    log.Fatal(err)
}

if name.Valid {
    fmt.Println("Name:", name.String)
} else {
    fmt.Println("Name is NULL")
}

if age.Valid {
    fmt.Println("Age:", age.Int64)
} else {
    fmt.Println("Age is NULL")
}

処理後のエラーチェック


rows.Nextのループ後にrows.Errを確認することで、Scan中のエラーやクエリ実行中の問題を検出できます。

まとめ


Scanを使用することで、データベースから取得した結果を効率的にプログラム内で利用可能にすることができます。正しい型と順序を守ることで、エラーを防ぎつつ柔軟にデータを扱うことが可能です。次に、エラーハンドリングの詳細について解説します。

エラーハンドリングの重要性と実践例


データベース操作においてエラーハンドリングは非常に重要です。適切なエラーハンドリングを行うことで、予期しない動作を防ぎ、問題の原因を特定しやすくなります。本章では、db.Queryを使用する際の具体的なエラーハンドリング手法を解説します。

エラーハンドリングの基本原則

  1. エラーの発生箇所で適切に処理する:
    SQLクエリの実行、rows.Scanの実行、rows.Errの確認など、各操作でエラーをチェックします。
  2. エラー内容を記録する:
    ログにエラーを出力し、詳細な情報を保存することで、トラブルシューティングを容易にします。
  3. エラーに応じた適切な対応を行う:
    必要に応じてリトライ処理やユーザーへの通知を行います。

`db.Query`でのエラーハンドリング


以下のコードは、db.Queryを使用する際のエラーハンドリングを示しています。

package main

import (
    "database/sql"
    "fmt"
    "log"
    _ "github.com/go-sql-driver/mysql"
)

func main() {
    // データベース接続
    db, err := sql.Open("mysql", "user:password@tcp(127.0.0.1:3306)/dbname")
    if err != nil {
        log.Fatalf("Failed to connect to database: %v", err)
    }
    defer db.Close()

    // クエリ実行
    rows, err := db.Query("SELECT id, name FROM users WHERE age > ?", 18)
    if err != nil {
        log.Fatalf("Query failed: %v", err)
    }
    defer rows.Close()

    // 結果を処理
    for rows.Next() {
        var id int
        var name string
        if err := rows.Scan(&id, &name); err != nil {
            log.Printf("Error scanning row: %v", err)
            continue // 問題がある行をスキップ
        }
        fmt.Printf("ID: %d, Name: %s\n", id, name)
    }

    // ループ後のエラーチェック
    if err := rows.Err(); err != nil {
        log.Fatalf("Error occurred during iteration: %v", err)
    }
}

エラーハンドリングのポイント

  1. 接続エラーの確認:
    sql.Open後、必ずdb.Pingを使用して接続が成功していることを確認します。
  2. クエリエラーの対処:
    SQL文に誤りがある場合や接続がタイムアウトした場合、db.Queryがエラーを返します。適切なログ出力と処理を行います。
  3. スキャンエラーの対策:
    Scanでの型不一致や値の読み込みエラーを検知し、問題がある行をスキップする処理を入れることで、プログラム全体の中断を防ぎます。

エラー時のリトライ処理


エラーが一時的なものである場合、リトライ処理を導入することが効果的です。

func queryWithRetry(db *sql.DB, query string, args ...interface{}) (*sql.Rows, error) {
    for i := 0; i < 3; i++ { // 最大3回リトライ
        rows, err := db.Query(query, args...)
        if err == nil {
            return rows, nil
        }
        log.Printf("Query failed (attempt %d): %v", i+1, err)
    }
    return nil, fmt.Errorf("all retry attempts failed")
}

エラーの分類と対応


エラーの種類に応じて適切な対応を取ることが重要です。

  • ネットワークエラー: リトライ処理を検討します。
  • データ型エラー: クエリやスキャンのロジックを見直します。
  • データベース接続エラー: コネクションプールの設定を確認します。

まとめ


エラーハンドリングは、Goでのデータベース操作を成功させるための基盤です。エラー発生時に適切な処理を行い、ユーザーへの影響を最小限に抑えることで、アプリケーションの信頼性を向上させることができます。次に、パフォーマンスを向上させる方法について解説します。

パフォーマンスの考慮


Go言語を使用してデータベースとやり取りする際、パフォーマンスの最適化はアプリケーション全体のスケーラビリティやユーザーエクスペリエンスに直結します。本章では、効率的なデータベース操作のためのベストプラクティスを解説します。

コネクションプールの活用


データベース接続は高コストな操作です。頻繁に接続を作成・閉鎖すると、パフォーマンスが大幅に低下します。Goのdatabase/sqlパッケージは、デフォルトでコネクションプールを管理しますが、設定を調整することでさらに効率的に動作させることができます。

コネクションプールの設定例


以下は、コネクションプールの最大接続数やアイドル接続数を設定する例です。

db, err := sql.Open("mysql", "user:password@tcp(127.0.0.1:3306)/dbname")
if err != nil {
    log.Fatal(err)
}
defer db.Close()

// 最大オープン接続数を設定
db.SetMaxOpenConns(10)

// 最大アイドル接続数を設定
db.SetMaxIdleConns(5)

// 接続のライフタイムを設定
db.SetConnMaxLifetime(60 * time.Second)

設定のポイント

  • SetMaxOpenConns: アプリケーションで同時にオープン可能な接続の最大数を設定します。多すぎるとデータベースが過負荷になります。
  • SetMaxIdleConns: アイドル状態で保持する接続数を制限します。過剰なアイドル接続はリソースの無駄になります。
  • SetConnMaxLifetime: 接続の寿命を設定して、古い接続が自動的に再作成されるようにします。

バッチ処理によるデータ読み取り


大量のデータを取得する際、一度に全てを読み込むとメモリ使用量が増大します。バッチ処理を利用することでメモリ効率を向上させることができます。

例: バッチ処理によるデータ取得

const batchSize = 100

for offset := 0; ; offset += batchSize {
    rows, err := db.Query("SELECT id, name FROM users LIMIT ? OFFSET ?", batchSize, offset)
    if err != nil {
        log.Fatal(err)
    }

    var hasRows bool
    for rows.Next() {
        hasRows = true
        var id int
        var name string
        if err := rows.Scan(&id, &name); err != nil {
            log.Fatal(err)
        }
        fmt.Printf("ID: %d, Name: %s\n", id, name)
    }

    rows.Close()

    // 結果が空なら終了
    if !hasRows {
        break
    }
}

インデックスの利用


データベース内でのクエリ速度を向上させるには、インデックスの設定が不可欠です。適切なインデックスを作成することで、WHERE句やJOIN句を使用したクエリの速度が劇的に向上します。

インデックスの作成例


MySQLでは以下のようにインデックスを作成します。

CREATE INDEX idx_users_name ON users(name);

インデックスは頻繁に検索やフィルタリングが行われる列に対して作成するのが最適です。

プレースホルダを使用した効率的なクエリ


動的なクエリを実行する場合、直接値を文字列に埋め込むのではなく、プレースホルダを使用することでパフォーマンスとセキュリティを両立できます。

rows, err := db.Query("SELECT id, name FROM users WHERE age > ?", 18)
if err != nil {
    log.Fatal(err)
}
defer rows.Close()

クエリのプロファイリング


データベース管理ツール(例: MySQLのEXPLAINステートメント)を使用してクエリをプロファイルすることで、ボトルネックを特定し、改善することができます。

EXPLAIN SELECT id, name FROM users WHERE age > 18;

まとめ


Goでデータベース操作のパフォーマンスを最適化するためには、コネクションプールの適切な管理、バッチ処理の活用、インデックスの設定が重要です。これらの技術を活用することで、大規模データベースを効率的に操作し、アプリケーションのスケーラビリティを向上させることができます。次に、複雑な条件を使用したSELECTクエリについて解説します。

応用例:複数条件の`SELECT`クエリ


データベースのSELECTクエリでは、複数条件を指定することで柔軟なデータ取得が可能になります。Go言語では、プレースホルダを使用して動的な条件を安全に組み込むことができます。本章では、複数条件を使用したクエリの具体例とその応用方法を解説します。

複数条件を使用した`WHERE`句


WHERE句では、条件をANDORで組み合わせて、必要なデータを絞り込むことができます。

基本的なコード例


以下の例では、複数条件を指定してagestatusの両方を満たすユーザーを取得します。

package main

import (
    "database/sql"
    "fmt"
    "log"
    _ "github.com/go-sql-driver/mysql"
)

func main() {
    // データベース接続
    db, err := sql.Open("mysql", "user:password@tcp(127.0.0.1:3306)/dbname")
    if err != nil {
        log.Fatal(err)
    }
    defer db.Close()

    // クエリ実行
    query := "SELECT id, name, age FROM users WHERE age > ? AND status = ?"
    rows, err := db.Query(query, 18, "active")
    if err != nil {
        log.Fatalf("Query failed: %v", err)
    }
    defer rows.Close()

    // 結果の処理
    for rows.Next() {
        var id int
        var name string
        var age int
        if err := rows.Scan(&id, &name, &age); err != nil {
            log.Fatalf("Error scanning row: %v", err)
        }
        fmt.Printf("ID: %d, Name: %s, Age: %d\n", id, name, age)
    }

    // エラーチェック
    if err := rows.Err(); err != nil {
        log.Fatalf("Row iteration error: %v", err)
    }
}

複数条件を動的に構築する


複数条件を動的に追加する場合、条件を組み立てるロジックを工夫する必要があります。

動的条件を組み立てる例


以下のコードは、動的に条件を構築する例です。

package main

import (
    "database/sql"
    "fmt"
    "log"
    "strings"
    _ "github.com/go-sql-driver/mysql"
)

func main() {
    db, err := sql.Open("mysql", "user:password@tcp(127.0.0.1:3306)/dbname")
    if err != nil {
        log.Fatal(err)
    }
    defer db.Close()

    // 動的条件の構築
    conditions := []string{}
    args := []interface{}{}

    minAge := 20
    status := "active"

    if minAge > 0 {
        conditions = append(conditions, "age > ?")
        args = append(args, minAge)
    }
    if status != "" {
        conditions = append(conditions, "status = ?")
        args = append(args, status)
    }

    query := "SELECT id, name FROM users"
    if len(conditions) > 0 {
        query += " WHERE " + strings.Join(conditions, " AND ")
    }

    rows, err := db.Query(query, args...)
    if err != nil {
        log.Fatalf("Query failed: %v", err)
    }
    defer rows.Close()

    for rows.Next() {
        var id int
        var name string
        if err := rows.Scan(&id, &name); err != nil {
            log.Fatalf("Error scanning row: %v", err)
        }
        fmt.Printf("ID: %d, Name: %s\n", id, name)
    }
}

コードのポイント

  • conditionsargsを使用: 条件をスライスに追加し、それに対応する値をargsに保存します。
  • strings.Joinで条件を結合: 複数条件をANDで結合します。
  • 動的条件に対応: 条件が存在しない場合でもエラーにならないように設計します。

応用例:日付範囲でのデータ取得


次の例は、日付範囲を指定してデータを取得する方法を示します。

query := "SELECT id, name FROM users WHERE created_at BETWEEN ? AND ?"
startDate := "2024-01-01"
endDate := "2024-12-31"

rows, err := db.Query(query, startDate, endDate)
if err != nil {
    log.Fatalf("Query failed: %v", err)
}
defer rows.Close()

まとめ


複数条件を活用したSELECTクエリにより、柔軟で詳細なデータ取得が可能になります。Goではプレースホルダを使って安全かつ効率的にクエリを実行できます。これらのテクニックを駆使して、より高度なデータ操作を実現しましょう。次に、セキュリティ対策について解説します。

セキュリティ対策:SQLインジェクションの防止


SQLインジェクションは、悪意のある入力によってアプリケーションのデータベースが不正に操作される攻撃手法です。このセクションでは、SQLインジェクションのリスクを理解し、Go言語を使用したデータベース操作でそれを防ぐ方法を解説します。

SQLインジェクションとは


SQLインジェクションは、ユーザー入力がSQLクエリの一部として直接挿入される際に発生します。これにより、攻撃者は不正なクエリを実行してデータベースを操作できます。

例: 脆弱なコード


以下は、SQLインジェクションのリスクを含むコード例です。

// ユーザー入力を直接クエリに挿入(危険!)
query := "SELECT id, name FROM users WHERE name = '" + userInput + "'"
rows, err := db.Query(query)
if err != nil {
    log.Fatal(err)
}

ここで、userInputに次のような悪意のある入力が含まれるとします:
' OR '1'='1' --
クエリは次のように解釈され、全てのデータが取得されてしまいます:

SELECT id, name FROM users WHERE name = '' OR '1'='1' -- '

安全なクエリの作成


SQLインジェクションを防ぐには、プレースホルダを使用してユーザー入力をクエリに安全に渡す必要があります。

例: プレースホルダを使用した安全なコード


Go言語では、?を使ったプレースホルダを使用します。

query := "SELECT id, name FROM users WHERE name = ?"
rows, err := db.Query(query, userInput)
if err != nil {
    log.Fatal(err)
}
defer rows.Close()

// 結果を処理
for rows.Next() {
    var id int
    var name string
    if err := rows.Scan(&id, &name); err != nil {
        log.Fatal(err)
    }
    fmt.Printf("ID: %d, Name: %s\n", id, name)
}

プレースホルダのメリット

  • SQL構文とデータを分離: SQLエンジンがプレースホルダを適切に処理するため、入力が直接SQL構文として解釈されません。
  • 自動エスケープ: 特殊文字が適切にエスケープされ、攻撃を防ぎます。

プリペアドステートメントを活用


プリペアドステートメントは、同じクエリを複数回実行する場合に効率的かつ安全な方法です。

プリペアドステートメントの例

stmt, err := db.Prepare("SELECT id, name FROM users WHERE age > ?")
if err != nil {
    log.Fatal(err)
}
defer stmt.Close()

rows, err := stmt.Query(18)
if err != nil {
    log.Fatal(err)
}
defer rows.Close()

// 結果を処理
for rows.Next() {
    var id int
    var name string
    if err := rows.Scan(&id, &name); err != nil {
        log.Fatal(err)
    }
    fmt.Printf("ID: %d, Name: %s\n", id, name)
}

入力データの検証


プレースホルダを使用しても、ユーザー入力の検証は不可欠です。検証により、データの一貫性を保ち、意図しないデータの操作を防ぐことができます。

入力検証の例

  • 長さ制限: 入力文字列の最大長を制限します。
  • 正規表現: 入力データが期待される形式に一致するかを確認します。
import "regexp"

var namePattern = regexp.MustCompile(`^[a-zA-Z]+$`)
if !namePattern.MatchString(userInput) {
    log.Fatalf("Invalid input: %s", userInput)
}

データベース権限の制限


アプリケーションが使用するデータベースユーザーの権限を最小限に制限することで、攻撃が成功した場合の影響を軽減できます。

  • 読み取り専用ユーザー: データを取得するだけのクエリには、読み取り専用のデータベースユーザーを使用します。
  • 特定テーブルへのアクセス制限: 必要なテーブルだけにアクセス権を付与します。

まとめ


SQLインジェクションは深刻なセキュリティリスクを引き起こしますが、Goのdatabase/sqlパッケージを正しく使用すれば、防止することができます。プレースホルダやプリペアドステートメントを利用し、さらに入力検証や権限管理を組み合わせることで、セキュリティを強化できます。次に、実践的な演習問題でこれらの知識を応用する方法を紹介します。

演習問題:データ読み取りの実践


ここでは、db.Queryを使用してデータベースからデータを読み取る実践的な演習問題を通じて、これまで学んだ知識を確認します。実際のコードを記述しながら、データベース操作のスキルを磨きましょう。

課題1: 基本的なデータ読み取り


以下のようなusersテーブルがあるとします。

idnameagestatus
1Alice25active
2Bob30inactive
3Charlie22active

このテーブルから、年齢が25歳以上のactiveなユーザーを取得し、IDと名前を出力するコードを記述してください。

ヒント

  • WHERE句でagestatusを条件として指定します。
  • プレースホルダを使用して安全にクエリを実行します。

期待される出力

ID: 1, Name: Alice

解答例

package main

import (
    "database/sql"
    "fmt"
    "log"
    _ "github.com/go-sql-driver/mysql"
)

func main() {
    // データベース接続
    db, err := sql.Open("mysql", "user:password@tcp(127.0.0.1:3306)/dbname")
    if err != nil {
        log.Fatal(err)
    }
    defer db.Close()

    // クエリ実行
    query := "SELECT id, name FROM users WHERE age >= ? AND status = ?"
    rows, err := db.Query(query, 25, "active")
    if err != nil {
        log.Fatal(err)
    }
    defer rows.Close()

    // 結果を処理
    for rows.Next() {
        var id int
        var name string
        if err := rows.Scan(&id, &name); err != nil {
            log.Fatal(err)
        }
        fmt.Printf("ID: %d, Name: %s\n", id, name)
    }
}

課題2: 動的条件によるデータ取得


複数の条件を動的に指定してデータを取得するコードを書いてみましょう。次の条件に基づいて結果を取得してください:

  1. ユーザーの年齢が指定されたminAge以上。
  2. ユーザーのステータスが指定された値。

ヒント

  • 条件が指定されていない場合、その条件を省略してください。
  • 動的にWHERE句を構築する方法を考えましょう。

期待される動作


次のように異なる条件でクエリを実行できるコードを作成してください。

  • minAge = 25, status = "active"
  • minAge = 30, status = ""(ステータス条件なし)

解答例

package main

import (
    "database/sql"
    "fmt"
    "log"
    "strings"
    _ "github.com/go-sql-driver/mysql"
)

func main() {
    db, err := sql.Open("mysql", "user:password@tcp(127.0.0.1:3306)/dbname")
    if err != nil {
        log.Fatal(err)
    }
    defer db.Close()

    // 動的条件の構築
    minAge := 25
    status := "active"

    conditions := []string{}
    args := []interface{}{}

    if minAge > 0 {
        conditions = append(conditions, "age >= ?")
        args = append(args, minAge)
    }
    if status != "" {
        conditions = append(conditions, "status = ?")
        args = append(args, status)
    }

    query := "SELECT id, name FROM users"
    if len(conditions) > 0 {
        query += " WHERE " + strings.Join(conditions, " AND ")
    }

    rows, err := db.Query(query, args...)
    if err != nil {
        log.Fatal(err)
    }
    defer rows.Close()

    for rows.Next() {
        var id int
        var name string
        if err := rows.Scan(&id, &name); err != nil {
            log.Fatal(err)
        }
        fmt.Printf("ID: %d, Name: %s\n", id, name)
    }
}

課題3: 日付範囲によるデータ取得


日付範囲を指定してusersテーブルからデータを取得してください。created_at列が指定された日付範囲内のデータを取得するクエリを記述します。

期待される動作

  • 日付範囲が2024-01-01から2024-12-31の場合、該当するユーザーを取得します。

まとめ


これらの演習を通じて、Goでの安全なデータベース操作と、柔軟な条件構築のスキルが身に付きます。次は記事全体を振り返り、総括を行います。

まとめ


本記事では、Go言語を用いたデータベース操作の基礎から応用までを解説しました。db.Queryを利用したSELECT文の基本操作に始まり、エラーハンドリングやパフォーマンス最適化、複数条件を使ったクエリの構築、SQLインジェクション対策など、安全で効率的なデータベース操作の方法を学びました。

特に、以下のポイントが重要です:

  • プレースホルダやプリペアドステートメントを活用し、セキュアなクエリを実行する。
  • 動的なクエリ条件を構築して柔軟なデータ取得を実現する。
  • コネクションプールやバッチ処理でパフォーマンスを向上させる。

これらの知識を応用し、安全でスケーラブルなデータベース操作を設計してください。実践演習を通じて理解を深め、さらに高度な操作にも挑戦していきましょう。

コメント

コメントする

目次