SELECT *
FROM reservation
WHERE id NOT IN (select reservation_id
FROM reservation_log
WHERE change_type = 'cancel')
OU:
SELECT r.*
FROM reservation r
LEFT JOIN reservation_log l ON r.id = l.reservation_id AND l.change_type = 'cancel'
WHERE l.id IS NULL
A primeira versão é mais intuitiva, mas acho que a segunda versão geralmente obtém melhor desempenho (supondo que você tenha índices nas colunas usadas na junção).
A segunda versão funciona porque
LEFT JOIN
retorna uma linha para todas as linhas da primeira tabela. Quando o ON
condição for bem-sucedida, essas linhas incluirão as colunas da segunda tabela, assim como INNER JOIN
. Quando a condição falhar, a linha retornada conterá NULL
para todas as colunas da segunda tabela. O WHERE l.id IS NULL
test então corresponde a essas linhas, então ele encontra todas as linhas que não têm uma correspondência entre as tabelas.