Questão de Banco de Dados — Consultas e Comandos em SQL — FGV 2024
Banco de Dados›Consultas e Comandos em SQL
Código
fg165278
Banca
FGV
Órgão
Pref Nova Iguaçu
Ano
2024
Cargo
AFTM (Pref N Iguaçu)
Seja um banco de dados relacional especificado em SQL de uma empresa de correspondência entre clientes, instituições financeiras e empréstimos contratados por esses clientes nessas instituições, previamente implementado em um banco de dados como a seguir:
OBS: Neste banco de dados, cadeias de caracteres (strings) são representadas envoltas em aspas simples.
Para fins de investigação, os auditores da empresa desejam saber os nomes dos clientes que contrataram empréstimos em todas as financeiras.
Assinale a consulta que apresenta o resultado desejado pelos auditores.
Aselect c.nome from tb_cliente c where exists ( select 1 from tb_emprestimo e where e.id_cliente = c.id_cliente )
Bselect c.nome from tb_cliente c where id_cliente = all ( select id_financeira from tb_emprestimo )
Cselect c.nome from tb_cliente c where not exists ( select id_financeira from tb_financeira except select e.id_financeira from tb_emprestimo e where e.id_cliente = c.id_cliente )
Dselect distinct c.nome from tb_financeira f natural join tb_emprestimo e full join tb_cliente c on c.id_cliente = e.id_cliente
Eselect distinct c.nome from tb_financeira natural join tb_emprestimo natural join tb_cliente c
Revelar gabarito e comentário▾
GabaritoC — select c.nome from tb_cliente c where not exists ( select id_financeira from tb_financeira except select e.id_financeira from tb_emprestimo e where e.id_cliente = c.id_cliente )
Comentário gerado por IA. É um apoio ao estudo, ancorado em fontes, mas pode conter imprecisões — confira sempre na fonte oficial (lei, súmula, edital e gabarito da banca). Encontrou um erro? Use “Reportar”.
Consultas SQL: Divisão Relacional e Quantificador Universal
Gabarito: letra C. A consulta correta usa a técnica de dupla negação (NOT EXISTS com subconsulta correlacionada) para implementar a divisão relacional — o cliente que contratou empréstimos em todas as financeiras é aquele para quem não existe nenhuma financeira na qual ele não tenha contratado empréstimo. Essa é a forma canônica de traduzir o quantificador universal "para todo" em SQL, que não possui um operador direto de divisão.
O problema pede uma consulta que responda a uma pergunta com quantificador universal: "clientes que contrataram empréstimos em todas as financeiras". Em SQL, não existe um operador nativo de divisão (como na álgebra relacional, onde há a operação de divisão). A solução clássica é usar a dupla negação: encontrar os clientes para os quais não existe uma financeira em que eles não tenham empréstimo. Isso é feito com uma subconsulta correlacionada que, para cada cliente, verifica se o conjunto de financeiras em que ele tem empréstimo é igual ao conjunto total de financeiras.
Vamos decompor a lógica da alternativa C, que é a correta:
A subconsulta interna select id_financeira from tb_financeira retorna o conjunto de todas as financeiras existentes.
A subconsulta select e.id_financeira from tb_emprestimo e where e.id_cliente = c.id_cliente retorna o conjunto de financeiras em que o cliente c (da consulta externa) tem empréstimo.
O operador except (diferença de conjuntos) subtrai o segundo conjunto do primeiro: o resultado são as financeiras em que o cliente não tem empréstimo.
O NOT EXISTS verifica se esse resultado é vazio. Se for vazio, significa que o cliente tem empréstimo em todas as financeiras, e ele é selecionado.
Essa é a tradução direta da divisão relacional: = clientes que têm empréstimo em todas as financeiras.
A pegadinha da banca está em confundir o quantificador universal ("todas") com o existencial ("alguma"). A alternativa A, por exemplo, usa EXISTS sem a negação, o que retorna clientes que têm pelo menos um empréstimo, não todos. A alternativa B tenta usar = ALL, mas compara id_cliente com id_financeira, o que é semanticamente incorreto. As alternativas D e E usam JOIN que, sem a lógica de divisão, não conseguem expressar a condição "em todas".
NÃO CAIA NESSA!
A banca explora a confusão entre quantificador existencial (EXISTS) e universal (NOT EXISTS com dupla negação). O candidato que vê "todas" e pensa em EXISTS cai na alternativa A. A técnica correta é sempre: para "todos", use NOT EXISTS com a negação interna.
Alternativa
Lógica aplicada
Quantificador
Resultado
Correta?
A
EXISTS (subconsulta correlacionada)
Existencial ("pelo menos um")
Clientes com ≥1 empréstimo
❌
B
= ALL (comparação de domínios distintos)
Universal (mal aplicado)
Erro semântico (id_cliente × id_financeira)
❌
C
NOT EXISTS + EXCEPT (dupla negação)
Universal ("todos")
Clientes sem financeira ausente (divisão relacional)
✅
D
FULL JOIN + NATURAL JOIN
Nenhum (junção ampla)
Combinações sem filtro de "todas"
❌
E
NATURAL JOIN (3 tabelas)
Existencial (implícito)
Clientes com algum empréstimo
❌
Divisão relacional em SQL: Quantificador universal ("todas") (Não existe operador nativo, Técnica: dupla negação); Dupla negação (NOT EXISTS) (Todas as financeiras, Menos as do cliente, Vazio = contratou em todas); Pegadinha da banca (EXISTS = "alguma" (existencial), NOT EXISTS = "todas" (universal))
Alternativa A — ❌ Incorreta
select c.nome from tb_cliente c where exists ( select 1 from tb_emprestimo e where e.id_cliente = c.id_cliente )
Esta consulta retorna os clientes que têm pelo menos um empréstimo (quantificador existencial). O EXISTS verifica se existe algum registro em tb_emprestimo para o cliente. Isso não atende ao requisito de "em todas as financeiras". Um cliente com apenas um empréstimo em uma única financeira seria retornado, o que está errado.
Alternativa B — ❌ Incorreta
select c.nome from tb_cliente c where id_cliente = all ( select id_financeira from tb_emprestimo )
Esta consulta é semanticamente inválida. Ela compara id_cliente (chave do cliente) com id_financeira (chave da financeira), que são domínios diferentes. Além disso, = ALL compara um valor com todos os valores de um conjunto, mas aqui o conjunto é de id_financeira, não de id_cliente. Mesmo que a sintaxe fosse aceita, a lógica não faz sentido: um cliente não pode ser igual a todas as financeiras.
Alternativa C — ✅ Correta ⟵ GABARITO
select c.nome from tb_cliente c where not exists ( select id_financeira from tb_financeira except select e.id_financeira from tb_emprestimo e where e.id_cliente = c.id_cliente )
Esta é a implementação correta da divisão relacional. Para cada cliente c, a subconsulta calcula as financeiras em que ele não tem empréstimo (todas as financeiras menos as que ele tem). Se esse conjunto for vazio (NOT EXISTS), o cliente tem empréstimo em todas as financeiras. É a dupla negação: "não existe financeira em que ele não tenha empréstimo".
Alternativa D — ❌ Incorreta
select distinct c.nome from tb_financeira f natural join tb_emprestimo e full join tb_cliente c on c.id_cliente = e.id_cliente
Esta consulta usa FULL JOIN, que retorna todas as linhas de ambas as tabelas, incluindo clientes sem empréstimo e financeiras sem empréstimo. O NATURAL JOIN entre tb_financeira e tb_emprestimo pode gerar combinações incorretas se houver colunas com o mesmo nome. Além disso, não há nenhuma lógica de divisão: ela simplesmente junta as tabelas e retorna nomes distintos, o que não filtra clientes que contrataram empréstimos em todas as financeiras.
Alternativa E — ❌ Incorreta
select distinct c.nome from tb_financeira natural join tb_emprestimo natural join tb_cliente c
Esta consulta faz um NATURAL JOIN entre as três tabelas, o que retorna apenas os clientes que têm empréstimos (pois o JOIN interno elimina linhas sem correspondência). No entanto, ela não verifica se o cliente tem empréstimo em todas as financeiras — apenas retorna clientes que têm algum empréstimo. A lógica de divisão está ausente.
Gabarito: letra C — a única consulta que implementa corretamente a divisão relacional, retornando os clientes que contrataram empréstimos em todas as financeiras.