Hier ist eine Lösung. Ich habe dies auf MySQL 5.5.8 getestet.
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;
Ich füge c2.notes als GROUP BY-Schlüssel hinzu, da Sie möglicherweise mehr als eine Zeile mit Nicht-Null-Notizen pro Wert von driver_id,car_id haben.
Ergebnis mit Ihren Beispieldaten:
+------+-----------+--------+-------+
| id | driver_id | car_id | notes |
+------+-----------+--------+-------+
| 2 | 1 | 1 | NULL |
| 4 | 2 | 1 | NULL |
| 8 | 3 | 2 | hi |
| 9 | 5 | 3 | NULL |
+------+-----------+--------+-------+
Apropos löschen. In Ihren Beispieldaten ist es immer der höchste ID-Wert pro Fahrer-ID und Auto-ID, den Sie behalten möchten. Wenn Sie sich darauf verlassen können, können Sie eine Multi-Table-Löschung durchführen, die alle Zeilen löscht, für die eine Zeile mit einem höheren ID-Wert und derselben Fahrer-ID und derselben Auto-ID existiert:
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;
Dadurch werden natürlich alle Fälle übersprungen, in denen nur eine Zeile mit einem gegebenen Wertepaar von driver_id und car_id vorhanden ist, da die Bedingungen des inneren Joins zwei Zeilen mit unterschiedlichen id-Werten erfordern.
Wenn Sie sich jedoch nicht darauf verlassen können, dass die neueste ID pro Gruppe diejenige ist, die Sie behalten möchten, ist die Lösung komplexer. Es ist wahrscheinlich komplexer, als es wert ist, es in einer Anweisung zu lösen, also tun Sie es in zwei Anweisungen.
Ich habe dies auch getestet, nachdem ich ein paar weitere Zeilen zum Testen hinzugefügt hatte:
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 |
+----+--------+-----------+-------+
Löschen Sie zuerst Zeilen mit Null-Notizen, wo eine Zeile mit Nicht-Null-Notizen vorhanden ist.
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 |
+----+--------+-----------+-------+
Zweitens, löschen Sie alle bis auf die Zeile mit der höchsten ID aus jeder Gruppe von Duplikaten.
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 |
+----+--------+-----------+-------+