DELETE SQL: Hapus Data dengan Aman (Selalu SELECT Dulu)
TL;DR
Perintah DELETE di SQL dipakai buat menghapus baris dari tabel, dengan sintaks DELETE FROM nama_tabel WHERE kondisi. Kalau klausa WHERE dilupakan, DELETE bakal menghapus SEMUA baris di tabel itu dan nggak bisa di-undo. Kebiasaan paling aman: tulis dulu SELECT * FROM tabel WHERE kondisi buat lihat baris mana yang bakal kena, baru ganti SELECT * jadi DELETE setelah hasilnya cocok, dan jalankan di dalam transaction (BEGIN ... ROLLBACK/COMMIT).
DELETE di SQL dipakai buat menghapus baris dari tabel. Sintaksnya DELETE FROM nama_tabel WHERE kondisi;
Lupa nulis WHERE? Seluruh isi tabel kehapus.
Nggak ada Ctrl+Z. Nggak ada tempat sampah. Kalau kamu udah COMMIT dan nggak punya backup, datanya beneran hilang.
Aku pernah lihat junior analyst ngehapus 40.000 baris transaksi gara-gara nge-highlight query-nya kurang satu baris waktu tekan Run. Semenit doang.
Jadi artikel ini bukan cuma soal sintaks DELETE. Ini soal kebiasaan yang bikin kamu nggak pernah ngalamin itu.
Apa itu perintah DELETE di SQL?
DELETE adalah perintah SQL buat menghapus satu atau lebih baris dari sebuah tabel. Sintaks dasarnya DELETE FROM nama_tabel WHERE kondisi; — klausa WHERE nentuin baris mana yang kena. Kalau WHERE nggak ditulis, DELETE bakal menghapus semua baris di tabel, tapi struktur tabelnya (kolom, index, constraint) tetap utuh.
-- Hapus satu baris spesifik
DELETE FROM transaksi
WHERE id = 42;
-- Hapus banyak baris berdasarkan kondisi
DELETE FROM transaksi
WHERE status = 'batal';
-- ⚠️ BAHAYA: ngosongin seluruh tabel
DELETE FROM transaksi;
Yang terakhir itu valid secara sintaks. Database nggak bakal nanya "yakin?" — dia langsung jalan.
Kenapa harus SELECT dulu sebelum DELETE?
Ini kebiasaan paling murah yang bisa nyelamatin kamu.
Alurnya: tulis query-nya sebagai SELECT dulu. Lihat hasilnya. Kalau barisnya persis yang kamu maksud, baru ganti SELECT * jadi DELETE.
-- Langkah 1: SELECT dulu
SELECT * FROM transaksi
WHERE status = 'batal';
-- Lihat hasilnya: 3 baris? Sesuai ekspektasi? Oke.
-- Langkah 2: ganti SELECT * jadi DELETE
DELETE FROM transaksi
WHERE status = 'batal';
Bagian WHERE-nya nggak berubah sama sekali. Kamu cuma ganti kata di depan.
Kenapa ini penting? Karena kesalahan DELETE hampir selalu ada di WHERE-nya, bukan di kata DELETE-nya.
WHERE yang kamu kira nyaring 3 baris ternyata nyaring 3.000. SELECT dulu bikin kamu lihat itu sebelum datanya hilang.
Trik tambahan: kalau kamu belum kuat sama logika WHERE, baca dulu panduan klausa WHERE. Di situ aku bahas jebakan NULL yang sering bikin filter meleset.
Gimana cara DELETE yang bisa dibatalkan?
Bungkus pakai transaction. Ini pengaman kedua kamu.
BEGIN;
DELETE FROM transaksi
WHERE tanggal < '2026-01-01';
-- Cek dulu: masuk akal nggak sisanya?
SELECT COUNT(*) FROM transaksi;
-- Kalau salah:
ROLLBACK;
-- Kalau benar:
COMMIT;
Antara BEGIN dan COMMIT, perubahan kamu belum permanen. ROLLBACK bakal balikin semuanya kayak semula.
Satu jebakan besar: banyak GUI SQL nyalain autocommit secara default. Artinya tiap query yang kamu Run langsung permanen, walaupun kamu ngetik BEGIN.
Cek setting autocommit di tool kamu sebelum ngerjain DELETE yang serius. Di DBeaver ada di toolbar atas. Di pgAdmin ada di menu Query.
PostgreSQL punya bonus yang enak: RETURNING.
DELETE FROM transaksi
WHERE status = 'batal'
RETURNING id, produk, total;
Dia ngasih tau persis baris apa yang barusan kehapus. Bukti buat kamu, bukan cuma angka "DELETE 3".
Gimana cara hapus data duplikat pakai DELETE?
Ini kasus paling sering di kerjaan analis. Data dari POS masuk dobel gara-gara sync error.
Langkah 1: cari duplikatnya dulu.
SELECT pelanggan_id, tanggal, produk, COUNT(*) AS jumlah
FROM transaksi
GROUP BY pelanggan_id, tanggal, produk
HAVING COUNT(*) > 1;
Langkah 2: hapus duplikatnya, sisakan yang id-nya paling kecil.
DELETE FROM transaksi t
WHERE t.id > (
SELECT MIN(t2.id)
FROM transaksi t2
WHERE t2.pelanggan_id = t.pelanggan_id
AND t2.tanggal = t.tanggal
AND t2.produk = t.produk
);
Logikanya: buat tiap grup duplikat, cari id terkecil. Hapus semua yang id-nya lebih besar dari itu.
Yang bikin ini aman: baris pertama dari tiap grup selalu selamat. Kamu nggak akan kehilangan datanya, cuma salinannya.
Jalanin di dalam BEGIN dulu, ya. Percaya sama aku.
Contoh kasus: bersihin transaksi batal di toko_berkah
Dataset toko_berkah — data distributor sembako. Tabel transaksi punya 8.400 baris.
Sebelum bersih-bersih, aku hitung dulu apa yang mau dihapus:
SELECT status, COUNT(*) AS jumlah, SUM(total) AS nilai
FROM transaksi
GROUP BY status;
| Status | Jumlah baris | Nilai (Rp) |
|---|---|---|
| selesai | 7.611 | 4.820.000.000 |
| batal | 642 | 318.000.000 |
| pending | 147 | 91.000.000 |
Nah, ini yang bikin aku nggak jadi DELETE.
642 transaksi batal itu bernilai Rp318 juta. Kalau dihapus, tim finance kehilangan kemampuan buat jawab "kenapa Desember banyak yang batal?"
Angka rata-rata transaksi batal Rp495 ribu, sementara transaksi selesai Rp633 ribu. Transaksi batal cenderung yang kecil-kecil — itu insight yang cuma ada kalau datanya masih di situ.
Jadi aku pakai soft delete. Data batal ditandai, nggak dibuang.
Ini pelajaran yang lebih penting dari sintaks DELETE-nya: tanya dulu "siapa yang bakal nyariin data ini enam bulan lagi" sebelum kamu tekan Run.
Kapan pakai soft delete, kapan hard delete?
Soft delete = kamu nggak hapus barisnya, cuma nandain.
-- Tambah kolom penanda
ALTER TABLE pelanggan ADD COLUMN deleted_at TIMESTAMP NULL;
-- "Hapus" pelanggan id 5
UPDATE pelanggan
SET deleted_at = NOW()
WHERE id = 5;
-- Semua query berikutnya harus nyaring
SELECT * FROM pelanggan
WHERE deleted_at IS NULL;
| Situasi | Pilih |
|---|---|
| Data transaksi, pelanggan, order | Soft delete |
| Data yang kepake buat audit atau laporan historis | Soft delete |
| User bisa "undo" hapus | Soft delete |
| Data staging / tabel sementara | Hard delete |
| Log lama yang udah kadaluwarsa | Hard delete |
| Data pribadi yang harus dihapus permanen (GDPR/UU PDP) | Hard delete |
Harga soft delete: semua query kamu harus nambah WHERE deleted_at IS NULL. Lupa sekali, laporan kamu ngitung data yang harusnya udah nggak ada.
Solusinya bikin VIEW yang udah ada filternya, terus semua orang query dari VIEW itu.
Kesalahan umum waktu pakai DELETE
1. Nge-highlight query nggak lengkap
Ini penyebab bencana nomor satu.
Kamu punya query dua baris:
DELETE FROM transaksi
WHERE status = 'batal';
Kamu blok pakai mouse, tapi kelewat baris kedua. Tekan Ctrl+Enter. Yang jalan cuma DELETE FROM transaksi.
Tabel ludes.
Kebiasaan pencegah: tulis WHERE-nya duluan. Ketik WHERE status = 'batal' di baris pertama, baru naik ke atas dan tambahin DELETE FROM transaksi. Query yang belum lengkap bakal error, bukan jalan setengah.
2. Lupa DELETE nggak reset ID
Setelah kamu hapus semua baris pakai DELETE, insert berikutnya tetap lanjut dari id terakhir. Kalau id terakhir 8.400, baris baru dapat id 8.401.
Mau balik ke 1? Itu kerjaan TRUNCATE. Aku bandingin dua-duanya di TRUNCATE vs DELETE.
3. Kaget kena error foreign key
ERROR: update or delete on table "pelanggan" violates
foreign key constraint on table "transaksi"
Artinya ada baris di tabel transaksi yang masih nunjuk ke pelanggan itu. Database nolak biar transaksinya nggak jadi yatim.
Tiga jalan keluar:
- Hapus dulu transaksinya, baru pelanggannya
- Set relasinya jadi
ON DELETE CASCADE— hati-hati, ini bikin hapus 1 pelanggan otomatis hapus semua transaksinya - Pakai soft delete (paling aman)
Detail perilaku CASCADE ada di dokumentasi constraint PostgreSQL.
4. DELETE jutaan baris sekaligus
DELETE nulis log buat tiap baris. Hapus 10 juta baris sekaligus bisa bikin tabel kekunci berjam-jam dan disk penuh sama WAL.
Pecah jadi batch:
DELETE FROM log_lama
WHERE id IN (
SELECT id FROM log_lama
WHERE tanggal < '2024-01-01'
LIMIT 10000
);
-- ulangi sampai 0 baris kehapus
5. DELETE di production tanpa backup
Nggak ada trik yang bisa nyelametin ini. Sebelum DELETE besar di production, pastiin ada backup yang bisa di-restore.
Kalau kamu ragu apakah backup-nya jalan, artinya kamu nggak punya backup.
FAQ
Apa yang terjadi kalau DELETE tanpa WHERE?
Semua baris di tabel itu kehapus. DELETE FROM transaksi; ngosongin seluruh tabel, tanpa konfirmasi, tanpa undo. Kalau kamu belum jalan di dalam transaction dan nggak punya backup, datanya hilang beneran. Biasakan tulis WHERE-nya duluan, atau jalanin di dalam BEGIN supaya masih bisa ROLLBACK.
Gimana cara membatalkan DELETE di SQL?
Cuma bisa kalau DELETE dijalanin di dalam transaction dan belum COMMIT. Ketik BEGIN; sebelum DELETE, cek jumlah baris yang kehapus, terus ROLLBACK; kalau salah. Kalau udah ke-commit atau kamu pakai autocommit, satu-satunya jalan pulih adalah restore dari backup.
Apa bedanya DELETE, TRUNCATE, dan DROP?
DELETE menghapus baris dan bisa disaring pakai WHERE. TRUNCATE menghapus semua baris sekaligus, jauh lebih cepat, tapi nggak bisa pakai WHERE. DROP menghapus tabelnya sama sekali — baris, kolom, index, semuanya lenyap. Urutan risikonya: DELETE paling aman, DROP paling ganas.
Kenapa DELETE gagal dengan error foreign key?
Karena ada tabel lain yang masih nunjuk ke baris yang mau kamu hapus. Misal kamu hapus pelanggan, tapi tabel transaksi masih nyimpen customer_id-nya. Database nolak biar datanya nggak jadi yatim. Solusinya: hapus dulu baris anaknya, pakai ON DELETE CASCADE, atau pilih soft delete.
Kapan sebaiknya pakai soft delete?
Kalau datanya masih mungkin dibutuhin buat audit, laporan historis, atau bisa dibatalin user. Tambah kolom deleted_at, terus UPDATE kolom itu alih-alih DELETE. Konsekuensinya semua query harus nambah filter WHERE deleted_at IS NULL. Buat data transaksi dan pelanggan, soft delete hampir selalu pilihan yang lebih waras.
Penutup
Tiga kebiasaan yang bikin kamu nggak pernah kehilangan data:
- SELECT dulu. Lihat barisnya, baru ganti jadi DELETE.
- BEGIN dulu. Cek hasilnya, baru COMMIT.
- Tanya dulu: "ada yang bakal nyariin data ini enam bulan lagi?" Kalau iya, pakai soft delete.
Copy DDL dan query di artikel ini ke database latihan kamu, terus coba sendiri — termasuk yang ROLLBACK. Ngerasain sendiri gimana ROLLBACK nyelametin kamu itu jauh lebih nempel daripada baca.
Lanjut ke TRUNCATE vs DELETE buat tau kapan mending pakai yang lebih cepat.
Mau praktek langsung? Mulai latihan SQL gratis
Latihan interaktif, langsung di browser.
Artikel Terkait
50 Latihan Soal SQL Pemula + Kunci Jawaban
50 latihan soal SQL dari SELECT sampai window function, pakai dataset toko_berkah. Kunci jawabannya ada semua, tapi coba dulu sendiri sebelum ngintip.
SQLite untuk Pemula: Database Ringan buat Latihan Query
SQLite itu database yang isinya cuma satu file, gak perlu install server. Ini cara bikin database toko_berkah sendiri dan latihan query dari nol.
BigQuery vs PostgreSQL: Beda Arsitektur, Beda Cara Pakai
BigQuery dan PostgreSQL sama-sama pakai SQL, tapi dibangun buat pekerjaan yang beda. Ini perbandingan arsitektur, biaya, dan kebiasaan nulis query yang harus kamu ubah waktu pindah.