🧚 Dengar! Beekeeper Studio adalah GUI database yang cepat, modern, dan open source Unduh
9 Januari 2026 Oleh Matthew Rathbone

Untuk mencari duplikat di SQL, kelompokkan baris berdasarkan kolom yang seharusnya unik, lalu ambil grup yang berisi lebih dari satu baris. Window function dan self-join kemudian menampilkan baris duplikatnya sendiri, sehingga Anda bisa memutuskan salinan mana yang dipertahankan. Contoh-contohnya berjalan di PostgreSQL, MySQL, SQLite, dan SQL Server.

Cara mencari duplikat di SQL

GROUP BY mengumpulkan baris dengan nilai yang sama, COUNT(*) menghitung setiap grup, dan HAVING menyisakan grup yang berisi lebih dari satu baris:

SELECT columns, COUNT(*)
FROM table_name
GROUP BY columns
HAVING COUNT(*) > 1;

Misalnya, untuk mencari alamat email yang dipakai oleh lebih dari satu pelanggan:

-- Input: customers
-- id | name           | email
-- ---+----------------+----------------------
-- 1  | Maria Lopez    | maria.lopez@gmail.com
-- 2  | Sam Patel      | sam@patel.dev
-- 3  | M. Lopez       | maria.lopez@gmail.com
-- 4  | Kenji Watanabe | kenji@watanabe.jp

SELECT email, COUNT(*) AS copies
FROM customers
GROUP BY email
HAVING COUNT(*) > 1;

-- Output:
-- email                 | copies
-- ----------------------+-------
-- maria.lopez@gmail.com | 2

Agar baris dianggap duplikat hanya jika beberapa kolom sekaligus cocok, cantumkan semua kolom tersebut di SELECT dan juga di GROUP BY.

Aturan yang perlu diketahui

  • Setiap kolom yang Anda pilih harus ada di GROUP BY atau berada di dalam fungsi agregat. Menambahkan name ke query di atas tidak akan menampilkan nama-nama duplikatnya. Hasilnya justru error, kecuali di SQLite, yang menampilkan satu nama per email:
-- Input: the customers table above

SELECT name, email, COUNT(*) AS copies
FROM customers
GROUP BY email
HAVING COUNT(*) > 1;

-- ERROR: column "customers.name" must appear in the GROUP BY clause or be used in an aggregate function

Untuk melihat setiap baris duplikat, gunakan salah satu query di bagian “Sintaks alternatif”.

  • NULL dikelompokkan bersama, dan huruf besar/kecil berpengaruh. Semua email NULL masuk ke satu grup sehingga tampak seperti duplikat satu sama lain, sementara Ana@Silva.dev dan ana@silva.dev tidak:
-- Input: users
-- id | email
-- ---+--------------
-- 1  | ana@silva.dev
-- 2  | NULL
-- 3  | Ana@Silva.dev
-- 4  | NULL

SELECT email, COUNT(*) AS copies
FROM users
GROUP BY email
HAVING COUNT(*) > 1;

-- Output:
-- email | copies
-- ------+-------
-- NULL  | 2

-- MySQL and SQL Server ignore case by default, so they also return ana@silva.dev | 2

-- Skip the NULLs and ignore case:
SELECT LOWER(email) AS email, COUNT(*) AS copies
FROM users
WHERE email IS NOT NULL
GROUP BY LOWER(email)
HAVING COUNT(*) > 1;

-- Output:
-- email         | copies
-- --------------+-------
-- ana@silva.dev | 2

Sintaks alternatif

GROUP BY mengembalikan satu baris untuk setiap nilai yang terduplikasi. Untuk mendapatkan baris duplikatnya sendiri, hitunglah melalui window. COUNT(*) OVER (PARTITION BY email) menaruh jumlah setiap email di setiap baris yang memilikinya:

-- Input: the customers table above

SELECT id, name, email, copies
FROM (
  SELECT id, name, email, COUNT(*) OVER (PARTITION BY email) AS copies
  FROM customers
) AS c
WHERE copies > 1
ORDER BY id;

-- Output:
-- id | name        | email                 | copies
-- ---+-------------+-----------------------+-------
-- 1  | Maria Lopez | maria.lopez@gmail.com | 2
-- 3  | M. Lopez    | maria.lopez@gmail.com | 2

EXISTS melakukan hal yang sama tanpa window function: ambil setiap baris yang memiliki baris lain dengan email yang sama:

-- Input: the customers table above

SELECT id, name, email
FROM customers c
WHERE EXISTS (
  SELECT 1 FROM customers other
  WHERE other.email = c.email AND other.id <> c.id
);

-- Output:
-- id | name        | email
-- ---+-------------+----------------------
-- 1  | Maria Lopez | maria.lopez@gmail.com
-- 3  | M. Lopez    | maria.lopez@gmail.com

Kapan Anda perlu mencari duplikat?

Duplikat masuk lewat impor data, klik ganda, percobaan ulang, dan constraint yang tidak ada. Berikut empat tugas yang paling sering perlu Anda lakukan terkait duplikat.

Memastikan kolom bersifat unik

Sebelum menambahkan constraint UNIQUE, atau ketika login atau pencarian mulai mengembalikan dua baris, tampilkan nilai-nilai yang muncul lebih dari sekali. GROUP BY dengan HAVING langsung menjawab hal ini.

Melihat setiap baris yang terlibat

Mengetahui nilai mana yang berulang saja belum cukup untuk memperbaikinya: Anda butuh baris lengkapnya untuk melihat perbedaannya dan mana yang benar. Masukkan kembali nilai-nilai duplikat itu ke query pada tabel yang sama.

Menandai salinan berlebih

Untuk membersihkan data, pertahankan satu baris dari setiap kelompok duplikat dan hapus sisanya. ROW_NUMBER() memberi nomor pada baris di setiap kelompok, sehingga semua baris setelah baris 1 adalah salinan berlebih.

Mendeteksi pengiriman ganda

Tombol yang diklik dua kali atau pembayaran yang dicoba ulang meninggalkan dua baris yang identik kecuali ID-nya. Menggabungkan tabel dengan dirinya sendiri memasangkan keduanya, dan t1.id < t2.id menampilkan setiap pasangan sekali saja.

Contoh nyata mencari duplikat

Mencari kamar hotel yang dipesan ganda

Sebuah kamar hanya bisa dipesan sekali per malam, jadi duplikat berarti kamar dan malam yang sama. Kelompokkan berdasarkan kedua kolom:

-- Input: bookings
-- booking_id | room | night      | guest
-- -----------+------+------------+----------
-- 1          | 204  | 2026-11-06 | R. Okafor
-- 2          | 204  | 2026-11-07 | R. Okafor
-- 3          | 310  | 2026-11-06 | L. Chen
-- 4          | 204  | 2026-11-06 | P. Novak

SELECT room, night, COUNT(*) AS bookings
FROM bookings
GROUP BY room, night
HAVING COUNT(*) > 1;

-- Output:
-- room | night      | bookings
-- -----+------------+---------
-- 204  | 2026-11-06 | 2

Menampilkan akun yang memakai alamat email yang sama

Orang sering mendaftar dua kali dengan alamat yang sama tapi huruf besar/kecil berbeda. Partisi berdasarkan LOWER(email) untuk menampilkan kedua akun berdampingan:

-- Input: accounts
-- id | username | email
-- ---+----------+----------------
-- 1  | jkim     | jin@kim.kr
-- 2  | olivia_r | olivia@reyes.mx
-- 3  | jin.kim  | Jin@Kim.kr
-- 4  | noah_b   | noah@baker.uk

SELECT id, username, email, accounts_with_email
FROM (
  SELECT id, username, email,
         COUNT(*) OVER (PARTITION BY LOWER(email)) AS accounts_with_email
  FROM accounts
) AS a
WHERE accounts_with_email > 1
ORDER BY id;

-- Output:
-- id | username | email      | accounts_with_email
-- ---+----------+------------+--------------------
-- 1  | jkim     | jin@kim.kr | 2
-- 3  | jin.kim  | Jin@Kim.kr | 2

Menandai orang duplikat sebelum pembersihan

Anggap pengguna dengan nama depan dan belakang yang sama sebagai satu orang, pertahankan data tertua, dan beri label pada sisanya. CTE memisahkan proses penomoran dari pemberian label:

-- Input: users
-- id | first_name | last_name | email
-- ---+------------+-----------+----------------
-- 1  | Priya      | Shah      | priya@shah.net
-- 2  | Tom        | Becker    | tom@becker.de
-- 3  | Priya      | Shah      | pshah@work.com
-- 4  | Lena       | Kowalski  | lena@kowal.pl
-- 5  | Tom        | Becker    | tbecker@mail.de

WITH numbered AS (
  SELECT id, first_name, last_name, email,
         ROW_NUMBER() OVER (PARTITION BY first_name, last_name ORDER BY id) AS copy_number
  FROM users
)
SELECT id, first_name, last_name, email,
       CASE WHEN copy_number > 1 THEN 'Duplicate' ELSE 'Original' END AS status
FROM numbered
ORDER BY id;

-- Output:
-- id | first_name | last_name | email           | status
-- ---+------------+-----------+-----------------+----------
-- 1  | Priya      | Shah      | priya@shah.net  | Original
-- 2  | Tom        | Becker    | tom@becker.de   | Original
-- 3  | Priya      | Shah      | pshah@work.com  | Duplicate
-- 4  | Lena       | Kowalski  | lena@kowal.pl   | Original
-- 5  | Tom        | Becker    | tbecker@mail.de | Duplicate

Baris yang bertanda Duplicate adalah baris yang perlu digabung atau dihapus. Panduan menghapus duplikat menjelaskan cara menghapusnya.

Mencari pesanan yang tidak sengaja dibuat dua kali

Dua pesanan dari pelanggan yang sama untuk produk yang sama di hari yang sama kemungkinan besar adalah satu pesanan yang terkirim dua kali:

-- Input: orders
-- order_id | customer_id | product   | order_date
-- ---------+-------------+-----------+-----------
-- 1001     | 42          | Desk Lamp | 2026-03-02
-- 1002     | 42          | Desk Lamp | 2026-03-02
-- 1003     | 42          | Desk Lamp | 2026-03-09
-- 1004     | 57          | Desk Lamp | 2026-03-02

SELECT o1.order_id AS first_order, o2.order_id AS repeat_order,
       o1.customer_id, o1.product
FROM orders o1
JOIN orders o2
  ON o1.customer_id = o2.customer_id
 AND o1.product = o2.product
 AND o1.order_date = o2.order_date
 AND o1.order_id < o2.order_id;

-- Output:
-- first_order | repeat_order | customer_id | product
-- ------------+--------------+-------------+----------
-- 1001        | 1002         | 42          | Desk Lamp

Bacaan lanjutan tentang duplikat

Di blog ini:

Dokumentasi resmi:

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.

Versi Linux Beekeeper punya fitur 100% lengkap, tanpa potong kompas, tanpa kompromi fitur.

Apa Kata Pengguna Tentang Beekeeper Studio

★★★★★
"Beekeeper Studio benar-benar menggantikan alur kerja SQL lama saya. Cepat, intuitif, dan membuat kerja dengan database kembali menyenangkan."
— Alex K., Database Developer
★★★★★
"Saya sudah mencoba banyak GUI database, tapi Beekeeper punya keseimbangan sempurna antara fitur dan kesederhanaan. Langsung jalan."
— Sarah M., Full Stack Engineer

Siap Meningkatkan Alur Kerja SQL Anda?

download Unduh Gratis