Encontrar valores duplicados en una tabla SQL

1936

Es fácil encontrar duplicados con un campo:

SELECT name, COUNT(email) 
FROM users
GROUP BY email
HAVING COUNT(email) > 1

Entonces si tenemos una mesa

ID   NAME   EMAIL
1    John   [email protected]
2    Sam    [email protected]
3    Tom    [email protected]
4    Bob    [email protected]
5    Tom    [email protected]

Esta consulta nos dará a John, Sam, Tom, Tom porque todos tienen lo mismo email.

Sin embargo, lo que quiero es obtener duplicados con el mismo email y name .

Es decir, quiero obtener "Tom", "Tom".

La razón por la que necesito esto: cometí un error y permití insertar duplicados namey emailvalores. Ahora necesito eliminar / cambiar los duplicados, así que primero necesito encontrarlos .

Alex
fuente
28
No creo que le permita seleccionar el nombre en su primera muestra, ya que no está en una función agregada. "¿Cuál es el recuento de direcciones de correo electrónico coincidentes y su nombre" es una lógica
engañosa
3
Descubrí que esto no funciona con el servidor MSSQL debido al namecampo en SELECT.
E. van Putten
lo que necesito es la identificación de los registros con correo electrónico duplicado
Marcos Di Paolo

Respuestas:

3038
SELECT
    name, email, COUNT(*)
FROM
    users
GROUP BY
    name, email
HAVING 
    COUNT(*) > 1

Simplemente agrupe en ambas columnas.

Nota: el estándar ANSI anterior es tener todas las columnas no agregadas en GROUP BY, pero esto ha cambiado con la idea de "dependencia funcional" :

En la teoría de bases de datos relacionales, una dependencia funcional es una restricción entre dos conjuntos de atributos en una relación de una base de datos. En otras palabras, la dependencia funcional es una restricción que describe la relación entre atributos en una relación.

El soporte no es consistente:

gbn
fuente
92
@webXL DONDE funciona con un solo registro HABER funciona con un grupo
bjan
8
@gbn ¿Es posible incluir el ID en los resultados? Entonces sería más fácil eliminar esos duplicados después.
user797717
13
@ user797717: necesitaría tener MIN (ID) y luego eliminar los valores de ID que no
estén
1
¿Qué pasa con los casos en que cualquiera de las columnas tiene valores nulos?
Ankit Dhingra
1
Muchas gracias por esto, y sí, funciona en Oracle, aunque necesitaba la singularidad de la condición, así que en lugar de>1 =1
Bill Naylor
370

prueba esto:

declare @YourTable table (id int, name varchar(10), email varchar(50))

INSERT @YourTable VALUES (1,'John','John-email')
INSERT @YourTable VALUES (2,'John','John-email')
INSERT @YourTable VALUES (3,'fred','John-email')
INSERT @YourTable VALUES (4,'fred','fred-email')
INSERT @YourTable VALUES (5,'sam','sam-email')
INSERT @YourTable VALUES (6,'sam','sam-email')

SELECT
    name,email, COUNT(*) AS CountOf
    FROM @YourTable
    GROUP BY name,email
    HAVING COUNT(*)>1

SALIDA:

name       email       CountOf
---------- ----------- -----------
John       John-email  2
sam        sam-email   2

(2 row(s) affected)

si quieres las ID de los dups usa esto:

SELECT
    y.id,y.name,y.email
    FROM @YourTable y
        INNER JOIN (SELECT
                        name,email, COUNT(*) AS CountOf
                        FROM @YourTable
                        GROUP BY name,email
                        HAVING COUNT(*)>1
                    ) dt ON y.name=dt.name AND y.email=dt.email

SALIDA:

id          name       email
----------- ---------- ------------
1           John       John-email
2           John       John-email
5           sam        sam-email
6           sam        sam-email

(4 row(s) affected)

para eliminar los duplicados intente:

DELETE d
    FROM @YourTable d
        INNER JOIN (SELECT
                        y.id,y.name,y.email,ROW_NUMBER() OVER(PARTITION BY y.name,y.email ORDER BY y.name,y.email,y.id) AS RowRank
                        FROM @YourTable y
                            INNER JOIN (SELECT
                                            name,email, COUNT(*) AS CountOf
                                            FROM @YourTable
                                            GROUP BY name,email
                                            HAVING COUNT(*)>1
                                        ) dt ON y.name=dt.name AND y.email=dt.email
                   ) dt2 ON d.id=dt2.id
        WHERE dt2.RowRank!=1
SELECT * FROM @YourTable

SALIDA:

id          name       email
----------- ---------- --------------
1           John       John-email
3           fred       John-email
4           fred       fred-email
5           sam        sam-email

(4 row(s) affected)
KM.
fuente
127

Prueba esto:

SELECT name, email
FROM users
GROUP BY name, email
HAVING ( COUNT(*) > 1 )
Chris Van Opstal
fuente
72

Si desea eliminar los duplicados, esta es una forma mucho más simple de hacerlo que tener que buscar filas pares / impares en una triple selección secundaria:

SELECT id, name, email 
FROM users u, users u2
WHERE u.name = u2.name AND u.email = u2.email AND u.id > u2.id

Y para eliminar:

DELETE FROM users
WHERE id IN (
    SELECT id/*, name, email*/
    FROM users u, users u2
    WHERE u.name = u2.name AND u.email = u2.email AND u.id > u2.id
)

Mucho más fácil de leer y entender en mi humilde opinión

Nota: El único problema es que debe ejecutar la solicitud hasta que no se eliminen filas, ya que elimina solo 1 de cada duplicado cada vez

AncAinu
fuente
2
Agradable y fácil de leer; Sin embargo, me gustaría encontrar una forma de eliminar varias filas duplicadas de una vez.
Dickon Reed
1
Esto no funciona para mí cuando llegoYou can't specify target table 'users' for update in FROM clause
Whitecat
1
@Whitecat parece un simple problema de MySQL: stackoverflow.com/questions/4429319/…
AncAinu
1
Me falla. Obtengo: "DBD :: CSV :: st execute falló: uso del valor no inicializado $ _ [1] en el elemento hash en /Users/hornenj/perl5/perlbrew/perls/perl-5.26.0/lib/site_perl/5.26. 0 / SQL / Eval.pm línea 43 "
Nigel Horne
1
Creo que la cláusula where debería ser "u.name = u2.name AND u.email = u2.email AND (u.id> u2.id OR u2.id> u.id)" ¿no es así?
GiveEmTheBoot
48

Intenta lo siguiente:

SELECT * FROM
(
    SELECT Id, Name, Age, Comments, Row_Number() OVER(PARTITION BY Name, Age ORDER By Name)
        AS Rank 
        FROM Customers
) AS B WHERE Rank>1
gaurav singh
fuente
3
Un ligero cambio en SELECT * me ayudó a resolver una búsqueda de una hora. Nunca he usado OVER (PARTITION BY antes. ¡Nunca dejo de sorprenderme de cuántas maneras de hacer lo mismo en SQL!
Joe Ruder
33
 SELECT name, email 
    FROM users
    WHERE email in
    (SELECT email FROM users
    GROUP BY email 
    HAVING COUNT(*)>1)
PRADEEPTA VIRLLEY
fuente
28

Un poco tarde para la fiesta, pero encontré una solución realmente genial para encontrar todas las ID duplicadas:

SELECT GROUP_CONCAT( id )
FROM users
GROUP BY email
HAVING ( COUNT(email) > 1 )
Indivision Dev
fuente
2
Parece ser una solución sintáctica de azúcar. Buen hallazgo
Chef_Code
3
Tenga en cuenta que GROUP_CONCATse detendrá después de una longitud predeterminada, por lo que es posible que no obtenga todos los ids.
v010dya
24

prueba este código

WITH CTE AS

( SELECT Id, Name, Age, Comments, RN = ROW_NUMBER()OVER(PARTITION BY Name,Age ORDER BY ccn)
FROM ccnmaster )
select * from CTE 
Tanmay Nehete
fuente
23

Esto selecciona / elimina todos los registros duplicados, excepto un registro de cada grupo de duplicados. Por lo tanto, la eliminación deja todos los registros únicos + un registro de cada grupo de duplicados.

Seleccionar duplicados:

SELECT *
FROM table
WHERE
    id NOT IN (
        SELECT MIN(id)
        FROM table
        GROUP BY column1, column2
);

Eliminar duplicados:

DELETE FROM table
WHERE
    id NOT IN (
        SELECT MIN(id)
        FROM table
        GROUP BY column1, column2
);

Tenga en cuenta las grandes cantidades de registros, puede causar problemas de rendimiento.

Martin Silovský
fuente
2
Error en la consulta de eliminación - No se puede especificar la tabla de destino 'ciudades' para actualizar en la cláusula FROM
Ali Azhar
2
No hay tabla 'ciudades' ni cláusula de actualización. ¿Qué quieres decir? ¿Dónde hay un error en la consulta de eliminación?
Martin Silovský
2
¿Cómo funciona eso con los datos de OP?
thoroc
3
¿Qué significa el "OP"?
Martin Silovský
19

En caso de que trabaje con Oracle, esta forma sería preferible:

create table my_users(id number, name varchar2(100), email varchar2(100));

insert into my_users values (1, 'John', '[email protected]');
insert into my_users values (2, 'Sam', '[email protected]');
insert into my_users values (3, 'Tom', '[email protected]');
insert into my_users values (4, 'Bob', '[email protected]');
insert into my_users values (5, 'Tom', '[email protected]');

commit;

select *
  from my_users
 where rowid not in (select min(rowid) from my_users group by name, email);
xDBA
fuente
15
select name, email
, case 
when ROW_NUMBER () over (partition by name, email order by name) > 1 then 'Yes'
else 'No'
end "duplicated ?"
from users
Narendra
fuente
2
Las respuestas de código solo están mal vistas en Stack Overflow, ¿podría explicar por qué esto responde la pregunta?
Rich Benner
2
@RichBenner: No encontré la respuesta como, cada una de las filas en el resultado y que nos dice cuáles son filas duplicadas y cuáles no están a la vista y no agruparlas, porque si queremos combinar esto consultar con cualquier otro grupo de consulta por no es una buena opción.
Narendra
2
Al agregar Id a la instrucción select y filtrar duplicados, le da la posibilidad de eliminar los identificadores duplicados y continuar con cada uno.
Antoine Reinhold Bertrand
12

Si desea ver si hay filas duplicadas en su tabla, utilicé la siguiente consulta:

create table my_table(id int, name varchar(100), email varchar(100));

insert into my_table values (1, 'shekh', '[email protected]');
insert into my_table values (1, 'shekh', '[email protected]');
insert into my_table values (2, 'Aman', '[email protected]');
insert into my_table values (3, 'Tom', '[email protected]');
insert into my_table values (4, 'Raj', '[email protected]');


Select COUNT(1) As Total_Rows from my_table 
Select Count(1) As Distinct_Rows from ( Select Distinct * from my_table) abc 
Shekhar Kumar
fuente
11

Esto es lo fácil que se me ocurrió. Utiliza una expresión de tabla común (CTE) y una ventana de partición (creo que estas características están en SQL 2008 y versiones posteriores).

Este ejemplo encuentra a todos los estudiantes con nombre y nombre duplicados. Los campos que desea verificar para duplicar van en la cláusula OVER. Puede incluir cualquier otro campo que desee en la proyección.

with cte (StudentId, Fname, LName, DOB, RowCnt)
as (
SELECT StudentId, FirstName, LastName, DateOfBirth as DOB, SUM(1) OVER (Partition By FirstName, LastName, DateOfBirth) as RowCnt
FROM tblStudent
)
SELECT * from CTE where RowCnt > 1
ORDER BY DOB, LName
Darrel Lee
fuente
11
select id,name,COUNT(*) from user group by Id,Name having COUNT(*)>1
Debendra Dash
fuente
10

¿Cómo podemos contar los valores duplicados? o se repite 2 veces o más de 2. solo cuéntelos, no en grupo.

tan simple como

select COUNT(distinct col_01) from Table_01
Muhammad Tahir
fuente
2
¿Cómo funcionaría esto para la pregunta formulada? Esto no proporciona filas que duplican información en varias columnas (por ejemplo, "correo electrónico" y "nombre") en diferentes filas.
Jeroen
10

Al usar CTE también podemos encontrar valores duplicados como este

with MyCTE
as
(
select Name,EmailId,ROW_NUMBER() over(PARTITION BY EmailId order by id) as Duplicate from [Employees]

)
select * from MyCTE where Duplicate>1
Debendra Dash
fuente
9
 select emp.ename, emp.empno, dept.loc 
          from emp
 inner join dept 
          on dept.deptno=emp.deptno
 inner join
    (select ename, count(*) from
    emp
    group by ename, deptno
    having count(*) > 1)
 t on emp.ename=t.ename order by emp.ename
/
naveed
fuente
8

SELECT id, COUNT(id) FROM table1 GROUP BY id HAVING COUNT(id)>1;

Creo que esto funcionará correctamente para buscar valores repetidos en una columna en particular.

usuario4877838
fuente
66
Esto no agrega nada a la respuesta principal , y técnicamente ni siquiera difiere del código OP publicado en la pregunta.
Jeroen
7
SELECT * FROM users u where rowid = (select max(rowid) from users u1 where
u.email=u1.email);
Panky031
fuente
6

Esto también debería funcionar, tal vez intentarlo.

  Select * from Users a
            where EXISTS (Select * from Users b 
                where (     a.name = b.name 
                        OR  a.email = b.email)
                     and a.ID != b.id)

Especialmente bueno en su caso Si busca duplicados que tienen algún tipo de prefijo o cambio general como, por ejemplo, un nuevo dominio en el correo. entonces puede usar replace () en estas columnas

veritas
fuente
5

Si desea encontrar datos duplicados (por uno o varios criterios) y seleccione las filas reales.

with MYCTE as (
    SELECT DuplicateKey1
        ,DuplicateKey2 --optional
        ,count(*) X
    FROM MyTable
    group by DuplicateKey1, DuplicateKey2
    having count(*) > 1
) 
SELECT E.*
FROM MyTable E
JOIN MYCTE cte
ON E.DuplicateKey1=cte.DuplicateKey1
    AND E.DuplicateKey2=cte.DuplicateKey2
ORDER BY E.DuplicateKey1, E.DuplicateKey2, CreatedAt

http://developer.azurewebsites.net/2014/09/better-sql-group-by-find-duplicate-data/

Lauri Lubi
fuente
4
SELECT name, email,COUNT(email) 
FROM users 
WHERE email IN (
    SELECT email 
    FROM users 
    GROUP BY email 
    HAVING COUNT(email) > 1)
Mohammad Neamul Islam
fuente
No puede usar COUNTsin GROUP BY, a menos que se refiera a toda la tabla.
RalfFriedl
Sin Group By usaste COUNT pero aquí he cometido un error al escribir COUNT
Mohammad Neamul Islam
3

Para eliminar registros cuyos nombres están duplicados

;WITH CTE AS    
(

    SELECT ROW_NUMBER() OVER (PARTITION BY name ORDER BY name) AS T FROM     @YourTable    
)

DELETE FROM CTE WHERE T > 1
Alguacil
fuente
3

Para verificar desde un registro duplicado en una tabla.

select * from users s 
where rowid < any 
(select rowid from users k where s.name = k.name and s.email = k.email);

o

select * from users s 
where rowid not in 
(select max(rowid) from users k where s.name = k.name and s.email = k.email);

Para eliminar el registro duplicado en una tabla.

delete from users s 
where rowid < any 
(select rowid from users k where s.name = k.name and s.email = k.email);

o

delete from users s 
where rowid not in 
(select max(rowid) from users k where s.name = k.name and s.email = k.email);
Arun Solomon
fuente
1

SELECT column_name,COUNT(*) FROM TABLE_NAME GROUP BY column1, HAVING COUNT(*) > 1;

rahul kumar
fuente
1

Podemos usar tener aquí que funciona en funciones agregadas como se muestra a continuación

create table #TableB (id_account int, data int, [date] date)
insert into #TableB values (1 ,-50, '10/20/2018'),
(1, 20, '10/09/2018'),
(2 ,-900, '10/01/2018'),
(1 ,20, '09/25/2018'),
(1 ,-100, '08/01/2018')  

SELECT id_account , data, COUNT(*)
FROM #TableB
GROUP BY id_account , data
HAVING COUNT(id_account) > 1

drop table #TableB

Aquí, como dos campos, id_account y data se usan con Count (*). Por lo tanto, dará todos los registros que tengan más de una vez los mismos valores en ambas columnas.

Por alguna razón, por error, no hemos agregado ninguna restricción en la tabla del servidor SQL y los registros se han insertado por duplicado en todas las columnas con la aplicación front-end. Entonces podemos usar la consulta a continuación para eliminar la consulta duplicada de la tabla.

SELECT DISTINCT * INTO #TemNewTable FROM #OriginalTable
TRUNCATE TABLE #OriginalTable
INSERT INTO #OriginalTable SELECT * FROM #TemNewTable
DROP TABLE #TemNewTable

Aquí hemos tomado todos los registros distintos de la tabla original y eliminado los registros de la tabla original. Nuevamente, insertamos todos los valores distintos de la nueva tabla en la tabla original y luego eliminamos la nueva tabla.

Suraj Kumar
fuente
1

Es posible que desee probar esto

SELECT NAME, EMAIL, COUNT(*)
FROM USERS
GROUP BY 1,2
HAVING COUNT(*) > 1
adesh
fuente
1

Lo más importante aquí es tener la función más rápida. También se deben identificar los índices de duplicados. La unión automática es una buena opción, pero para tener una función más rápida, es mejor encontrar primero las filas que tienen duplicados y luego unirse a la tabla original para encontrar la identificación de las filas duplicadas. Finalmente ordene por cualquier columna, excepto id, para tener filas duplicadas una cerca de la otra.

SELECT u.*
FROM users AS u
JOIN (SELECT username, email
      FROM users
      GROUP BY username, email
      HAVING COUNT(*)>1) AS w
ON u.username=w.username AND u.email=w.email
ORDER BY u.email;
RyanAbnavi
fuente
0

Puede usar la palabra clave SELECT DISTINCT para deshacerse de los duplicados. También puede filtrar por nombre y poner a todos con ese nombre en una tabla.

Parkofadown
fuente
0

El código exacto diferirá dependiendo de si desea encontrar filas duplicadas o solo identificadores diferentes con el mismo correo electrónico y nombre. Si id es una clave principal o tiene una restricción única, esta distinción no existe, pero la pregunta no lo especifica. En el primer caso, puede usar el código proporcionado en varias otras respuestas:

SELECT name, email, COUNT(*)
FROM users
GROUP BY name, email
HAVING COUNT(*) > 1

En este último caso usarías:

SELECT name, email, COUNT(DISTINCT id)
FROM users
GROUP BY name, email
HAVING COUNT(DISTINCT id) > 1
ORDER BY COUNT(DISTINCT id) DESC
JUBILADO
fuente