Estou tentando aprender SQL e tenho dificuldade em entender as instruções EXISTS. Me deparei com esta citação sobre "existe" e não entendi algo:
Usando o operador exists, sua subconsulta pode retornar zero, uma ou várias linhas, e a condição simplesmente verifica se a subconsulta retornou alguma linha. Se você olhar a cláusula select da subconsulta, verá que ela consiste em um único literal (1); uma vez que a condição na consulta contida precisa apenas saber quantas linhas foram retornadas, os dados reais que a subconsulta retornou são irrelevantes.
O que não entendo é como a consulta externa sabe qual linha a subconsulta está verificando? Por exemplo:
SELECT *
FROM suppliers
WHERE EXISTS (select *
from orders
where suppliers.supplier_id = orders.supplier_id);
Eu entendo que se o id do fornecedor e da tabela de pedidos corresponderem, a subconsulta retornará verdadeiro e todas as colunas da linha correspondente na tabela de fornecedores serão geradas. O que não entendo é como a subconsulta comunica qual linha específica (digamos, a linha com o id do fornecedor 25) deve ser impressa se apenas um verdadeiro ou falso estiver sendo retornado.
Parece-me que não há relação entre a consulta externa e a subconsulta.
O que você acha que a cláusula WHERE dentro do exemplo EXISTS está fazendo? Como você chega a essa conclusão quando a referência SUPPLIERS não está nas cláusulas FROM ou JOIN dentro da cláusula EXISTS?
EXISTS é avaliado como TRUE / FALSE e sai como TRUE na primeira correspondência dos critérios - é por isso que pode ser mais rápido do que
IN
. Também esteja ciente de que a cláusula SELECT em um EXISTS é ignorada - IE:SELECT s.* FROM SUPPLIERS s WHERE EXISTS (SELECT 1/0 FROM ORDERS o WHERE o.supplier_id = s.supplier_id)
... deve atingir uma divisão por erro zero, mas não vai. A cláusula WHERE é a parte mais importante de uma cláusula EXISTS.
Esteja ciente também de que um JOIN não é uma substituição direta de EXISTS, porque haverá registros pai duplicados se houver mais de um registro filho associado ao pai.
fonte
EXISTS
saída, retornando TRUE na primeira correspondência - porque o fornecedor existe pelo menos uma vez na tabela ORDERS. Se você quiser ver a duplicação dos dados do FORNECEDOR por ter mais de um relacionamento filho em PEDIDOS, deverá usar JOIN. Mas a maioria não quer essa duplicação, e a execução de GROUP BY / DISTINCT pode adicionar sobrecarga à consulta.EXISTS
é mais eficiente do queSELECT DISTINCT ... FROM SUPPLIERS JOIN ORDERS ...
no SQL Server, não testei no Oracle ou MySQL recentemente.Você pode produzir resultados idênticos usando
JOIN
,EXISTS
,IN
ouINTERSECT
:SELECT s.supplier_id FROM suppliers s INNER JOIN (SELECT DISTINCT o.supplier_id FROM orders o) o ON o.supplier_id = s.supplier_id SELECT s.supplier_id FROM suppliers s WHERE EXISTS (SELECT * FROM orders o WHERE o.supplier_id = s.supplier_id) SELECT s.supplier_id FROM suppliers s WHERE s.supplier_id IN (SELECT o.supplier_id FROM orders o) SELECT s.supplier_id FROM suppliers s INTERSECT SELECT o.supplier_id FROM orders o
fonte
Se você tivesse uma cláusula where parecida com esta:
WHERE id in (25,26,27) -- and so on
você pode entender facilmente por que algumas linhas são retornadas e outras não.
Quando a cláusula where é assim:
WHERE EXISTS (select * from orders where suppliers.supplier_id = orders.supplier_id);
significa apenas: retornar linhas que possuem um registro existente na tabela de pedidos com o mesmo id.
fonte
Modelo de tabela de banco de dados
Vamos supor que temos as duas tabelas a seguir em nosso banco de dados, que formam uma relação de tabela um para muitos.
A
student
tabela é o pai e ostudent_grade
é a tabela filho, pois tem uma coluna de chave estrangeira student_id referenciando a coluna de chave primária id na tabela de alunos.O
student table
contém os dois registros a seguir:| id | first_name | last_name | admission_score | |----|------------|-----------|-----------------| | 1 | Alice | Smith | 8.95 | | 2 | Bob | Johnson | 8.75 |
E a
student_grade
tabela armazena as notas que os alunos receberam:| id | class_name | grade | student_id | |----|------------|-------|------------| | 1 | Math | 10 | 1 | | 2 | Math | 9.5 | 1 | | 3 | Math | 9.75 | 1 | | 4 | Science | 9.5 | 1 | | 5 | Science | 9 | 1 | | 6 | Science | 9.25 | 1 | | 7 | Math | 8.5 | 2 | | 8 | Math | 9.5 | 2 | | 9 | Math | 9 | 2 | | 10 | Science | 10 | 2 | | 11 | Science | 9.4 | 2 |
SQL EXISTS
Digamos que queremos todos os alunos que receberam nota 10 na aula de matemática.
Se estivermos interessados apenas no identificador do aluno, podemos executar uma consulta como esta:
SELECT student_grade.student_id FROM student_grade WHERE student_grade.grade = 10 AND student_grade.class_name = 'Math' ORDER BY student_grade.student_id
Mas, o aplicativo está interessado em exibir o nome completo de a
student
, não apenas o identificador, portanto, também precisamos das informações dastudent
tabela.Para filtrar os
student
registros que têm nota 10 em matemática, podemos usar o operador EXISTS SQL, assim:SELECT id, first_name, last_name FROM student WHERE EXISTS ( SELECT 1 FROM student_grade WHERE student_grade.student_id = student.id AND student_grade.grade = 10 AND student_grade.class_name = 'Math' ) ORDER BY id
Ao executar a consulta acima, podemos ver que apenas a linha Alice está selecionada:
| id | first_name | last_name | |----|------------|-----------| | 1 | Alice | Smith |
A consulta externa seleciona as
student
colunas de linha que estamos interessados em devolver ao cliente. No entanto, a cláusula WHERE está usando o operador EXISTS com uma subconsulta interna associada.O operador EXISTS retorna verdadeiro se a subconsulta retornar pelo menos um registro e falso se nenhuma linha for selecionada. O mecanismo de banco de dados não precisa executar a subconsulta inteiramente. Se um único registro for correspondido, o operador EXISTS retornará verdadeiro e a outra linha de consulta associada será selecionada.
A subconsulta interna é correlacionada porque a coluna student_id da
student_grade
tabela é comparada com a coluna id da tabela externa de aluno.fonte
EXIST
apenas com subconsulta correlacionada? Eu estava brincando com uma consulta contendo apenas 1 tabela, tipoSELECT id FROM student WHERE EXISTS (SELECT 1 FROM student WHERE student.id > 1)
. Eu sei que o que escrevi poderia ser alcançado por uma simples consulta WHERE, mas eu estava apenas usando para entender EXISTS. Eu tenho todas as linhas. É realmente devido ao fato de que eu não usei subconsulta correlacionada? Obrigado.EXISTS significa que a subconsulta retorna pelo menos uma linha, só isso. Nesse caso, é uma subconsulta correlacionada porque verifica o fornecedor_id da tabela externa para o fornecedor_id da tabela interna. Esta consulta diz, com efeito:
SELECIONE todos os fornecedores Para cada ID de fornecedor, veja se existe um pedido para este fornecedor Se o fornecedor não estiver presente na tabela de pedidos, remova o fornecedor dos resultados RETORNAR todos os fornecedores que possuem linhas correspondentes na tabela de pedidos
Você poderia fazer a mesma coisa neste caso com um INNER JOIN.
SELECT suppliers.* FROM suppliers INNER JOIN orders ON suppliers.supplier_id = orders.supplier_id;
O comentário dos pôneis está correto. Você precisaria fazer o agrupamento com essa junção ou selecionar distinto, dependendo dos dados de que precisa.
fonte
O que você descreve é uma chamada consulta com uma subconsulta correlacionada .
(Em geral) é algo que você deve tentar evitar escrevendo a consulta usando uma junção:
SELECT suppliers.* FROM suppliers JOIN orders USING supplier_id GROUP BY suppliers.supplier_id
Caso contrário, a subconsulta será executada para cada linha na consulta externa.
fonte
orders
que corresponda à condição de junção.