Cómo encontrar valores duplicados en SQL Server

En este artículo encontrarás la manera de encontrar valores duplicados en una tabla o en una vista utilizando SQL. Vamos a ir paso a paso en el proceso. De primeras vamos a empezar con un problema sencillo, poco a poco construiremos la sentencia SQL, hasta obtener el resultado final.

Al final de este tutorial lograrás entender el patrón utilizado para identificar valores duplicados y serás capaz de utilizarlo en tu base de datos.

Todos los ejemplos de esta lección se basan en Microsoft SQL Server Management Studio y la base de datos AdventureWorks2012. Puedes empezar a utilizar estas herramientas gratuitamente y de forma libre, sin tener que rascarte el bolsillo.

Buscar valores duplicados en SQL Server

Empecemos. He basado este artículo en un caso real para que todo el mundo lo pueda entender; a la gerente de recursos humanos le gustaría encontrar a todos los empleados que comparten un mismo cumpleaños. A ella le gustaría tener una lista ordenada por la fecha de cumpleaños y por el nombre del empleado.

Después de buscar en la base de datos se hace evidente que la tabla HumanResources.Employee es la que tenemos que utilizar, ya que contiene las fechas de nacimiento de los empleados.

A primera vista parece que será bastante fácil encontrar valores duplicados en SQL Server. Después de todo, podemos ordenar fácilmente los datos.

¡Pero una vez ordenas los datos se hace mucho más difícil! Desde que SQL es un lenguaje basado en conjunto, no hay una manera sencilla, excepto si utilizamos cursores, de conocer los valores del registro anterior.

Por suerte hay otro manera para hacer esto. Usaremos un INNER JOIN para coincidir los cumpleaños de los empleados. Al hacer esto, vamos a obtener una lista de empleados que comparten la misma fecha de nacimiento.

Vamos a empezar con una simple consulta, los resultados se mostrarán, y veremos qué es lo que necesita un mejor pulido para seguir adelante. Vamos a empezar con conseguir una lista de los empleados y sus fechas de nacimiento.

Paso 1 - Obtener una lista de empleados ordenada por fecha de nacimiento

Cuando trabajamos con SQL, especialmente en un entorno desconocido, creo que es mejor construir una sentencia en pequeños pasos, verificando los resultados a medida que avanzamos, en lugar de escribir un comando SQL "final" en un solo paso. Esto hace que el depurado de errores sea mucho más sencillo.

Sugerencia: Si estás trabajando con una base de datos muy grande, puede tener sentido hacer una copia más ligera para pruebas y para que no tengas ningún problema a la hora para escribir sus consultas. De esta manera no consumirás el rendimiento de la base de datos en producción y obtener todos los datos que necesites de una manera ágil.

Así que para nuestro primer paso, vamos a enumerar todos los empleados. Para ello, uniremos la tabla de Employee con la tabla Person para poder obtener el nombre del empleado.

Aquí puedes ver la consulta:

SELECT   E1.BusinessEntityID,
         P.FirstName + ' ' + p.LastName AS FullName,
         E1.BirthDate
FROM     HumanResources.Employee AS E1
         INNER JOIN
         Person.Person AS P
         ON P.BusinessEntityID = E1.BusinessEntityID
ORDER BY E1.BirthDate, FullName

Si nos fijamos en el resultado podemos ver todos los elementos que nos ha solicitado la gerente de recursos humanos, con la excepción de que estamos mostrando todos los empleados.

En el siguiente paso vamos a configurar los resultados para que podamos empezar a comparar las fechas de nacimiento para encontrar valores duplicados.

Paso 2 - Comparar fechas de nacimiento para identificar duplicados.

Ahora que tenemos una lista con los empleados, necesitamos un medio para comparar las fechas de nacimiento para que podamos identificar a los empleados con las mismas fechas de nacimiento. En general, hablamos de valores duplicados.

Para hacer la comparación vamos a hacer un self-join en la tabla de empleados. Una self-join es sólo una versión simplificada de un INNER JOIN. Empezamos usando BirthDate como nuestra condición de unión. Esto asegura que sólo estemos recuperando los empleados con la misma fecha de nacimiento.

SELECT   E1.BusinessEntityID,
         E2.BusinessEntityID,
         P.FirstName + ' ' + p.LastName AS FullName,
         E1.BirthDate
FROM     HumanResources.Employee AS E1
         INNER JOIN
         Person.Person AS P
         ON P.BusinessEntityID = E1.BusinessEntityID
         INNER JOIN
         HumanResources.Employee AS E2
         ON E2.BirthDate = E1.BirthDate
ORDER BY E1.BirthDate, FullName

He añadido E2.BusinessEntityID a la consulta para que se pueda comparar la clave principal de tanto E1 como de E2. Puedes ver en muchos casos, que son los mismos.

La razón por la que nos estamos centrando en BusinessEntityID es por que es la clave principal y el identificador único de la tabla. Se convierte en un medio muy conciso y práctico para identificar los resultados de una fila y así entender su origen.

Nos estamos acercando a la obtención de nuestro resultado final, pero viendo los resultados estamos obteniendo el mismo registro tanto en las coincidencias de E1 y de E2.

Echa un vistazo a los elementos marcados en rojo. Estos son los falsos positivos que tenemos que eliminar de nuestros resultados. Esas son las mismas filas que se emparejan a sí mismos.

La buena noticia es que estamos muy cerca de identificar los duplicados.

He marcado a los duplicados en azul. Observa que los BusinessEntityID son diferentes. Esto indica que el self-join está uniendo BirthDate en diferentes filas - verdaderos duplicados para estar seguros.

En el siguiente paso vamos a tomar estos falsos positivos y los eliminaremos de nuestros resultados.

Paso 3 - Eliminando falsos positivos

En el paso previo te habrás dado cuenta de que todos los falsos positivos tienen el mismo BusinessEntityID; mientras que, los verdaderos duplicados no.

Esto que puede considerarse nimio, es una gran pista.

Si queremos sólo obtener los duplicados, tenemos que traer solamente los registros en donde los valores BusinessEntityID no sean iguales.

Para ello podemos añadir

E2.BusinessEntityID <> E1.BusinessEntityID

Como condición de unión en nuestroself-join.

SELECT   E1.BusinessEntityID,
         E2.BusinessEntityID,
         P.FirstName + ' ' + p.LastName AS FullName,
         E1.BirthDate
FROM     HumanResources.Employee AS E1
         INNER JOIN
         Person.Person AS P
         ON P.BusinessEntityID = E1.BusinessEntityID
         INNER JOIN
         HumanResources.Employee AS E2
         ON E2.BirthDate = E1.BirthDate
            AND E2.BusinessEntityID <> E1.BusinessEntityID
ORDER BY E1.BirthDate, FullName

Una vez ejecutes esta consulta verás que hay menos filas en los resultados, y los que quedan son realmente los duplicados.

Como se trata de una petición del trabajo, vamos a limpiar la consulta para mostrar únicamente la información solicitada.

Paso 4 - Toques Finales

Vamos a deshacernos de los valores BusinessEntityID en la consulta. Solo lo mostrábamos para ayudar, pero ya no nos sirven de nada.

Esta sí que sí, es la consulta final.

SELECT   P.FirstName + ' ' + p.LastName AS FullName,
         E1.BirthDate
FROM     HumanResources.Employee AS E1
         INNER JOIN
         Person.Person AS P
         ON P.BusinessEntityID = E1.BusinessEntityID
         INNER JOIN
         HumanResources.Employee AS E2
         ON E2.BirthDate = E1.BirthDate
            AND E2.BusinessEntityID <> E1.BusinessEntityID
ORDER BY E1.BirthDate, FullName

Y aquí están los resultados que se pueden presentar a la Gerente de Recursos Humanos

COMPARTE ESTE ARTÍCULO

COMPARTIR EN FACEBOOK
COMPARTIR EN TWITTER
COMPARTIR EN LINKEDIN
COMPARTIR EN WHATSAPP