Go Wiki: SQLInterface

はじめに

database/sqlパッケージは、SQL(またはSQLライク)データベースに関する汎用インターフェースを提供します。詳細については、公式ドキュメントを参照してください。

このページでは、使用パターンの例を示します。

データベースドライバ

database/sqlパッケージは、データベースドライバと組み合わせて使用する必要があります。ドライバのリストについては、https://go.dokyumento.jp/s/sqldriversを参照してください。

以下のドキュメントでは、ドライバがインポートされていることを前提としています。

データベースへの接続

Openは、データベースハンドルを作成するために使用されます

db, err := sql.Open(driver, dataSourceName)

ここで、driverはデータベースドライバを指定し、dataSourceNameはデータベース名や認証情報など、データベース固有の接続情報を指定します。

Openはデータベース接続を直接開くのではなく、クエリが実行されるまで延期されることに注意してください。クエリを実行する前に接続が確立できることを確認するには、PingContextメソッドを使用します

if err := db.PingContext(ctx); err != nil {
  log.Fatal(err)
}

使用後、データベースはCloseを使用して閉じます。

クエリの実行

ExecContextは、行が返されないクエリに使用されます

result, err := db.ExecContext(ctx,
    "INSERT INTO users (name, age) VALUES ($1, $2)",
    "gopher",
    27,
)

ここで、resultには、最後の挿入IDと影響を受けた行数が含まれます。これらの値の可用性は、データベースドライバによって異なります。

QueryContextは、取得に使用されます

rows, err := db.QueryContext(ctx, "SELECT name FROM users WHERE age = $1", age)
if err != nil {
    log.Fatal(err)
}
defer rows.Close()
for rows.Next() {
    var name string
    if err := rows.Scan(&name); err != nil {
        log.Fatal(err)
    }
    fmt.Printf("%s is %d\n", name, age)
}
if err := rows.Err(); err != nil {
    log.Fatal(err)
}

QueryRowContextは、単一行のみが予期される場合に使用されます

var age int64
err := db.QueryRowContext(ctx, "SELECT age FROM users WHERE name = $1", name).Scan(&age)

プリペアドステートメントは、PrepareContextで作成できます

age := 27
stmt, err := db.PrepareContext(ctx, "SELECT name FROM users WHERE age = $1")
if err != nil {
    log.Fatal(err)
}
rows, err := stmt.Query(age)
// process rows

ExecContextQueryContext、およびQueryRowContextは、ステートメントに対して呼び出すことができます。使用後、ステートメントはCloseで閉じる必要があります。

トランザクション

トランザクションは、BeginTxで開始されます

tx, err := db.BeginTx(ctx, nil)
if err != nil {
    log.Fatal(err)
}

すでに説明したExecContextQueryContextQueryRowContext、およびPrepareContextメソッドをトランザクションで使用できます。

トランザクションは、CommitまたはRollbackの呼び出しで終了する必要があります。

NULLの処理

データベースの列がNULL可能な場合は、NULL値をサポートする型のいずれかをScanに渡す必要があります。

たとえば、namesテーブルのname列がNULL可能な場合

var name sql.NullString
err := db.QueryRowContext(ctx, "SELECT name FROM names WHERE id = $1", id).Scan(&name)
...
if name.Valid {
    // use name.String
} else {
    // value is NULL
}

database/sqlで実装されているのは、NullByteNullBoolNullFloat64NullInt64NullInt32 NullInt16NullString、およびNullTimeのみです。データベース固有のNULL型の実装は、データベースドライバに任されています。NULLをサポートするユーザ型は、インターフェースdatabase/sql/driver.Valuerdatabase/sql.Scannerを実装することで作成できます。

ポインタ型を渡すこともできます。メモリ割り当てが増えるため、パフォーマンスの問題には注意してください。

var name *string
err := db.QueryRowContext(ctx, "SELECT name FROM names WHERE id = $1", id).Scan(&name)

テーブルの取得

SQLクエリから構造体の配列が必要な場合。

func getTable[T any](rows *sql.Rows) (out []T) {
    var table []T
    for rows.Next() {
        var data T
        s := reflect.ValueOf(&data).Elem()
        numCols := s.NumField()
        columns := make([]interface{}, numCols)

        for i := 0; i < numCols; i++ {
            field := s.Field(i)
            columns[i] = field.Addr().Interface()
        }

        if err := rows.Scan(columns...); err != nil {
            fmt.Println("Case Read Error ", err)
        }

        table = append(table, data)
    }
    return table
}

データベースからのNULLを処理するようにしてください。

type User struct {
  UUID  sql.NullString
  Name  sql.NullString
}

rows, err := db.Query("SELECT * FROM Users")
cases := getTable[User](rows)

このコンテンツはGo Wikiの一部です。