Menghapus duplikat di SQL berarti salah satu dari dua hal: menyembunyikan baris berulang dari hasil query dengan DISTINCT, atau menghapus salinan berlebih dari tabel sehingga tersisa satu dari masing-masing. Contoh-contohnya berfungsi di PostgreSQL, MySQL, SQLite, dan SQL Server.
Cara menghapus duplikat di SQL
SELECT DISTINCT mengembalikan setiap kombinasi kolom yang disebutkan satu kali saja dan tidak mengubah tabel. Untuk membersihkan tabelnya sendiri, hapus semua baris kecuali satu di setiap kelompok duplikat, biasanya yang id-nya paling kecil:
SELECT DISTINCT column_list FROM table_name;
DELETE FROM table_name
WHERE id NOT IN (
SELECT id FROM (
SELECT MIN(id) AS id FROM table_name GROUP BY column_list
) AS keepers
);
Di sini Maria Lopez dimasukkan tiga kali:
-- Input: customers
-- id | name | email | phone
-- ---+-------------+-------------------+---------
-- 1 | Maria Lopez | maria@example.com | 555-0142
-- 2 | Sam Patel | sam@example.com | 555-0178
-- 3 | Maria Lopez | maria@example.com | 555-0142
-- 4 | Lena Novak | lena@example.com | 555-0121
-- 5 | Maria Lopez | maria@example.com | 555-0142
SELECT DISTINCT name, email, phone
FROM customers
ORDER BY name;
-- Output:
-- name | email | phone
-- ------------+-------------------+---------
-- Lena Novak | lena@example.com | 555-0121
-- Maria Lopez | maria@example.com | 555-0142
-- Sam Patel | sam@example.com | 555-0178
DELETE FROM customers
WHERE id NOT IN (
SELECT id FROM (
SELECT MIN(id) AS id FROM customers GROUP BY name, email, phone
) AS keepers
);
-- Output (customers after the DELETE):
-- id | name | email | phone
-- ---+-------------+-------------------+---------
-- 1 | Maria Lopez | maria@example.com | 555-0142
-- 2 | Sam Patel | sam@example.com | 555-0178
-- 4 | Lena Novak | lena@example.com | 555-0121
Aturan yang perlu diketahui
-
Sebutkan kolom yang membuat dua baris dianggap duplikat. Jangan sertakan id dan kolom lain yang selalu berbeda, seperti timestamp
created_at. Baris yang berbeda di salah satu kolom yang disebutkan akan tetap disimpan semuanya. -
Pembungkus
SELECT id FROM (...) AS keepersdiperlukan untuk MySQL, yang kalau tidak akan menolakDELETEmembaca tabelnya sendiri di dalam subquery: “You can’t specify target table ‘customers’ for update in FROM clause”. -
Periksa dulu sebelum menghapus. Jalankan query
GROUP BYatauROW_NUMBER()terlebih dahulu untuk melihat apa yang akan terhapus, dan lakukan penghapusan di dalam transaksi yang bisa di-rollback. - Cegah duplikat baru dengan unique index setelah tabel bersih:
CREATE UNIQUE INDEX customers_email_key ON customers (email);
INSERT INTO customers VALUES (6, 'Maria Lopez', 'maria@example.com', '555-0142');
-- ERROR: duplicate key value violates unique constraint "customers_email_key"
Sintaks alternatif
Di kode SQL Server, Anda akan sering melihat salinan diberi nomor di dalam CTE lalu dihapus melalui CTE tersebut, yang hanya diizinkan oleh SQL Server. Pada tabel customers asli:
-- SQL Server
WITH numbered AS (
SELECT *, ROW_NUMBER() OVER (
PARTITION BY name, email, phone ORDER BY id
) AS row_num
FROM customers
)
DELETE FROM numbered WHERE row_num > 1;
-- Output (customers after the DELETE):
-- id | name | email | phone
-- ---+-------------+-------------------+---------
-- 1 | Maria Lopez | maria@example.com | 555-0142
-- 2 | Sam Patel | sam@example.com | 555-0178
-- 4 | Lena Novak | lena@example.com | 555-0121
Untuk menggabungkan dua query tanpa baris berulang, gunakan UNION, yang menghapus duplikat, bukan UNION ALL, yang mempertahankannya:
SELECT 'apple' AS fruit UNION ALL SELECT 'apple';
-- Output:
-- fruit
-- -----
-- apple
-- apple
SELECT 'apple' AS fruit UNION SELECT 'apple';
-- Output: apple
Kapan sebaiknya Anda menghapus duplikat?
Terkadang Anda hanya perlu menyembunyikan pengulangan di hasil query, dan terkadang tabelnya sendiri yang perlu dibersihkan. Berikut langkah-langkah yang biasa dilakukan, dari memeriksa hingga menghapus.
Tampilkan setiap nilai sekali
Untuk laporan, dropdown, atau sekadar mengecek isi sebuah kolom, SELECT DISTINCT membuang pengulangan dari hasil dan membiarkan tabel apa adanya.
Hitung salinan sebelum menghapus
Kelompokkan berdasarkan kolom yang menentukan duplikat dan pertahankan kelompok dengan HAVING COUNT(*) > 1. Cara ini menunjukkan apa yang terduplikasi dan berapa kali, sebelum ada yang dihapus. Panduan mencari duplikat di SQL membahas lebih banyak cara untuk memeriksanya.
Pilih salinan yang dipertahankan
ROW_NUMBER() memberi nomor pada baris di setiap kelompok duplikat. ORDER BY di dalam OVER (...) menentukan baris mana yang mendapat nomor 1 dan dipertahankan: yang paling lama, paling baru, atau paling lengkap.
Hapus salinan berlebih
Setelah tahu baris mana yang dipertahankan, hapus sisanya. Untuk mempertahankan baris terbaru alih-alih yang terlama, beri nomor baris berdasarkan tanggal, mulai dari yang terbaru, lalu hapus semua yang bernomor di atas 1.
Contoh nyata menghapus duplikat
Semua negara dan kurir pengiriman Anda
DISTINCT berlaku untuk seluruh baris dari kolom yang dipilih, jadi Kanada muncul sekali untuk setiap kurir:
-- Input: shipments
-- order_id | country | carrier
-- ---------+---------+--------
-- 1001 | Canada | UPS
-- 1002 | Germany | DHL
-- 1003 | Canada | FedEx
-- 1004 | Canada | UPS
-- 1005 | Germany | DHL
SELECT DISTINCT country, carrier
FROM shipments
ORDER BY country, carrier;
-- Output:
-- country | carrier
-- --------+--------
-- Canada | FedEx
-- Canada | UPS
-- Germany | DHL
Alamat email yang hanya berbeda huruf besar-kecil
Kelompokkan berdasarkan email dalam huruf kecil agar Jon@Example.com dan jon@example.com dihitung sebagai salinan. (MySQL dan SQL Server secara default sudah mengabaikan huruf besar-kecil, dan LOWER() tidak berdampak buruk di sana.)
-- Input: accounts
-- id | email
-- ---+----------------------
-- 1 | Ana.Silva@example.com
-- 2 | jon@example.com
-- 3 | ana.silva@example.com
-- 4 | Jon@Example.com
-- 5 | mei@example.com
SELECT LOWER(email) AS email, COUNT(*) AS copies
FROM accounts
GROUP BY LOWER(email)
HAVING COUNT(*) > 1
ORDER BY email;
-- Output:
-- email | copies
-- ----------------------+-------
-- ana.silva@example.com | 2
-- jon@example.com | 2
Pertahankan salinan yang punya nomor telepon
Urutkan baris yang memiliki nomor telepon lebih dulu, lalu berdasarkan id, sehingga baris paling lengkap di setiap kelompok mendapat nomor 1:
-- Input: contacts
-- id | email | phone
-- ---+------------------+---------
-- 1 | ravi@example.com | NULL
-- 2 | ravi@example.com | 555-0110
-- 3 | ines@example.com | 555-0133
-- 4 | ines@example.com | NULL
SELECT
id,
email,
phone,
ROW_NUMBER() OVER (
PARTITION BY email
ORDER BY CASE WHEN phone IS NULL THEN 1 ELSE 0 END, id
) AS row_num
FROM contacts
ORDER BY email, row_num;
-- Output:
-- id | email | phone | row_num
-- ---+------------------+----------+--------
-- 3 | ines@example.com | 555-0133 | 1
-- 4 | ines@example.com | NULL | 2
-- 2 | ravi@example.com | 555-0110 | 1
-- 1 | ravi@example.com | NULL | 2
Menghapus baris bernomor 2 atau lebih (id 1 dan 4) menyisakan satu kontak lengkap per email.
Hapus salinan identik dari tabel tanpa id
Jika salinannya identik di semua kolom, tidak ada id terkecil yang bisa dipertahankan. Salin baris-baris distinct ke tabel baru lalu tukar dengan tabel lama. Tambahkan kembali index dan constraint yang dimiliki tabel lama, karena salinannya tidak membawanya:
-- Input: page_views
-- url | viewed_at
-- ---------+--------------------
-- /pricing | 2026-10-01 09:00:00
-- /pricing | 2026-10-01 09:00:00
-- /docs | 2026-10-01 09:05:00
-- /pricing | 2026-10-01 09:00:00
-- In SQL Server, use SELECT DISTINCT * INTO page_views_clean FROM page_views,
-- then EXEC sp_rename 'page_views_clean', 'page_views'
CREATE TABLE page_views_clean AS SELECT DISTINCT * FROM page_views;
DROP TABLE page_views;
ALTER TABLE page_views_clean RENAME TO page_views;
-- Output (page_views afterwards):
-- url | viewed_at
-- ---------+--------------------
-- /pricing | 2026-10-01 09:00:00
-- /docs | 2026-10-01 09:05:00
Bacaan lanjutan tentang menghapus duplikat
Di blog ini:
- Panduan mencari duplikat di SQL: lebih banyak cara menemukan baris duplikat sebelum menghapusnya
- Mendeteksi baris duplikat di tabel SQL Server: dari sudut pandang SQL Server
- Panduan SQL COUNT DISTINCT: menghitung nilai unik alih-alih menghapus pengulangan
- Sintaks dan contoh pernyataan SQL DELETE: dasar-dasar menghapus baris
- Membuat unique constraint di PostgreSQL: mencegah duplikat untuk selamanya
- Panduan SQL CTE vs subquery: dua cara menulis langkah “baris mana yang dipertahankan”
Dokumentasi resmi:
- PostgreSQL: DELETE dan window function
- MySQL: DELETE dan ROW_NUMBER()
- SQLite: DELETE dan window function
- SQL Server: DELETE dan ROW_NUMBER
Beekeeper Studio Adalah GUI Database Gratis & Open Source
Tool query & editor SQL terbaik yang pernah saya pakai. Semua yang saya butuhkan untuk mengelola database ada di sini. - ⭐⭐⭐⭐⭐ Mit
Beekeeper Studio cepat, intuitif, dan mudah digunakan. Beekeeper mendukung banyak sekali database, dan berjalan mulus di Windows, Mac, dan Linux.
Apa Kata Pengguna Tentang Beekeeper Studio
"Beekeeper Studio sepenuhnya menggantikan alur kerja SQL lama saya. Cepat, intuitif, dan membuat pekerjaan database kembali menyenangkan."
"Saya sudah mencoba banyak GUI database, tetapi Beekeeper punya keseimbangan sempurna antara fitur dan kesederhanaan. Langsung berfungsi."