Oracle
 sql >> Base de Dados >  >> RDS >> Oracle

Convertendo NOT IN em NOT EXISTS


É bastante simples, quando você pega o jeito:
SELECT s.S_Fname, s.S_Lname
FROM STUDENT s
WHERE s.S_Sex = 'F'
AND S.S_Id NOT IN(SELECT e.S_Id           -- take this line
        FROM ENROLLMENT e
        WHERE e.Mark < 70);

Essa linha basicamente compara S.S_Id com todos os e.S_Id valores que vêm da subconsulta.

Agora mude para NOT EXISTS e coloque uma verificação de igualdade S.S_Id = e.S_Id , dentro da subconsulta:
SELECT s.S_Fname, s.S_Lname
FROM STUDENT s
WHERE s.S_Sex = 'F'
AND NOT EXISTS (SELECT e.S_Id          
        FROM ENROLLMENT e
        WHERE (e.Mark < 70)       -- if this is complex, you'll need parentheses
        AND S.S_Id = e.S_Id);

A menor mudança possível é perceber que (SELECT e.S_Id ... não precisa realmente do e.S_Id . Subconsultas com EXISTS e NOT EXISTS apenas verifique se há linhas retornadas ou não e os valores das colunas não importam. Você pode colocar SELECT * ou uma constante lá (SELECT 1 é comum) ou SELECT NULL ou mesmo SELECT 1/0 (Sim, isso vai funcionar!):
SELECT s.S_Fname, s.S_Lname
FROM STUDENT s
WHERE s.S_Sex = 'F'
AND NOT EXISTS (SELECT 1
        FROM ENROLLMENT e
        WHERE e.Mark < 70  
        AND S.S_Id = e.S_Id);

Outra consideração importante é que, quando você faz a conversão dessa maneira, o (aparentemente equivalente) NOT EXISTS e NOT IN os escritos de uma consulta são realmente equivalentes apenas se ambos S_Id colunas não são anuláveis. Se o e.S_Id coluna é anulável, o NOT IN pode fazer com que a consulta inteira não retorne nenhuma linha (porque x NOT IN (a, b, c, ...) é equivalente a x<>a AND x<>b AND ... e essa condição não pode ser verdadeira quando um dos a,b,c... é NULL .)

Por motivos semelhantes, você terá resultados diferentes se o s.S_Id é anulável (isso não é muito provável neste caso, pois provavelmente é a chave primária, mas em outros casos é importante.)

Portanto, é quase sempre melhor usar NOT EXISTS , pois ele se comporta de maneira diferente, mesmo que qualquer coluna seja anulável (o S.S_Id = e.S_Id check descartará linhas com null anteriormente) e geralmente esse comportamento é o desejado. Há muitos detalhes na pergunta: NÃO ESTÁ vs NÃO EXISTE , na resposta de @Martin Smith. Você também encontrará maneiras de converter o NOT IN para NOT EXISTS e manter o comportamento nulo relacionado (desagradável).