DELETE Menghapus Baris, Tidak Lebih
DELETE mengeluarkan baris dari sebuah tabel. Ia tidak membuang tabel, tidak mengubah skemanya, dan tidak menyentuh tabel lain (kecuali kamu sudah mengatur cascade). Bentuknya singkat:
DELETE FROM users WHERE id = 2; mencari baris yang cocok dengan kondisi lalu menghapusnya. Dua baris lainnya tidak tersentuh. Tabelnya sendiri masih ada, jadi kamu masih bisa terus memasukkan data ke dalamnya.
Gambaran sederhananya: DELETE adalah SELECT yang membuang baris yang cocok, bukan mengembalikannya.
Klausa WHERE yang Melakukan Semua Pekerjaannya
Setiap DELETE yang serius bergantung sepenuhnya pada klausa WHERE-nya. Kalau benar, kamu menghapus apa yang kamu maksud. Kalau salah, kamu menghapus lebih banyak, kadang seluruh isi tabel.
Kedua draft yang belum dipublikasikan dan belum pernah dilihat sudah hilang. Baris yang sudah dipublikasikan tetap ada karena kondisinya tidak cocok. Kamu bisa memakai ekspresi apa pun yang diterima WHERE: IN, LIKE, BETWEEN, subquery, atau kombinasi AND/OR.
Kebiasaan yang layak dibangun: sebelum menjalankan DELETE, jalankan dulu klausa WHERE yang sama sebagai SELECT.
-- Preview what will go:
SELECT * FROM posts WHERE published = 0 AND views = 0;
-- Happy with the rows? Now delete them:
DELETE FROM posts WHERE published = 0 AND views = 0;
Dua langkah sederhana itu sudah menyelamatkan lebih banyak database daripada semua tool backup digabungkan.
DELETE Tanpa WHERE Mengosongkan Tabel
Kalau WHERE dihilangkan, DELETE menghapus setiap baris:
Tabelnya kosong, tapi masih ada. SQLite tidak punya pernyataan TRUNCATE: DELETE FROM table; adalah padanannya, dan SQLite menerapkan "truncate optimization" internal yang membuang semua page sekaligus, bukan menghapus baris satu per satu. Cepat, tapi tetap operasi transaksional yang bisa kamu rollback.
Kalau kamu memakai AUTOINCREMENT pada primary key, penghitungnya tidak diatur ulang dengan sendirinya. Untuk memulai id dari 1 lagi, hapus juga baris sequence-nya:
DELETE FROM log;
DELETE FROM sqlite_sequence WHERE name = 'log';
Untuk INTEGER PRIMARY KEY biasa (tanpa AUTOINCREMENT), SQLite sudah memakai ulang id dengan bebas, jadi langkah ini tidak diperlukan.
Menghapus Beberapa Baris Tertentu
IN adalah cara paling rapi untuk menghapus sekumpulan baris yang sudah diketahui:
Kamu juga bisa mengendalikan penghapusan dari subquery. Ini praktis kalau baris yang akan dihapus ditentukan oleh join atau tabel lain:
SQLite tidak mendukung sintaks DELETE ... JOIN seperti MySQL, tapi subquery di WHERE melakukan tugas yang sama.
RETURNING: Lihat Apa yang Kamu Hapus
Tambahkan RETURNING untuk mendapatkan kembali baris yang dihapus sebagai hasil query, seperti SELECT:
Kamu mendapatkan kembali id dan email dari setiap baris yang dihapus. Ini sangat berharga untuk:
- Mencatat persis apa yang dihapus.
- Membangun fitur undo (simpan baris yang dikembalikan di suatu tempat).
- Memastikan penghapusan mengenai baris yang kamu harapkan, dalam satu kali bolak-balik.
RETURNING bekerja pada INSERT, UPDATE, dan DELETE. Pembahasan lengkapnya ada di halaman tersendiri.
ON DELETE CASCADE untuk Baris Terkait
Saat tabel induk dan tabel anak dihubungkan oleh foreign key, menghapus baris induk meninggalkan baris anak yatim piatu, kecuali kamu menyuruh SQLite melakukan cascade:
Menghapus penulis juga menghapus buku-bukunya. Tanpa ON DELETE CASCADE, DELETE yang sama akan berhasil dan meninggalkan buku tanpa penulis (kalau foreign key nonaktif) atau gagal dengan error constraint (kalau aktif).
Jebakan besarnya: foreign key nonaktif secara bawaan di SQLite. Kamu harus menjalankan PRAGMA foreign_keys = ON; di setiap koneksi. Kalau pragma itu tidak diatur, ON DELETE CASCADE diabaikan diam-diam, dan buku-bukunya tetap ada. Sebagian besar driver aplikasi mengaturnya untukmu atau menyediakan opsinya; periksa driver yang kamu pakai.
Opsi cascade lain yang perlu diketahui: ON DELETE SET NULL (mengosongkan foreign key), ON DELETE RESTRICT (menolak penghapusan kalau masih ada baris anak), ON DELETE NO ACTION (bawaannya, dalam sebagian besar kasus sama dengan RESTRICT).
DELETE dengan LIMIT (Opsi Saat Kompilasi)
Beberapa build SQLite mendukung DELETE ... LIMIT, yang berguna untuk menghapus tabel yang sangat besar sedikit demi sedikit:
DELETE FROM logs
WHERE created_at < '2024-01-01'
ORDER BY created_at
LIMIT 1000;
Ini mengharuskan SQLite dikompilasi dengan SQLITE_ENABLE_UPDATE_DELETE_LIMIT. Binary resmi dan sebagian besar binding bahasa (sqlite3 di Python, better-sqlite3 di Node) sudah mengaktifkannya. Kalau milikmu tidak, kamu akan mendapat syntax error. Gunakan subquery sebagai gantinya:
DELETE FROM logs
WHERE id IN (
SELECT id FROM logs
WHERE created_at < '2024-01-01'
ORDER BY created_at
LIMIT 1000
);
Penghapusan bertahap menjaga transaksi tetap kecil, dan itu penting saat koneksi lain sedang membaca database.
Bungkus Penghapusan Besar dalam Transaksi
DELETE secara tersirat bersifat transaksional: semua baris yang cocok terhapus, atau tidak sama sekali. Tapi saat kamu akan menghapus banyak data, membungkusnya dalam transaksi eksplisit berarti kamu bisa melakukan ROLLBACK kalau ada yang terlihat janggal:
ROLLBACK membatalkan penghapusan sepenuhnya. Dalam sesi sungguhan, kamu akan melakukan COMMIT begitu jumlahnya terlihat benar. Transaksi juga jauh lebih cepat saat menghapus banyak baris dengan satu pernyataan per baris: membungkus perulangan dalam BEGIN/COMMIT menghindari satu fsync untuk setiap penghapusan.
Hal-Hal yang Tidak Menghapus
Beberapa kebingungan umum yang perlu disorot:
DELETE FROM table;mengosongkan tabel tapi tidak membuangnya. PakaiDROP TABLE table;untuk menghapus tabelnya sendiri.DELETEtidak mengecilkan file database. Page ditandai kosong untuk dipakai ulang. Untuk mengambil kembali ruang disk, jalankanVACUUM;(dibahas di bab performa).- Menghapus sebuah baris tidak menghapus baris anak di tabel lain, kecuali
ON DELETE CASCADEdiatur dan foreign key diaktifkan. DELETEyang tidak cocok dengan baris mana pun bukan error. Itu pernyataan yang berhasil denganchanges() = 0. Periksa jumlah barisnya kalau kamu perlu tahu.
Berikutnya: UPSERT
Sering kali kamu sebenarnya tidak ingin menghapus: kamu ingin memasukkan baris kalau masih baru, atau memperbaruinya kalau sudah ada. SQLite menyebutnya UPSERT, dan klausa ON CONFLICT menjadikannya satu pernyataan, bukan tiga. Itu yang berikutnya.
Pertanyaan yang sering diajukan
Bagaimana cara menghapus baris di SQLite?
Pakai DELETE FROM table_name WHERE condition;. Klausa WHERE memilih baris mana yang dihapus. Misalnya, DELETE FROM users WHERE id = 7; menghapus satu user dengan id 7. Tanpa WHERE, setiap baris di tabel akan dihapus.
Bagaimana cara menghapus semua baris di tabel SQLite?
Jalankan DELETE FROM table_name; tanpa klausa WHERE. SQLite tidak punya pernyataan TRUNCATE. DELETE tanpa filter adalah padanannya, dan SQLite mengoptimalkannya secara internal ('truncate optimization'). Untuk juga mengatur ulang penghitung AUTOINCREMENT, hapus baris dari sqlite_sequence setelahnya.
Bisakah SQLite menghapus secara berantai ke tabel terkait?
Bisa, kalau kamu mendeklarasikan ON DELETE CASCADE pada foreign key dan foreign key diaktifkan dengan PRAGMA foreign_keys = ON;. Foreign key nonaktif secara bawaan di SQLite, jadi pragma itu penting. Tanpanya, cascade diabaikan diam-diam.
Bagaimana cara melihat baris mana yang dihapus?
Tambahkan klausa RETURNING: DELETE FROM users WHERE active = 0 RETURNING id, email; mengembalikan baris yang dihapus seperti SELECT. Ini berguna untuk pencatatan, fitur undo, atau memastikan kamu menghapus persis apa yang kamu maksud.