2009-07-24 23 views
6

¿Alguien tiene una declaración SQL elegante para eliminar registros duplicados de una tabla, pero solo si hay más de x número de duplicados? Entonces permite hasta 2 o 3 duplicados, pero eso es todo?Consulta SQL - ¿Eliminar duplicados si hay más de 3 dups?

Actualmente tengo una instrucción SELECT que hace lo siguiente:

delete table 
from table t 
left outer join (
select max(id) as rowid, dupcol1, dupcol2 
from table 
group by dupcol1, dupcol2 
) as keeprows on t.id=keeprows.rowid 
where keeprows.rowid is null 

Esto funciona muy bien. Pero ahora lo que me gustaría hacer es eliminar esas filas solo si tienen más de 2 duplicados.

Gracias

+0

cuando hay 5 duplicados, ¿quieres único que queda después de la eliminación, o tres? – Stobor

Respuesta

7
with cte as (
    select row_number() over (partition by dupcol1, dupcol2 order by ID) as rn 
    from table) 
delete from cte 
    where rn > 2; -- or >3 etc 

La consulta es la fabricación de un 'número de fila' para cada registro, agrupados por el (dupcol1, dupcol2) y ordenados por ID . En efecto, este número de fila cuenta 'duplicados' que tienen el mismo dupcol1 y dupcol2 y asigna el número 1, 2, 3 .. N, ordena por ID. Si desea mantener sólo 2 'duplicados', entonces usted necesita para eliminar aquellas que fueron asignados los números 3,4,.. N y que es la parte atendido por el DELLETE.. WHERE rn > 2;

Usando este método se puede cambiar el ORDER BY para adaptarse a su preferido orden (por ejemplo, ORDER BY ID DESC), por lo que el LATEST tiene rn=1, luego el siguiente a la última es rn = 2 y así sucesivamente. El resto permanece igual, el DELETE eliminará solo los más antiguos ya que tienen los números de fila más altos.

A diferencia de this closely related question, a medida que la condición se vuelve más compleja, usar CTE y row_number() se vuelve más simple. El rendimiento puede ser problemático aún si no existe un índice de acceso adecuado.

+0

Gracias Remus, pero como no soy un experto en SQL y no estoy familiarizado con las palabras clave específicas de 2005, ¿podría explicarme qué está haciendo la consulta? Supongo que la partición es un buen atajo para unir izquierda a una tabla agrupada, similar a mi primer ejemplo ?? ¿Entonces su segunda línea está devolviendo una nueva identificación de todos los registros duplicados basados ​​en las columnas proporcionadas? ¿Cuál es la cantidad de veces que se duplicó la fila según las columnas de la segunda línea? Gracias. – Scott

+1

La consulta está generando un 'número de fila' para cada registro, agrupados por (dupcol1, dupcol2) y ordenados por ID. En efecto, este número de fila cuenta 'duplicados' que tienen el mismo dupcol1 y dupcol2 y asigna el número 1, 2, 3 .. N, ordena por ID. Si desea mantener solo 2 'duplicados', entonces debe eliminar aquellos a los que se asignaron los números 3,4, .. N y esa es la parte que cuida DELLETE .. WHERE rn> 2; HTH, avíseme si todavía no está claro. –

+0

No, lo tengo, muchas gracias. Una última cosa, sin embargo, quiero asegurarme de que siempre conservo el último registro. Entonces, si conservo registros con decir <2 duplicados y luego elimino todos los demás, ¿cómo puedo modificar la consulta para garantizar que se conservan los registros más recientes (máximo (id)) de la tabla? Como ejemplo: digamos que un registro aparece en nuestro sistema 10 veces. Esto viola la regla duplicada "2". Nos gustaría eliminar 7 de los duplicados, dejando solo un registro maestro y dos duplicados. Por registro maestro, nos referimos al registro MÁS RECIENTE (más actualizado) que ingresó al sistema. – Scott

3

HAVING es su amigo

select id, count(*) cnt from table group by id having cnt>2

0

bastante tarde pero la solución más simple podría ser la siguiente supongamos que tenemos mesa emp_dept (empid, deptid) que tiene filas duplicadas, Aquí he utilizado @Count como por ejemplo varibale .. 2 duplican deja entonces @count = 2 En base de datos Oracle

delete from emp_dept where @Count <= (select count(1) from emp_dept i where i.empid = emp_dept.empid and i.deptid = emp_dept.deptid and i.rowid < emp_dept.rowid) 

En el servidor SQL o anydatabase que no soporta la fila ID de característica poco, tenemos que añadir la columna de identidad sólo para identificar cada fila. decir que hemos añadido nid como la identidad de la tabla

alter table emp_dept add nid int identity(1,1) -- to add identity column 

ahora consulta para eliminar duplicados podría escribir como

delete from emp_dept where @@Count <= (select count(1) from emp_dept i where i.empid = emp_dept.empid and i.deptid = emp_dept.deptid and i.nid< emp_dept.nid) 

Aquí el concepto es eliminar todas las filas para las que existe otras filas que tienen similares valores centrales pero n o mayor cantidad de rowid o identidad más pequeños. Por lo tanto, si existen filas duplicadas, se eliminará una que tenga una identificación de fila o identidad más alta. y para la fila no hay duplicado, falla al encontrar la identificación de la fila inferior, por lo que no se borrará.

0

Para Oracle:

delete from test where rowid = ANY (select min(test.rowid) from test left 
    outer join 
    (select min(rowid) row_id from test group by id,name)t on 
    test.rowid=t.row_id where t.row_id is null group by test.id,test.name); 
Cuestiones relacionadas