Pada artikel ini, kita akan membahas secara lengkap perbedaan antara Query
, Exec
, dan Prepare
pada penggunaan package database/sql
di bahasa pemrograman Go. Pengetahuan ini sangat penting untuk menulis kode yang efisien, aman, dan mudah dipelihara ketika berinteraksi dengan database.
Pendahuluan
Go menyediakan package built-in database/sql
untuk mengelola koneksi dan operasi database secara umum. Namun, untuk driver spesifik seperti MySQL, kita tetap perlu mengimpor driver pihak ketiga, misalnya github.com/go-sql-driver/mysql
.
Query
Fungsi dengan prefix Query
digunakan untuk mengeksekusi perintah SQL yang mengembalikan data, seperti SELECT
. Ada beberapa varian fungsi ini:
Query(query string, args ...any) (*Rows, error)
QueryContext(ctx context.Context, query string, args ...any) (*Rows, error)
QueryRow(query string, args ...any) *Row
QueryRowContext(ctx context.Context, query string, args ...any) *Row
Query()
Digunakan untuk mengambil banyak baris data (multiple rows). Biasanya dipakai untuk operasi SELECT
yang mengembalikan lebih dari satu record.
rows, err := db.Query("SELECT id, name, balance FROM customers")
Contoh penggunaan:
for rows.Next() {
var id, name string
var balance int
err = rows.Scan(&id, &name, &balance)
if err != nil {
panic(err)
}
fmt.Println("ID:", id, "Name:", name, "Balance:", balance)
}
QueryContext()
Sama seperti Query()
, namun menerima parameter context untuk mengatur timeout, deadline, atau pembatalan operasi.
rows, err := db.QueryContext(context.Background(), "SELECT id, name, balance FROM customers")
QueryRow()
Digunakan untuk mengambil satu baris data saja (single row), misal berdasarkan ID unik.
row := db.QueryRow("SELECT id, name, balance FROM customers WHERE id = ?", 1)
var id, name string
var balance int
err := row.Scan(&id, &name, &balance)
if err != nil {
panic(err)
}
fmt.Println("ID:", id, "Name:", name, "Balance:", balance)
QueryRowContext()
Sama seperti QueryRow()
, namun menerima parameter context.
row := db.QueryRowContext(context.Background(), "SELECT id, name, balance FROM customers WHERE id = ?", 2)
var id, name string
var balance int
err := row.Scan(&id, &name, &balance)
Catatan:
Pada contoh sebelumnya, penggunaanrows, err := db.QueryRow(...)
adalah keliru. FungsiQueryRow
mengembalikan objek*Row
, bukan*Rows
, dan tidak memiliki methodNext()
. Data langsung di-scan menggunakanScan()
.
Exec
Fungsi dengan prefix Exec
digunakan untuk mengeksekusi perintah SQL yang tidak mengembalikan baris data, seperti INSERT
, UPDATE
, atau DELETE
.
Exec(query string, args ...any) (Result, error)
ExecContext(ctx context.Context, query string, args ...any) (Result, error)
Exec()
result, err := db.Exec("UPDATE customers SET balance = ? WHERE id = ?", 5000000, 2)
if err != nil {
panic(err)
}
rowsAffected, err := result.RowsAffected()
if err != nil {
panic(err)
}
fmt.Println("Rows affected:", rowsAffected)
ExecContext()
Sama seperti Exec()
, namun menerima parameter context.
result, err := db.ExecContext(context.Background(), "DELETE FROM customers WHERE id = ?", 3)
rowsAffected, err := result.RowsAffected()
Catatan:
Pada contoh sebelumnya, penggunaanresult.RowAffected()
adalah keliru. Method yang benar adalahRowsAffected()
.
Prepare
Fungsi dengan prefix Prepare
digunakan untuk membuat prepared statement, yaitu query yang dapat dieksekusi berulang kali dengan parameter berbeda tanpa harus parsing ulang query oleh database. Ini sangat efisien untuk operasi batch.
Prepare(query string) (*Stmt, error)
PrepareContext(ctx context.Context, query string) (*Stmt, error)
Prepare()
stmt, err := db.Prepare("INSERT INTO customers(name, balance) VALUES(?, ?)")
if err != nil {
panic(err)
}
defer stmt.Close()
for i := 1; i <= 10; i++ {
name := fmt.Sprintf("Nama Seseorang ke-%d", i)
balance := 100000 + i
_, err := stmt.Exec(name, balance)
if err != nil {
panic(err)
}
}
PrepareContext()
Sama seperti Prepare()
, namun menerima parameter context.
stmt, err := db.PrepareContext(context.Background(), "INSERT INTO customers(name, balance) VALUES(?, ?)")
defer stmt.Close()
for i := 1; i <= 10; i++ {
name := fmt.Sprintf("Nama Seseorang ke-%d", i)
balance := 100000 + i
_, err := stmt.ExecContext(context.Background(), name, balance)
if err != nil {
panic(err)
}
}
Catatan:
Pada contoh sebelumnya, parameterid
padaINSERT
sebaiknya dihilangkan jika sudah auto-increment di database.
Kesimpulan
- Gunakan
Query
/QueryContext
untuk mengambil banyak data. - Gunakan
QueryRow
/QueryRowContext
untuk mengambil satu baris data. - Gunakan
Exec
/ExecContext
untuk operasi yang tidak mengembalikan data (INSERT, UPDATE, DELETE). - Gunakan
Prepare
/PrepareContext
untuk operasi berulang dengan query yang sama agar lebih efisien.