Explicação:
No primeiro caso, quando vc utiliza o “exists”, a subconsulta será executada para cada uma das linhas “resultado” dos joins anteriores, normalmente apresenta melhor desempenho em consultas que se precisa verificar se existe qualquer linha, sem se importar com a quantidade de linhas que serão retornadas na subconsulta, pois ela “para” e retorna ‘true’ quando encontra o primeiro resultado.
Já no segundo caso, quando você utiliza o IN, a subconsulta será executada 1 vez (por não existir parâmetros das consultas externas dentro da subconsulta, embora alguns poucos SGBDs executem uma vez para cada linha resultado dos joins, normalmente em versões antigas), e essa vez que será executada, será depois dos resultados dos joins (pode até ser em paralelo, mas a verificação do IN será depois dos joins). Esse caso costuma ter desempenho melhor quando você precisa aplicar filtros a outras subconsultas (por exemplo, um agrupamento necessariamente antes do filtro no IN, ou um conjunto de joins que precisam ser feitos exclusivamente antes do filtro no IN)
O terceiro caso, o banco vai fazer a subconsulta e o join “em paralelo” aos outros joins, escolhendo a melhor opção de acordo com as estatísticas interna dele. Normalmente ele faz os joins das tabelas com menores quantidades de registros, mas não é regra, as estatísticas dele são mais complexas que isso. Dessa forma, ele só vai cruzar os resultados das consultas que não pareça ser o caminho mais oneroso para ele. (por exemplo, se eu tenho a tabela A com 1000000 de registros, a B com 1000000, etc… mas a AL com 1000 registros, ele provavelmente vai executar primeiro o cruzamento com essas, antes de cruzar as outras, pois provavelmente vai reduzir a quantidade de linhas comparadas com as outras tabelas)