2010-08-17 22 views
17

Tengo filas en una tabla de base de datos Oracle que deben ser únicas para una combinación de dos campos pero la restricción única no está configurada en la tabla, así que necesito encontrar todas las filas que violen la restricción yo mismo usando SQL. Lamentablemente, mis escasas habilidades SQL no están a la altura de la tarea.SQL: ¿Cómo encontrar duplicados basados ​​en dos campos?

Mi tabla tiene tres columnas que son relevantes: entity_id, station_id y obs_year. Para cada fila, la combinación de station_id y obs_year debe ser única, y quiero averiguar si hay filas que violan esto al eliminarlas con una consulta SQL.

He probado el siguiente código SQL (sugerido por this previous question), pero no funciona para mí (me da ORA-00918 columna ambiguamente definido):

SELECT 
entity_id, station_id, obs_year 
FROM 
mytable t1 
INNER JOIN (
SELECT entity_id, station_id, obs_year FROM mytable 
GROUP BY entity_id, station_id, obs_year HAVING COUNT(*) > 1) dupes 
ON 
t1.station_id = dupes.station_id AND 
t1.obs_year = dupes.obs_year 

Puede alguien sugerir lo que estoy haciendo mal y/o cómo resolver esto?

Respuesta

33
SELECT * 
FROM (
     SELECT t.*, ROW_NUMBER() OVER (PARTITION BY station_id, obs_year ORDER BY entity_id) AS rn 
     FROM mytable t 
     ) 
WHERE rn > 1 
+0

Muchas gracias por esta respuesta. Lamentablemente, cuando ejecuto esto recibo un mensaje "ORA-00923: palabra clave FROM no encontrada donde esperaba". –

+0

@James: inténtalo ahora – Quassnoi

+0

En mssql tenía que poner 'como x' (el nombre no importa) detrás de la paranthesis FROM() para que funcione. ¡Gran respuesta! – Mafii

2

Re-escritura de la consulta

SELECT 
t1.entity_id, t1.station_id, t1.obs_year 
FROM 
mytable t1 
INNER JOIN (
SELECT entity_id, station_id, obs_year FROM mytable 
GROUP BY entity_id, station_id, obs_year HAVING COUNT(*) > 1) dupes 
ON 
t1.station_id = dupes.station_id AND 
t1.obs_year = dupes.obs_year 

creo que el error de la columna ambigua (ORA-00918) fue porque eras select columnas ing cuyos nombres aparecían en la tabla y la subconsulta, pero no lo hicieron especifique si lo desea desde dupes o desde mytable (con alias t1).

1

¿No podría crear una tabla nueva que incluya la restricción única y luego copiar a través de los datos fila por fila, ignorando fallas?

+0

Sí, esta es una buena idea, ¡gracias! BTW Estoy intentando descubrir cómo crear la restricción en mi tabla usando anotaciones en mi clase de entidad (soy un desarrollador de Java usando JPA/Hibernate), vea http://stackoverflow.com/questions/3504477/ how-to-specify-that-a-combination-of-columns-should-be-a-unique-constraint-using –

2

Cambiar las 3 campos en el selecto inicial para ser

SELECT 
t1.entity_id, t1.station_id, t1.obs_year 
10
SELECT entity_id, station_id, obs_year 
FROM mytable t1 
WHERE EXISTS (SELECT 1 from mytable t2 Where 
     t1.station_id = t2.station_id 
     AND t1.obs_year = t2.obs_year 
     AND t1.RowId <> t2.RowId) 
+0

Parece que no podemos hacer esto en una vista: ORA-01445: no se puede seleccionar ROWID de, o muestra, una vista de unión sin una tabla clave conservada – Thyag

1

Es necesario especificar la tabla de las columnas de la principal selección. Además, suponiendo que entity_id es la clave única para mytable y es irrelevante para encontrar duplicados, no debería agruparse en la subconsulta dupes.

Probar:

SELECT t1.entity_id, t1.station_id, t1.obs_year 
FROM mytable t1 
INNER JOIN (
SELECT station_id, obs_year FROM mytable 
GROUP BY station_id, obs_year HAVING COUNT(*) > 1) dupes 
ON 
t1.station_id = dupes.station_id AND 
t1.obs_year = dupes.obs_year 
+0

Gracias, Mark, por la sugerencia de no usar entity_id en la subconsulta de agrupación y para el ejemplo ilustrativo. –

0
SELECT * 
FROM (
     SELECT t.*, ROW_NUMBER() OVER (PARTITION BY station_id, obs_year ORDER BY entity_id) AS rn 
     FROM mytable t 
     ) 
WHERE rn > 1 

por Quassnoi es el más eficiente para tablas grandes. Tenía este análisis de coste:

SELECT a.dist_code, a.book_date, a.book_no 
FROM trn_refil_book a 
WHERE EXISTS (SELECT 1 from trn_refil_book b Where 
     a.dist_code = b.dist_code and a.book_date = b.book_date and a.book_no = b.book_no 
     AND a.RowId <> b.RowId) 
     ; 

dio un coste de 1322341

SELECT a.dist_code, a.book_date, a.book_no 
FROM trn_refil_book a 
INNER JOIN (
SELECT b.dist_code, b.book_date, b.book_no FROM trn_refil_book b 
GROUP BY b.dist_code, b.book_date, b.book_no HAVING COUNT(*) > 1) c 
ON 
a.dist_code = c.dist_code and a.book_date = c.book_date and a.book_no = c.book_no 
; 

dio un coste de 1271699

mientras

SELECT dist_code, book_date, book_no 
FROM (
     SELECT t.dist_code, t.book_date, t.book_no, ROW_NUMBER() OVER (PARTITION BY t.book_date, t.book_no 
      ORDER BY t.dist_code) AS rn 
     FROM trn_refil_book t 
     ) p 
WHERE p.rn > 1 
; 

dio un coste de

La mesa no fue indexada ....

+0

formatea tu respuesta correctamente. – SSP

0
SELECT entity_id, station_id, obs_year 
    FROM mytable 
GROUP BY entity_id, station_id, obs_year 
HAVING COUNT(*) > 1 

especificar los campos para encontrar duplicados tanto en el SELECT y el GROUP BY.

Funciona mediante el uso de GROUP BY para buscar las filas que coincidan con cualquier otra fila en función de las Columnas especificadas. HAVING COUNT(*) > 1 dice que solo estamos interesados ​​en ver las filas que se producen más de 1 vez (y por lo tanto son duplicados)

+0

Hola, esto bien puede resolver el problema ... pero sería bueno si pudieras dar una pequeña explicación sobre cómo y por qué funciona :) No lo olvides: hay montones de novatos en el desbordamiento de pila, y podrían aprende una o dos cosas de tu experiencia; lo que es obvio para ti podría no serlo para ellos. –

+0

Gracias Taryn. Funciona al usar GROUP BY para buscar filas que coincidan con otras filas basadas en las Columnas especificadas. El TENER COUNT (*)> 1 dice que solo estamos interesados ​​en ver las filas que ocurren más de 1 vez (y por lo tanto son duplicados) – grokster

+0

Hola, no me digas (en los comentarios). Sé SQL, no estoy preguntando por mí ... Este tipo de explicación es "parte de tu respuesta completa" ... así que edita tu respuesta y agrégala allí. :) –

Cuestiones relacionadas