Prepared Statement PHP Error: Penyebab dan Solusi

Prepared statement adalah salah satu fondasi penting saat bekerja dengan PDO, karena membuat query lebih aman dari SQL injection dan lebih rapi saat menerima input user. Tetapi justru karena ada placeholder, binding, dan array parameter, error yang muncul bisa terasa membingungkan.

Gejala yang paling sering adalah query tidak jalan, data tidak tersimpan, atau muncul pesan seperti SQLSTATE[HY093] dan Invalid parameter number.

Kapan Error Ini Biasanya Muncul?

  • Query INSERT atau UPDATE gagal walaupun sintaks SQL terlihat benar
  • Muncul error Invalid parameter number
  • Placeholder :nama atau ? terasa tidak cocok dengan data yang dikirim
  • LIMIT dan OFFSET selalu error saat dipakai di prepared statement
  • Query aman di phpMyAdmin, tetapi gagal saat dipindahkan ke PDO

Penyebab Paling Umum

1. Jumlah Placeholder dan Parameter Tidak Sama

<?php
$stmt = $pdo->prepare("INSERT INTO users (nama, email) VALUES (?, ?)");
$stmt->execute(['Budi']);

Query punya dua placeholder, tetapi data yang dikirim hanya satu.

2. Nama Placeholder Salah Ketik

<?php
$stmt = $pdo->prepare("
    INSERT INTO users (nama, email)
    VALUES (:nama, :email)
");

$stmt->execute([
    'nama' => 'Budi',
    'emial' => '[email protected]',
]);

Placeholder :email tidak pernah mendapat nilai karena key array salah ketik.

3. Mencampur Placeholder Posisional dan Named Placeholder

Jangan gabungkan ? dan :nama dalam query yang sama.

4. LIMIT dan OFFSET Tidak Dikirim sebagai Integer

Beberapa driver MySQL sensitif saat LIMIT dan OFFSET dikirim sebagai string.

5. Error Sebenarnya Ada di SQL, Bukan di Binding

Kadang kita fokus ke prepared statement, padahal sumber masalahnya adalah:

  • nama tabel salah
  • kolom tidak ada
  • keyword SQL salah tulis

6. Mode Error PDO Belum Diatur ke Exception

Kalau PDO::ATTR_ERRMODE tidak diatur, error bisa terasa "diam" dan lebih susah ditelusuri.

Langkah Diagnosis

  1. Pastikan koneksi PDO memakai PDO::ERRMODE_EXCEPTION.
  2. Baca pesan error lengkap. Perhatikan kode SQLSTATE.
  3. Hitung jumlah placeholder di query dan bandingkan dengan jumlah parameter.
  4. Untuk named placeholder, cek ejaan nama key satu per satu.
  5. Untuk LIMIT dan OFFSET, gunakan bindValue(..., PDO::PARAM_INT).
Baca

SQLSTATE Dulu Kalau muncul HY093, biasanya masalahnya ada di jumlah atau nama parameter. Kalau muncul 42S22 atau 42000, besar kemungkinan masalahnya ada di SQL atau nama kolom.

Langkah Fix

1. Samakan Jumlah Placeholder dan Data

<?php
$stmt = $pdo->prepare("INSERT INTO users (nama, email) VALUES (?, ?)");
$stmt->execute(['Budi', '[email protected]']);

2. Gunakan Named Placeholder dengan Konsisten

<?php
$stmt = $pdo->prepare("
    INSERT INTO users (nama, email)
    VALUES (:nama, :email)
");

$stmt->execute([
    'nama' => 'Budi',
    'email' => '[email protected]',
]);

3. Pakai bindValue() untuk Angka

<?php
$stmt = $pdo->prepare("
    SELECT *
    FROM users
    ORDER BY id DESC
    LIMIT :limit OFFSET :offset
");

$stmt->bindValue(':limit', 10, PDO::PARAM_INT);
$stmt->bindValue(':offset', 0, PDO::PARAM_INT);
$stmt->execute();

4. Aktifkan Error Mode yang Jelas

<?php
$pdo = new PDO($dsn, $username, $password, [
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
]);

5. Bungkus Query di try-catch

<?php
try {
    $stmt = $pdo->prepare("
        UPDATE users
        SET nama = :nama
        WHERE id = :id
    ");

    $stmt->execute([
        'nama' => 'Siti',
        'id' => 3,
    ]);
} catch (PDOException $e) {
    echo $e->getMessage();
}

Contoh Sebelum dan Sesudah

Sebelum

<?php
$stmt = $pdo->prepare("
    INSERT INTO buku_tamu (nama, email, pesan)
    VALUES (:nama, :email, :pesan)
");

$stmt->execute([
    'nama' => $_POST['nama'],
    'email' => $_POST['email'],
]);

Masalahnya: placeholder :pesan tidak pernah diisi.

Sesudah

<?php
$stmt = $pdo->prepare("
    INSERT INTO buku_tamu (nama, email, pesan)
    VALUES (:nama, :email, :pesan)
");

$stmt->execute([
    'nama' => trim($_POST['nama'] ?? ''),
    'email' => trim($_POST['email'] ?? ''),
    'pesan' => trim($_POST['pesan'] ?? ''),
]);

Error Umum

SQLSTATE[HY093]: Invalid parameter number

Penyebab paling umum:

  • jumlah parameter tidak cocok
  • placeholder salah ketik
  • placeholder posisional dan named dicampur

SQLSTATE[42S22]: Column not found

Query berhasil diparsing, tetapi nama kolom salah.

SQLSTATE[42000]: Syntax error or access violation

Biasanya ada masalah di sintaks SQL, termasuk penggunaan LIMIT atau nama tabel.

Pencegahan

  1. Pilih satu gaya placeholder: posisional atau named.
  2. Untuk query panjang, named placeholder biasanya lebih mudah dibaca.
  3. Selalu aktifkan ERRMODE_EXCEPTION.
  4. Pakai bindValue(..., PDO::PARAM_INT) untuk LIMIT, OFFSET, dan angka sensitif lain.
  5. Validasi dan normalisasi input sebelum dilempar ke query.

Bacaan Terkait

FAQ

Lebih baik pakai ? atau :nama?

Untuk query pendek, keduanya sama-sama valid. Untuk query panjang atau banyak kolom, named placeholder biasanya lebih mudah dirawat.

Kenapa query saya jalan di phpMyAdmin tetapi gagal di PDO?

Karena di PDO ada layer binding parameter. Jadi selain SQL-nya benar, jumlah dan tipe parameternya juga harus cocok.

Apakah prepared statement otomatis mencegah semua bug SQL?

Tidak. Prepared statement membantu keamanan input, tetapi typo nama tabel, kolom, atau logika query tetap bisa salah.