Mysql
 sql >> Base de Dados >  >> RDS >> Mysql

Como selecionar e/ou excluir todas, exceto uma linha de cada conjunto de duplicatas em uma tabela?


Aqui está uma solução. Eu testei isso no MySQL 5.5.8.
SELECT MAX(COALESCE(c2.id, c1.id)) AS id,
 c1.driver_id, c1.car_id,
 c2.notes AS notes
FROM cars_drivers AS c1
LEFT OUTER JOIN cars_drivers AS c2
 ON (c1.driver_id,c1.car_id) = (c2.driver_id,c2.car_id) AND c2.notes IS NOT NULL
GROUP BY c1.driver_id, c1.car_id, c2.notes;

Eu incluo c2.notes como uma chave GROUP BY porque você pode ter mais de uma linha com notas não nulas por valores de driver_id,car_id.

Resultado usando seus dados de exemplo:
+------+-----------+--------+-------+
| id   | driver_id | car_id | notes |
+------+-----------+--------+-------+
|    2 |         1 |      1 | NULL  |
|    4 |         2 |      1 | NULL  |
|    8 |         3 |      2 | hi    |
|    9 |         5 |      3 | NULL  |
+------+-----------+--------+-------+

Sobre a exclusão. Em seus dados de exemplo, é sempre o valor de id mais alto por driver_id e car_id que você deseja manter. Se você puder depender disso, poderá fazer uma exclusão de várias tabelas que exclui todas as linhas para as quais existe uma linha com um valor de id mais alto e o mesmo driver_id e car_id:
DELETE c1 FROM cars_drivers AS c1 INNER JOIN cars_drivers AS c2
 ON (c1.driver_id,c1.car_id) = (c2.driver_id,c2.car_id) AND c1.id < c2.id;

Isso naturalmente ignora todos os casos em que existe apenas uma linha com um determinado par de valores driver_id e car_id, porque as condições da junção interna exigem duas linhas com valores de id diferentes.

Mas se você não puder depender de que o ID mais recente por grupo seja o que deseja manter, a solução é mais complexa. Provavelmente é mais complexo do que vale a pena resolver em uma instrução, então faça isso em duas instruções.

Eu testei isso também, depois de adicionar mais algumas linhas para teste:
INSERT INTO cars_drivers VALUES (10,2,3,NULL), (11,2,3,'bye');

+----+--------+-----------+-------+
| id | car_id | driver_id | notes |
+----+--------+-----------+-------+
|  1 |      1 |         1 | NULL  |
|  2 |      1 |         1 | NULL  |
|  3 |      1 |         2 | NULL  |
|  4 |      1 |         2 | NULL  |
|  5 |      2 |         3 | NULL  |
|  6 |      2 |         3 | NULL  |
|  7 |      2 |         3 | NULL  |
|  8 |      2 |         3 | hi    |
|  9 |      3 |         5 | NULL  |
| 10 |      2 |         3 | NULL  |
| 11 |      2 |         3 | bye   |
+----+--------+-----------+-------+

Primeiro, exclua as linhas com notas nulas, onde existe uma linha com notas não nulas.
DELETE c1 FROM cars_drivers AS c1 INNER JOIN cars_drivers AS c2
 ON (c1.driver_id,c1.car_id) = (c2.driver_id,c2.car_id)
WHERE c1.notes IS NULL AND c2.notes IS NOT NULL;

+----+--------+-----------+-------+
| id | car_id | driver_id | notes |
+----+--------+-----------+-------+
|  1 |      1 |         1 | NULL  |
|  2 |      1 |         1 | NULL  |
|  3 |      1 |         2 | NULL  |
|  4 |      1 |         2 | NULL  |
|  8 |      2 |         3 | hi    |
|  9 |      3 |         5 | NULL  |
| 11 |      2 |         3 | bye   |
+----+--------+-----------+-------+

Em segundo lugar, exclua todas as linhas, exceto a de maior id, de cada grupo de duplicatas.
DELETE c1 FROM cars_drivers AS c1 INNER JOIN cars_drivers AS c2
 ON (c1.driver_id,c1.car_id) = (c2.driver_id,c2.car_id) AND c1.id < c2.id;

+----+--------+-----------+-------+
| id | car_id | driver_id | notes |
+----+--------+-----------+-------+
|  2 |      1 |         1 | NULL  |
|  4 |      1 |         2 | NULL  |
|  9 |      3 |         5 | NULL  |
| 11 |      2 |         3 | bye   |
+----+--------+-----------+-------+