SQLの実行
Go標準ライブラリのdatabase/sql
パッケージを利用して、SQLを発行する方法を説明します。
SQLの実行方法(SELECT以外)
DB.Exec
メソッドを使って、SQLを実行します。
// DDL文実行
ddl := `
CREATE TABLE IF NOT EXISTS tasks (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
status TEXT NOT NULL,
created_at TEXT DEFAULT CURRENT_TIMESTAMP,
updated_at TEXT DEFAULT CURRENT_TIMESTAMP
);
`
if _, err := db.Exec(ddl); err != nil {
log.Fatal(err)
}
プレースホルダを利用する場合は、第二引数以降に値を設定します。
プレースホルダは、SQL文に動的に値を埋め込むものです。
SQLiteの場合は?
と記述した部分に値が埋め込まれます。
// INSERT文実行(プレースホルダを利用)
sqlIns := `INSERT INTO tasks(name, status) VALUES (?, ?);`
r, err := db.Exec(sqlIns, "task name", "open")
if err != nil {
log.Fatal(err)
}
ID発番結果の取得
INSERT文でIDの自動発番をした場合、Result.LastInsertId
メソッドを使って、IDの発番結果を取得できます。
// INSERT文実行
sqlIns := `INSERT INTO tasks(name, status) VALUES (?, ?);`
r, err := db.Exec(sqlIns, "task name", "open")
if err != nil {
log.Fatal(err)
}
id, err := r.LastInsertId()
if err != nil {
log.Fatal(err)
}
log.Println(id)
操作行数の取得
Result.RowsAffected
メソッドを使って、操作した行数を取得できます。
// UPDATE文実行
sqlUpd := `
UPDATE tasks
SET name = ?, status = ?, updated_at = CURRENT_TIMESTAMP
WHERE id = ?
;`
r, err = db.Exec(sqlUpd, "task name a", "doing", id)
if err != nil {
log.Fatal(err)
}
n, err := r.RowsAffected()
if err != nil {
log.Fatal(err)
}
log.Println("n:", n)
SQLの実行方法(NレコードのSELECT)
クエリ対象のレコード数が決まっていない場合はDB.Query
メソッドを使います。
クエリ結果は、次の通り取得します。
Rows.Next
メソッドで取得できるレコードがあることを確認Rows.Scan
メソッドでレコードを取得
// クエリの実行
sqlSel := `SELECT * FROM tasks;`
rows, err := db.Query(sqlSel)
if err != nil {
log.Fatal(err)
}
defer rows.Close()
// 結果取得
for rows.Next() {
var id int
var name string
var status string
var createdAt string
var updatedAt string
err := rows.Scan(&id, &name, &status, &createdAt, &updatedAt)
if err != nil {
log.Fatal(err)
}
log.Println(id, name, status, createdAt, updatedAt)
}
SQLの実行方法(1レコードのSELECT)
クエリ対象が1レコードの場合はDB.QueryRow
メソッドを使います。
クエリ結果はRow.Scan
メソッドを使って取得します。
レコードが選択されなかった場合は、ErrNoRows
が返却されます。
レコードが複数選択された場合は、1レコード目のみスキャンされ、他のレコードは破棄されます。
// クエリの実行
sqlSelRow := `SELECT * FROM tasks WHERE id = ?;`
row := db.QueryRow(sqlSelRow, 1)
// 結果取得
var id int
var name string
var status string
var createdAt string
var updatedAt string
err := row.Scan(&id, &name, &status, &createdAt, &updatedAt)
if err != nil {
if err == sql.ErrNoRows {
log.Fatal("no rows")
}
log.Fatal(err)
}
log.Println(id, name, status, createdAt, updatedAt)
NULLの扱い
NULLを許容する列をスキャンするには次の方法があります。
- 受け取る変数の型をポインタにする
database/sql
パッケージが用意したNULL許容型を利用する
database/sql
パッケージが用意したNULL許容型は次の通りです。
sql.NullBool
sql.NullFloat64
sql.NullInt32
sql.NullInt64
sql.NullString
sql.NullTime
// NULLを許容するテーブルを作成
ddl := `CREATE TABLE IF NOT EXISTS nullables (col TEXT);`
if _, err := db.Exec(ddl); err != nil {
log.Fatal(err)
}
// NULLをINSERT
sqlIns := `INSERT INTO nullables(col) VALUES (?);`
if _, err := db.Exec(sqlIns, nil); err != nil {
log.Fatal(err)
}
// ポインタ型で受け取る
sqlSel := `SELECT * FROM nullables;`
var strPtr *string
if err := db.QueryRow(sqlSel).Scan(&strPtr); err != nil {
log.Fatal(err)
}
log.Println(strPtr)
// 出力結果:
// nil
// database/sqlのnull許容型で受け取る
var nullStr sql.NullString
if err := db.QueryRow(sqlSel).Scan(&nullStr); err != nil {
log.Fatal(err)
}
log.Println("Valid:", nullStr.Valid, "String:", nullStr.String)
// 出力結果:
// Valid: false String: