Hier is een oplossing. Ik heb dit getest op 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;
Ik neem c2.notes op als een GROUP BY-sleutel omdat je mogelijk meer dan één rij hebt met niet-null-notities per waarden van driver_id,car_id.
Resultaat met uw voorbeeldgegevens:
+------+-----------+--------+-------+
| id | driver_id | car_id | notes |
+------+-----------+--------+-------+
| 2 | 1 | 1 | NULL |
| 4 | 2 | 1 | NULL |
| 8 | 3 | 2 | hi |
| 9 | 5 | 3 | NULL |
+------+-----------+--------+-------+
Wat betreft het verwijderen. In uw voorbeeldgegevens is dit altijd de hoogste id-waarde per driver_id &car_id die u wilt behouden. Als je daarop kunt vertrouwen, kun je een multi-table delete doen die alle rijen verwijdert waarvoor een rij met een hogere id-waarde en dezelfde driver_id &car_id bestaat:
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;
Dit slaat natuurlijk alle gevallen over waarin slechts één rij bestaat met een bepaald paar driver_id &car_id-waarden, omdat de voorwaarden van de inner join twee rijen met verschillende id-waarden vereisen.
Maar als u er niet op kunt vertrouwen dat de laatste id per groep degene is die u wilt behouden, is de oplossing complexer. Het is waarschijnlijk ingewikkelder dan het waard is om in één verklaring op te lossen, dus doe het in twee verklaringen.
Ik heb dit ook getest, nadat ik nog een paar rijen had toegevoegd om te testen:
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 |
+----+--------+-----------+-------+
Verwijder eerst rijen met null-notities, waar een rij met niet-null-notities bestaat.
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 |
+----+--------+-----------+-------+
Ten tweede, verwijder alle rijen behalve de rij met het hoogste ID uit elke groep duplicaten.
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 |
+----+--------+-----------+-------+