Go SQL チートシート


# Go SQL チートシート

Goの標準ライブラリ「`database/sql`」を使ったSQLの基本操作をまとめたチートシートです。簡単な説明とともにコードサンプルを紹介します。

## 1. インポート & ドライバ設定

```go
import (
    "database/sql"
    _ "github.com/go-sql-driver/mysql"  // 使用するデータベースドライバ（MySQLの場合）をインポート
    // _ "github.com/lib/pq"  // PostgreSQLの場合はこのように別のドライバをインポート
)
```
- `database/sql`: Go標準のSQLデータベースインターフェース。
- `_` は、ドライバパッケージを明示的に使用しないが、初期化は行う必要があるために使う。

## 2. データベースに接続

```go
db, err := sql.Open("mysql", "user:password@tcp(127.0.0.1:3306)/dbname")
if err != nil {
    log.Fatal(err)  // エラー発生時にプログラムを終了させる
}
defer db.Close()    // 関数終了時に接続を閉じる
```
- `sql.Open`: データベースへの接続を開始。接続自体はまだ確立されていない。
- `defer db.Close()`: 関数が終了する際にデータベース接続を閉じるための予約。

## 3. データベースにPingを送る

```go
err = db.Ping()
if err != nil {
    log.Fatal(err)  // 接続が確立されていない場合、エラーメッセージを表示して終了
}
```
- `Ping()`: 実際にデータベースに接続して通信ができるか確認する。

## 4. クエリ実行（SELECT）

### 4.1 単一行を取得 (`QueryRow`)

```go
var name string
err := db.QueryRow("SELECT name FROM users WHERE id = ?", 1).Scan(&name)
if err != nil {
    log.Fatal(err)  // エラー発生時にエラーメッセージを表示
}
fmt.Println(name)  // 取得した名前を表示
```
- `QueryRow`: 結果が1行のクエリを実行する際に使用。
- `Scan`: データベースから取得した値をGoの変数にマッピング。

### 4.2 複数行を取得 (`Query`)

```go
rows, err := db.Query("SELECT id, name FROM users")
if err != nil {
    log.Fatal(err)  // クエリ実行時にエラーがあれば終了
}
defer rows.Close()  // `rows`は使用後に必ず閉じる

for rows.Next() {  // 次の行が存在する限りループを実行
    var id int
    var name string
    err := rows.Scan(&id, &name)  // カラムの値を変数に格納
    if err != nil {
        log.Fatal(err)  // エラーが発生した場合終了
    }
    fmt.Printf("ID: %d, Name: %s\n", id, name)  // 取得したデータを表示
}

if err := rows.Err(); err != nil {  // クエリ中のエラーチェック
    log.Fatal(err)
}
```
- `Query`: 複数行を取得する際に使用。
- `rows.Next()`: 次の行が存在するか確認してループを継続。
- `Scan`: 各行のカラムを変数にマッピング。

## 5. データ挿入 (INSERT)

```go
result, err := db.Exec("INSERT INTO users (name, age) VALUES (?, ?)", "Alice", 30)
if err != nil {
    log.Fatal(err)  // 挿入時にエラーがあれば終了
}

lastInsertID, err := result.LastInsertId()  // 最後に挿入された行のIDを取得
if err != nil {
    log.Fatal(err)  // エラーがあれば終了
}
fmt.Println("Last Insert ID:", lastInsertID)  // 挿入したレコードのIDを表示
```
- `Exec`: データベースの変更系操作（INSERT, UPDATE, DELETE）に使用。
- `LastInsertId`: 挿入した行のIDを取得する。

## 6. データ更新 (UPDATE)

```go
result, err := db.Exec("UPDATE users SET age = ? WHERE id = ?", 31, 1)
if err != nil {
    log.Fatal(err)  // 更新時にエラーがあれば終了
}

rowsAffected, err := result.RowsAffected()  // 更新された行数を取得
if err != nil {
    log.Fatal(err)  // エラーがあれば終了
}
fmt.Println("Rows Affected:", rowsAffected)  // 更新された行数を表示
```
- `RowsAffected`: 更新された行数を取得できる。

## 7. データ削除 (DELETE)

```go
result, err := db.Exec("DELETE FROM users WHERE id = ?", 1)
if err != nil {
    log.Fatal(err)  // 削除時にエラーがあれば終了
}

rowsAffected, err := result.RowsAffected()  // 削除された行数を取得
if err != nil {
    log.Fatal(err)  // エラーがあれば終了
}
fmt.Println("Rows Affected:", rowsAffected)  // 削除された行数を表示
```
- DELETEクエリも`Exec`で実行し、影響を受けた行数を確認。

## 8. トランザクション処理

```go
tx, err := db.Begin()  // トランザクションの開始
if err != nil {
    log.Fatal(err)  // エラーがあれば終了
}

_, err = tx.Exec("UPDATE accounts SET balance = balance - 100 WHERE id = ?", 1)  // 口座1から100を減額
if err != nil {
    tx.Rollback()  // エラー発生時はトランザクションをロールバック
    log.Fatal(err)
}

_, err = tx.Exec("UPDATE accounts SET balance = balance + 100 WHERE id = ?", 2)  // 口座2に100を加算
if err != nil {
    tx.Rollback()  // エラー発生時はトランザクションをロールバック
    log.Fatal(err)
}

err = tx.Commit()  // 成功したらトランザクションを確定
if err != nil {
    log.Fatal(err)  // コミット時にエラーがあれば終了
}
```
- `Begin`: トランザクションを開始。
- `Rollback`: エラー時にロールバック。
- `Commit`: トランザクションが正常に完了したらコミットして確定。

## 9. プリペアドステートメント (Prepared Statements)

```go
stmt, err := db.Prepare("INSERT INTO users (name, age) VALUES (?, ?)")
if err != nil {
    log.Fatal(err)  // 準備時にエラーがあれば終了
}
defer stmt.Close()  // 使用後は必ずクローズ

_, err = stmt.Exec("Bob", 25)  // 準備したステートメントでクエリを実行
if err != nil {
    log.Fatal(err)  // 実行時にエラーがあれば終了
}

_, err = stmt.Exec("Charlie", 29)  // 別のデータを実行
if err != nil {
    log.Fatal(err)  // 実行時にエラーがあれば終了
}
```
- `Prepare`: 複数回同じクエリを実行する場合に有効。パフォーマンスが向上。
- `Exec`: 準備されたステートメントを実行。

## 10. Null値の処理

```go
var age sql.NullInt64
err := db.QueryRow("SELECT age FROM users WHERE id = ?", 1).Scan(&age)
if err != nil {
    log.Fatal(err)  // クエリ実行時にエラーがあれば終了
}

if age.Valid {
    fmt.Println("Age:", age.Int64)  // NULLでない場合、値を表示
} else {
    fmt.Println("Age is NULL")  // NULLの場合は「NULL」と表示
}
```
- `sql.NullInt64`や`sql.NullString`などを使って、データベースのNULL値を扱うことができる。
- `Valid`フィールドで、値がNULLかどうかを確認する。