Ето едно решение. Тествах това на 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;
Включвам c2.notes като ключ GROUP BY, защото може да имате повече от един ред с ненулеви бележки за стойности на driver_id,car_id.
Резултат, използвайки вашите примерни данни:
+------+-----------+--------+-------+
| id | driver_id | car_id | notes |
+------+-----------+--------+-------+
| 2 | 1 | 1 | NULL |
| 4 | 2 | 1 | NULL |
| 8 | 3 | 2 | hi |
| 9 | 5 | 3 | NULL |
+------+-----------+--------+-------+
Относно изтриването. Във вашите примерни данни това винаги е най-високата стойност на идентификатор за driver_id и car_id, които искате да запазите. Ако можете да разчитате на това, можете да направите изтриване на няколко таблици, което изтрива всички редове, за които съществува ред с по-висока стойност на идентификатора и същия driver_id и 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;
Това естествено пропуска всички случаи, при които съществува само един ред с дадена двойка стойности driver_id и car_id, тъй като условията на вътрешното свързване изискват два реда с различни стойности на идентификатора.
Но ако не можете да разчитате на последния идентификатор на група, който искате да запазите, решението е по-сложно. Вероятно е по-сложно, отколкото си струва да се реши с едно твърдение, така че го направете с две изявления.
Тествах и това, след като добавих още няколко реда за тестване:
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 |
+----+--------+-----------+-------+
Първо изтрийте редове с нулеви бележки, където съществува ред с ненулеви бележки.
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 |
+----+--------+-----------+-------+
Второ, изтрийте всички редове с най-висок идентификатор от всяка група дубликати.
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 |
+----+--------+-----------+-------+