Tuve una situación en la que necesitaba actualizar o insertar en una tabla de acuerdo con dos campos (ambas claves externas) en los que no podía establecer una restricción ÚNICA (por lo que INSERT ... ON DUPLICATE KEY UPDATE no funcionará). Esto es lo que terminé usando:
replace into last_recogs (id, hasher_id, hash_id, last_recog)
select l.* from
(select id, hasher_id, hash_id, [new_value] from last_recogs
where hasher_id in (select id from hashers where name=[hasher_name])
and hash_id in (select id from hashes where name=[hash_name])
union
select 0, m.id, h.id, [new_value]
from hashers m cross join hashes h
where m.name=[hasher_name]
and h.name=[hash_name]) l
limit 1;
Este ejemplo se extrajo de una de mis bases de datos, con los parámetros de entrada (dos nombres y un número) reemplazados por [hasher_name], [hash_name] y [new_value]. El SELECT ... LIMIT 1 anidado extrae el primero del registro existente o un nuevo registro (last_recogs.id es una clave primaria de autoincremento) y lo usa como entrada de valor en REPLACE INTO.