Cómo encontrar duplicados en 2 columnas, no en 1

107

Tengo una tabla de base de datos MySQL con dos columnas que me interesan. Individualmente, cada uno puede tener duplicados, pero nunca deben tener un duplicado de AMBOS que tengan el mismo valor.

stone_idpuede tener duplicados siempre que cada upshargetítulo sea diferente y al revés. Pero digamos, por ejemplo, stone_id= 412 y upcharge_title= "zafiro", esa combinación solo debería ocurrir una vez.

Esto esta bien:

stone_id = 412 upcharge_title = "sapphire"
stone_id = 412 upcharge_title = "ruby"

Esto NO está bien:

stone_id = 412 upcharge_title = "sapphire"
stone_id = 412 upcharge_title = "sapphire"

¿Existe alguna consulta que encuentre duplicados en ambos campos? Y si es posible, ¿hay alguna manera de configurar mi base de datos para que no permita eso?

Estoy usando MySQL versión 4.1.22

JD Isaacks
fuente

Respuestas:

192

Debe configurar una clave compuesta entre los dos campos. Esto requerirá un stone_id y upcharge_title únicos para cada fila.

En cuanto a encontrar los duplicados existentes, intente esto:

select   stone_id,
         upcharge_title,
         count(*)
from     your_table
group by stone_id,
         upcharge_title
having   count(*) > 1
Codificador Miyagi
fuente
Gracias, eso los selecciona. ¿Podría ser tan amable de decirme cómo eliminar duplicados (pero dejar 1 copia por supuesto) GRACIAS !!
JD Isaacks
2
Una forma sería tomar todos los datos distintos y volver a crear la tabla.
Miyagi Coder
1
@John Isaacks: Si no hay otros campos con los que pueda distinguirlos (es decir, todos los campos son duplicados), tendrá que eliminar ambas filas y volver a crear una. Una forma sería copiar duplicados en una copia de la tabla, eliminarlos del original y reinsertar distintas filas de la copia.
P Daddy
Esto no funciona en postgres 8.1, ¿alguien podría echarme una mano al respecto?
Lennon
muchas gracias, ¿importa el orden que agrupan?
Andrew
35

Encontré útil agregar un índice único usando un "ALTER IGNORE" que elimina los duplicados y refuerza los registros únicos que suenan como le gustaría hacer. Entonces la sintaxis sería:

ALTER IGNORE TABLE `table` ADD UNIQUE INDEX(`id`, `another_id`, `one_more_id`);

Esto agrega efectivamente la restricción única, lo que significa que nunca tendrá registros duplicados y IGNORE elimina los duplicados existentes.

Puede leer más sobre eh ALTER IGNORE aquí: http://mediakey.dk/~cc/mysql-remove-duplicate-entries/

Actualización: @Inquisitive me informó que esto puede fallar en las versiones de MySql> 5.5:

Falla en MySQL> 5.5 y en la tabla InnoDB, y en Percona debido a su función de creación de índice rápido InnoDB [ http://bugs.mysql.com/bug.php?id=40344] . En este caso, primero ejecute set session old_alter_table=1y luego el comando anterior funcionará bien

Actualización: ALTER IGNOREeliminada en 5.7

De los documentos

A partir de MySQL 5.6.17, la cláusula IGNORE está obsoleta y su uso genera una advertencia. IGNORE se elimina en MySQL 5.7.

Uno de los desarrolladores de MySQL ofrece dos alternativas :

  • Agrupe por campos únicos y elimínelos como se ve arriba
  • Cree una nueva tabla, agregue un índice único, use INSERT IGNORE, por ejemplo:
CREATE TABLE duplicate_row_table LIKE regular_row_table;
ALTER TABLE duplicate_row_table ADD UNIQUE INDEX (id, another_id);
INSERT IGNORE INTO duplicate_row_table SELECT * FROM regular_row_table;
DROP TABLE regular_row_table;
RENAME TABLE duplicate_row_table TO regular_row_table;

Pero dependiendo del tamaño de su mesa, esto puede no ser práctico

SeanDowney
fuente
1
Es cierto, pero al menos para la próxima vez que lo sepas. Tuve el mismo problema y pensé que era bueno compartirlo con otros
SeanDowney
Solo estaba bromeando sobre el retraso de 3 años. Realmente me alegro de que lo hayas compartido. De ahí el plus 1.
JD Isaacks
Imagino que esto elimina uno de los duplicados arbitrariamente, así que asegúrese de que no haya datos diferentes entre cada fila que pueda ser útil conocer o conservar.
Joshua Pinter
+1 para la respuesta incluso después de 2 años de retraso. Eliminé accidentalmente una clave compuesta y esto fue un salvavidas. Gracias
ivcode
He probado algunas técnicas de búsqueda de duplicados y ninguna de ellas fue tan simple y rápida. Gracias por compartir este método.
Kristjan O.
8

Puedes encontrar duplicados como este ...

Select
    stone_id, upcharge_title, count(*)
from 
    particulartable
group by 
    stone_id, upcharge_title
having 
    count(*) > 1
Jason Punyon
fuente
4

Para encontrar los duplicados:

select stone_id, upcharge_title from tablename group by stone_id, upcharge_title having count(*)>1

Para limitar y evitar esto en el futuro, cree una clave única compuesta en estos dos campos.

Ian Nelson
fuente
1
Muchas gracias, ¿podría decirme cómo eliminar todos los duplicados menos uno? ¿Y cómo configuro una clave compisite en phpmyadmin. ¡¡¡GRACIAS!!!
JD Isaacks
3

Por cierto, una restricción única compuesta sobre la mesa evitaría que esto ocurriera en primer lugar.

ALTER TABLE table
    ADD UNIQUE(stone_id, charge_title)

(Esto es T-SQL válido. No estoy seguro acerca de MySQL).

Papi
fuente
1
Creo que funciona, pero no me dejará hacerlo hasta que primero elimine los duplicados. Gracias.
JD Isaacks
1

esta publicación SO me ayudó, pero yo también quería saber cómo eliminar y mantener una de las filas ... aquí hay una solución PHP para eliminar las filas duplicadas y mantener una (en mi caso, solo había 2 columnas y está en un función para borrar asociaciones de categorías duplicadas)

$dupes = $db->query('select *, count(*) as NUM_DUPES from PRODUCT_CATEGORY_PRODUCT group by fkPRODUCT_CATEGORY_ID, fkPRODUCT_ID having count(*) > 1');
if (!is_array($dupes))
    return true;
foreach ($dupes as $dupe) {
    $db->query('delete from PRODUCT_CATEGORY_PRODUCT where fkPRODUCT_ID = ' . $dupe['fkPRODUCT_ID'] . ' and fkPRODUCT_CATEGORY_ID = ' . $dupe['fkPRODUCT_CATEGORY_ID'] . ' limit ' . ($dupe['NUM_DUPES'] - 1);
}

el (límite NUM_DUPES - 1) es lo que conserva la fila única ...

gracias a todos

néctar
fuente
3
ALTER IGNORE TABLE table ADD UNIQUE INDEX index_name(stone_id, charge_title)eliminará filas duplicadas dejando solo un par único.
dev-null-dweller