Prepare

プリペアードステートメントを作成して、クエリを効率的に再利用できます。

Exec Statements

結果を返さないクエリ(INSERT、UPDATE、DELETE など)のプリペアードステートメントを作成します。

ctx := context.Background()
db, err := bob.Open("postgres", "...")
if err != nil {
    // ...
}

q := psql.Update(...)
stmt, err := bob.Prepare(ctx, db, q)
if err != nil {
    // ...
}
defer stmt.Close()

// ステートメントを実行
result, err := stmt.Exec(ctx, args...)
if err != nil {
    // ...
}

使用例

// UPDATE ステートメントの準備
updateQuery := psql.Update("users").
    Set("name", psql.Arg("")).
    Set("updated_at", psql.Arg(time.Time{})).
    Where(psql.Quote("id").EQ(psql.Arg(0)))

updateStmt, err := bob.Prepare(ctx, db, updateQuery)
if err != nil {
    return err
}
defer updateStmt.Close()

// 複数のユーザーを更新
for _, user := range users {
    result, err := updateStmt.Exec(ctx, user.Name, time.Now(), user.ID)
    if err != nil {
        return err
    }
    
    rowsAffected, _ := result.RowsAffected()
    fmt.Printf("Updated %d rows for user %d\n", rowsAffected, user.ID)
}

Query Statements

行を返すクエリのプリペアードステートメントを作成します。bob.PrepareQuery() または bob.PrepareQueryx() を使用します。

q := psql.Select(...)
stmt, err := bob.PrepareQuery(ctx, db, q, scan.StructMapper[userObj]())
if err != nil {
    // ...
}
defer stmt.Close()

// ステートメントを実行
users, err := stmt.All(ctx, args...)
if err != nil {
    // ...
}

使用例

type User struct {
    ID    int    `db:"id"`
    Name  string `db:"name"`
    Email string `db:"email"`
}

// SELECT ステートメントの準備
selectQuery := psql.Select("id", "name", "email").
    From("users").
    Where(psql.Quote("active").EQ(psql.Arg(true))).
    Where(psql.Quote("created_at").GTE(psql.Arg(time.Time{}))).
    OrderBy(psql.Quote("id").Asc())

selectStmt, err := bob.PrepareQuery(ctx, db, selectQuery, scan.StructMapper[User]())
if err != nil {
    return err
}
defer selectStmt.Close()

// 異なる日付でクエリを実行
dates := []time.Time{
    time.Now().AddDate(0, -1, 0), // 1ヶ月前
    time.Now().AddDate(0, -3, 0), // 3ヶ月前
    time.Now().AddDate(0, -6, 0), // 6ヶ月前
}

for _, date := range dates {
    users, err := selectStmt.All(ctx, date)
    if err != nil {
        return err
    }
    
    fmt.Printf("Users since %s: %d\n", date.Format("2006-01-02"), len(users))
}

単一結果の取得

// 単一ユーザーを取得するステートメント
getUserQuery := psql.Select("id", "name", "email").
    From("users").
    Where(psql.Quote("id").EQ(psql.Arg(0)))

getUserStmt, err := bob.PrepareQuery(ctx, db, getUserQuery, scan.StructMapper[User]())
if err != nil {
    return err
}
defer getUserStmt.Close()

// 異なるIDでユーザーを取得
userIDs := []int{1, 2, 3, 4, 5}

for _, userID := range userIDs {
    user, err := getUserStmt.One(ctx, userID)
    if err != nil {
        if errors.Is(err, sql.ErrNoRows) {
            fmt.Printf("User %d not found\n", userID)
            continue
        }
        return err
    }
    
    fmt.Printf("User %d: %s\n", user.ID, user.Name)
}

利用可能なメソッド

Exec Statements

  • Exec(ctx, args...) - ステートメントを実行

Query Statements

  • One(ctx, args...) - 単一の結果を取得
  • All(ctx, args...) - 全ての結果を取得
  • Cursor(ctx, args...) - カーソルを取得

注意点

  • リソース管理: defer stmt.Close() を必ず使用する
  • パフォーマンス: 同じクエリを複数回実行する場合に最適
  • メモリ効率: プリペアードステートメントはメモリに保存される
  • データベース接続: 同じデータベース接続で使用する必要がある

プリペアードステートメントは、同じクエリを異なるパラメータで繰り返し実行する場合に、パフォーマンスを大幅に向上させます。