Aquí hay once opciones para devolver filas duplicadas en Oracle Database cuando esas filas tienen una clave principal o alguna otra columna de identificador único y desea ignorarlo.
Datos de muestra
Usaremos los siguientes datos para nuestros ejemplos:
SELECT * FROM Dogs;
Resultado:
DOGID | NOMBRE | APELLIDO |
---|---|---|
1 | Ladrar | Smith |
2 | Ladrar | Smith |
3 | Guau | Jones |
4 | Ruff | Robinson |
5 | Meneo | Johnson |
6 | Meneo | Johnson |
7 | Meneo | Johnson |
Las primeras dos filas son duplicados y las últimas tres filas son duplicados. Las filas duplicadas comparten exactamente los mismos valores en todas las columnas con la excepción de su clave principal/columna de ID única.
La columna de clave principal garantiza que no haya filas duplicadas, lo cual es una buena práctica en RDBMS, porque las claves principales ayudan a reforzar la integridad de los datos. Pero el hecho de que las claves primarias contengan valores únicos significa que debemos ignorar esa columna al buscar duplicados.
En nuestra tabla anterior, la columna de clave principal es un número creciente, y su valor no tiene ningún significado y no es significativo. Por lo tanto, podemos ignorar los datos de esa columna cuando buscamos duplicados.
Opción 1
Esta es nuestra primera opción para devolver duplicados:
SELECT
FirstName,
LastName,
COUNT(*) AS Count
FROM Dogs
GROUP BY FirstName, LastName
ORDER BY Count DESC;
Resultado:
NOMBRE | APELLIDO | CONTAR |
---|---|---|
Meneo | Johnson | 3 |
Ladrar | Smith | 2 |
Ruff | Robinson | 1 |
Guau | Jones | 1 |
Aquí construimos nuestra consulta con GROUP BY
cláusula para que la salida se agrupe por las columnas relevantes. También usamos el COUNT()
función para devolver el número de filas idénticas. Y lo ordenamos por conteo en orden descendente para que los duplicados aparezcan primero.
El resultado nos dice que hay tres filas que contienen a Wag Johnson y dos filas que contienen a Bark Smith. Estos son duplicados (o triplicados en el caso de Wag Johnson). Las otras dos filas no tienen duplicados.
Opción 2
Podemos agregar el HAVING
cláusula a nuestro ejemplo anterior para excluir no duplicados de la salida:
SELECT
FirstName,
LastName,
COUNT(*) AS Count
FROM Dogs
GROUP BY FirstName, LastName
HAVING COUNT(*) > 1
ORDER BY Count DESC;
Resultado:
NOMBRE | APELLIDO | CONTAR |
---|---|---|
Meneo | Johnson | 3 |
Ladrar | Smith | 2 |
Opción 3
También podemos buscar duplicados en columnas concatenadas. En este caso usamos el DISTINCT
palabra clave para obtener valores distintos, luego use COUNT()
función para devolver el conteo:
SELECT
DISTINCT FirstName || ' ' || LastName AS DogName,
COUNT(*) AS Count
FROM Dogs
GROUP BY FirstName || ' ' || LastName
ORDER BY Count DESC;
Resultado:
NOMBRE DE PERRO | CONTAR |
---|---|
Wag Johnson | 3 |
Herrero de ladridos | 2 |
Ruff Robinson | 1 |
Guau Jones | 1 |
Opción 4
Cada fila en Oracle tiene un rowid
pseudocolumna que devuelve la dirección de la fila. El rowid
es un identificador único para las filas de la tabla y, por lo general, su valor identifica de forma única una fila en la base de datos (aunque es importante tener en cuenta que las filas de diferentes tablas que se almacenan juntas en el mismo clúster pueden tener el mismo rowid
).
De todos modos, podemos construir una consulta que use el rowid
si queremos:
SELECT * FROM Dogs
WHERE EXISTS (
SELECT 1 FROM Dogs d2
WHERE Dogs.FirstName = d2.FirstName
AND Dogs.LastName = d2.LastName
AND Dogs.rowid > d2.rowid
);
Resultado:
DOGID | NOMBRE | APELLIDO |
---|---|---|
2 | Ladrar | Smith |
6 | Meneo | Johnson |
7 | Meneo | Johnson |
Podríamos reemplazar el SELECT *
con DELETE
para realizar una operación de eliminación de duplicados en la tabla.
Tenga en cuenta que podríamos haber usado el DogId
columna (nuestra clave principal) en lugar de rowid
si quisiéramos. Dicho esto, el rowid
puede ser útil si no puede usar la columna de clave principal por algún motivo, o si la tabla no tiene una clave principal.
Opción 5
Aquí hay otra consulta que usa el rowid
:
SELECT * FROM Dogs
WHERE rowid > (
SELECT MIN(rowid) FROM Dogs d2
WHERE Dogs.FirstName = d2.FirstName
AND Dogs.LastName = d2.LastName
);
Resultado:
DOGID | NOMBRE | APELLIDO |
---|---|---|
2 | Ladrar | Smith |
6 | Meneo | Johnson |
7 | Meneo | Johnson |
Al igual que en el ejemplo anterior, podríamos reemplazar el SELECT *
con DELETE
para eliminar las filas duplicadas.
Opción 6
Los dos rowid
Las opciones anteriores son excelentes si debe ignorar por completo la clave principal en su consulta (o si no tiene una columna de clave principal). Sin embargo, como se mencionó, todavía existe la opción de reemplazar rowid
con la columna de la clave principal, en nuestro caso, el DogId
columna:
SELECT * FROM Dogs
WHERE EXISTS (
SELECT 1 FROM Dogs d2
WHERE Dogs.FirstName = d2.FirstName
AND Dogs.LastName = d2.LastName
AND Dogs.DogId > d2.DogId
);
Resultado:
DOGID | NOMBRE | APELLIDO |
---|---|---|
2 | Ladrar | Smith |
6 | Meneo | Johnson |
7 | Meneo | Johnson |
Opción 7
Y aquí está la otra consulta con rowid
reemplazado por el DogId
columna:
SELECT * FROM Dogs
WHERE DogId > (
SELECT MIN(DogId) FROM Dogs d2
WHERE Dogs.FirstName = d2.FirstName
AND Dogs.LastName = d2.LastName
);
Resultado:
DOGID | NOMBRE | APELLIDO |
---|---|---|
2 | Ladrar | Smith |
6 | Meneo | Johnson |
7 | Meneo | Johnson |
Opción 8
Otra forma de encontrar duplicados es usar ROW_NUMBER()
función de ventana:
SELECT
DogId,
FirstName,
LastName,
ROW_NUMBER() OVER (
PARTITION BY FirstName, LastName
ORDER BY FirstName, LastName
) AS row_num
FROM Dogs;
Resultado:
DOGID | NOMBRE | APELLIDO | ROW_NUM |
---|---|---|---|
1 | Ladrar | Smith | 1 |
2 | Ladrar | Smith | 2 |
4 | Ruff | Robinson | 1 |
7 | Meneo | Johnson | 1 |
5 | Meneo | Johnson | 2 |
6 | Meneo | Johnson | 3 |
3 | Guau | Jones | 1 |
Usando la PARTITION
La cláusula da como resultado que se agregue una nueva columna, con un número de fila que aumenta cada vez que hay un duplicado, pero se restablece nuevamente cuando hay una fila única.
En este caso, no agrupamos los resultados, lo que significa que podemos ver cada fila duplicada, incluida su columna de identificador único.
Opción 9
También podemos usar el ejemplo anterior como una expresión de tabla común en una consulta más grande:
WITH cte AS
(
SELECT
DogId,
FirstName,
LastName,
ROW_NUMBER() OVER (
PARTITION BY FirstName, LastName
ORDER BY FirstName, LastName
) AS row_num
FROM Dogs
)
SELECT * FROM cte WHERE row_num <> 1;
Resultado:
DOGID | NOMBRE | APELLIDO | ROW_NUM |
---|---|---|---|
2 | Ladrar | Smith | 2 |
5 | Meneo | Johnson | 2 |
6 | Meneo | Johnson | 3 |
Esa consulta excluye los no duplicados de la salida y excluye una fila de cada duplicado de la salida.
Opción 10
Aquí hay otra forma de obtener el mismo resultado que el ejemplo anterior:
SELECT * FROM Dogs
WHERE DogId IN (
SELECT DogId FROM Dogs
MINUS SELECT MIN(DogId) FROM Dogs
GROUP BY FirstName, LastName
);
Resultado:
DOGID | NOMBRE | APELLIDO |
---|---|---|
2 | Ladrar | Smith |
6 | Meneo | Johnson |
7 | Meneo | Johnson |
Este ejemplo utiliza MINUS
de Oracle operador, que devuelve solo filas únicas devueltas por la primera consulta pero no por la segunda.
El MINUS
El operador es similar al EXCEPT
operador en otros DBMS, como SQL Server, MariaDB, PostgreSQL y SQLite.
Opción 11
Aquí hay otra opción para seleccionar duplicados de nuestra tabla:
SELECT *
FROM Dogs d1, Dogs d2
WHERE d1.FirstName = d2.FirstName
AND d1.LastName = d2.LastName
AND d1.DogId <> d2.DogId
AND d1.DogId = (
SELECT MAX(DogId)
FROM Dogs d3
WHERE d3.FirstName = d1.FirstName
AND d3.LastName = d1.LastName
);
Resultado:
DOGID | NOMBRE | APELLIDO | DOGID | NOMBRE | APELLIDO |
---|---|---|---|---|---|
2 | Ladrar | Smith | 1 | Ladrar | Smith |
7 | Meneo | Johnson | 5 | Meneo | Johnson |
7 | Meneo | Johnson | 6 | Meneo | Johnson |