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: