🧚 Escute! O Beekeeper Studio é uma GUI de banco de dados rápida, moderna e open source Baixar
9 de janeiro de 2026 Por Matthew Rathbone

Para encontrar duplicatas em SQL, agrupe as linhas pelas colunas que deveriam ser únicas e mantenha os grupos com mais de uma linha. Depois, funções de janela e self-joins mostram as próprias linhas duplicadas, para você decidir qual cópia manter. Os exemplos funcionam no PostgreSQL, MySQL, SQLite e SQL Server.

Como encontrar duplicatas em SQL

GROUP BY junta as linhas com o mesmo valor, COUNT(*) conta cada grupo e HAVING mantém os grupos com mais de uma linha:

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

Por exemplo, para encontrar endereços de email usados por mais de um cliente:

-- 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

Para considerar linhas duplicadas apenas quando várias colunas coincidem, liste todas elas tanto no SELECT quanto no GROUP BY.

Regras importantes

  • Toda coluna selecionada precisa estar no GROUP BY ou dentro de uma agregação. Adicionar name à consulta acima não mostra os nomes das duplicatas. Isso gera um erro, exceto no SQLite, que mostra um nome por 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

Para ver todas as linhas duplicadas, use uma das consultas em “Sintaxe alternativa”.

  • NULLs são agrupados juntos, e maiúsculas e minúsculas fazem diferença. Todos os emails NULL caem no mesmo grupo, então parecem duplicatas entre si, enquanto Ana@Silva.dev e ana@silva.dev não:
-- 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

Sintaxe alternativa

GROUP BY retorna uma linha por valor duplicado. Para obter as próprias linhas duplicadas, conte sobre uma janela. COUNT(*) OVER (PARTITION BY email) coloca a contagem de cada email em todas as linhas que o contêm:

-- 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 faz o mesmo sem função de janela: mantenha cada linha para a qual existe outra linha com o mesmo email:

-- 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

Quando você deve procurar duplicatas?

Duplicatas entram por importações, cliques duplos, novas tentativas e constraints ausentes. Estas são as quatro tarefas que você mais vai precisar fazer com elas.

Verificar se uma coluna é única

Antes de adicionar uma constraint UNIQUE, ou quando um login ou uma busca começa a retornar duas linhas, liste os valores que aparecem mais de uma vez. GROUP BY com HAVING responde isso diretamente.

Ver todas as linhas envolvidas

Saber qual valor se repete não basta para corrigir: você precisa das linhas completas para ver como diferem e qual está certa. Use os valores duplicados em uma consulta na mesma tabela.

Marcar as cópias extras

Para limpar, mantenha uma linha de cada conjunto de duplicatas e remova o resto. ROW_NUMBER() numera as linhas dentro de cada conjunto, então tudo depois da linha 1 é uma cópia extra.

Detectar envios duplicados

Um botão clicado duas vezes ou um pagamento repetido deixa duas linhas iguais em tudo, menos no ID. Fazer um join da tabela com ela mesma forma os pares, e t1.id < t2.id lista cada par uma única vez.

Exemplos reais de como encontrar duplicatas

Encontrar quartos de hotel com reserva dupla

Um quarto só pode ser reservado uma vez por noite, então uma duplicata é a combinação do mesmo quarto e da mesma noite. Agrupe pelas duas colunas:

-- 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

Listar contas que compartilham um endereço de email

As pessoas se cadastram duas vezes com o mesmo endereço em maiúsculas e minúsculas diferentes. Particione por LOWER(email) para colocar as duas contas lado a lado:

-- 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

Sinalizar pessoas duplicadas antes de uma limpeza

Trate usuários com o mesmo nome e sobrenome como uma única pessoa, mantenha o registro mais antigo e rotule os demais. Uma CTE separa a numeração da rotulagem:

-- 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

As linhas marcadas como Duplicate são as que devem ser mescladas ou excluídas. O guia para remover duplicatas mostra como excluí-las.

Encontrar pedidos feitos duas vezes por engano

Dois pedidos do mesmo cliente para o mesmo produto no mesmo dia provavelmente são um único pedido enviado duas vezes:

-- 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

Leituras adicionais sobre duplicatas

Neste blog:

Documentação oficial:

Beekeeper Studio é um cliente de banco de dados gratuito e open source

A melhor ferramenta de consultas e edição de SQL que já usei. Tem tudo o que preciso para gerenciar meu banco de dados. - ⭐⭐⭐⭐⭐ Mit

O Beekeeper Studio é rápido, intuitivo e fácil de usar. Ele suporta vários bancos de dados e funciona muito bem no Windows, Mac e Linux.

A versão do Beekeeper para Linux tem 100% dos recursos, sem atalhos e sem concessões.

O que os usuários dizem sobre o Beekeeper Studio

★★★★★
"O Beekeeper Studio substituiu completamente meu antigo fluxo de trabalho com SQL. É rápido, intuitivo e torna o trabalho com bancos de dados prazeroso de novo."
— Alex K., desenvolvedor de banco de dados
★★★★★
"Já testei muitas GUIs de banco de dados, mas o Beekeeper encontra o equilíbrio perfeito entre recursos e simplicidade. Simplesmente funciona."
— Sarah M., engenheira full stack

Pronto para melhorar seu fluxo de trabalho com SQL?

download Baixar grátis