Pular para o conteúdo principal

Questão de Banco de Dados — Consultas e Comandos em SQL — FUNDATEC 2023

Banco de DadosConsultas e Comandos em SQL
Código
qa537837
Banca
FUNDATEC
Órgão
PROCERGS
Ano
2023
Cargo
ANC ( )

Considere o esquema relacional que representa parte de um sistema de uma empresa:


Projetos(IdProjeto, nome)
Pessoas(IdPessoa, nome)
ProjetosPessoas(#IdProjeto,#IdPessoa)

 

Legenda: Campos sublinhados compõem a chave primária da tabela e campo precedido de # é uma chave estrangeira

 

A coordenação de recursos humanos deseja saber os nomes das pessoas que já trabalharam em todos os projetos cadastrados na base de dados. A consulta que expressa correta e eficientemente o que o relatório deve mostrar é:

  1. ASELECT * FROM Projetos, ProjetosPessoas, Pessoas WHERE USING(IdProjeto, IdPessoa) AND IdProjeto = ALL (SELECT * FROM Projetos)
  2. BSELECT nome FROM Pessoas WHERE IdPessoa IN (SELECT IdPessoa FROM ProjetosPessoas WHERE EXISTS (SELECT * FROM Projetos))
  3. CSELECT nome FROM Pessoas PE WHERE NOT EXISTS (SELECT * FROM Projetos PR WHERE NOT EXISTS (SELECT * FROM ProjetosPessoas PP WHERE PE.IdPessoa = PP.IdPessoa AND PR.IdProjeto = PP.IdProjeto))
  4. DSELECT nome FROM PESSOAS PE WHERE EXISTS (SELECT IdPessoa FROM ProjetosPessoas LEFT JOIN Projetos)
  5. ESELECT nome FROM Projetos PR WHERE EXISTS (SELECT * FROM Pessoas PE WHERE EXISTS (SELECT * FROM ProjetosPessoas PP WHERE PE.IdPessoa = PP.IdPessoa AND PR.IdProjeto = PP.IdProjeto))
Revelar gabarito e comentário

GabaritoC — SELECT nome FROM Pessoas PE WHERE NOT EXISTS (SELECT * FROM Projetos PR WHERE NOT EXISTS (SELECT * FROM ProjetosPessoas PP WHERE PE.IdPessoa = PP.IdPessoa AND PR.IdProjeto = PP.IdProjeto))

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 dupla negação com NOT EXISTS aninhado — o padrão clássico para expressar "todos" em SQL (divisão relacional). Ela seleciona pessoas para as quais não existe um projeto em que a pessoa não tenha trabalhado, o que equivale a dizer que a pessoa trabalhou em todos os projetos. As demais alternativas ou têm sintaxe inválida, ou retornam resultados incorretos (como todas as pessoas, ou apenas as que trabalharam em pelo menos um projeto).

O problema pede uma operação conhecida como divisão relacional na álgebra relacional: encontrar pessoas que participaram de todos os projetos. O SQL não tem um operador direto de divisão, então a solução padrão é usar dupla negação com NOT EXISTS. A lógica é: para cada pessoa PE, verifique se existe algum projeto PR para o qual não exista um registro em ProjetosPessoas ligando PE àquele PR. Se não existir tal projeto, então a pessoa participou de todos. Essa é a técnica consagrada e eficiente, pois usa apenas índices e subconsultas correlacionadas, sem produtos cartesianos desnecessários.

Vamos entender por que a dupla negação funciona com um exemplo. Suponha os projetos P1 e P2, e as pessoas Ana (trabalhou em P1 e P2) e Bruno (trabalhou apenas em P1). Para Ana: existe algum projeto em que ela não trabalhou? Não — ela trabalhou em todos. Então o NOT EXISTS externo retorna verdadeiro, e Ana aparece. Para Bruno: existe o projeto P2 em que ele não trabalhou. Então o NOT EXISTS externo retorna falso, e Bruno não aparece. Perfeito.

A alternativa C implementa exatamente isso: o NOT EXISTS mais externo percorre as pessoas; o NOT EXISTS interno percorre os projetos; e a subconsulta mais interna verifica se existe o vínculo. A alternativa E, por outro lado, usa apenas um EXISTS simples, que retorna verdadeiro se a pessoa trabalhou em pelo menos um projeto — não em todos. Essa é a distinção crucial: EXISTS verifica existência de pelo menos um; a dupla negação com NOT EXISTS verifica universalidade (todos).

A pegadinha da banca está em confundir "trabalhou em todos" com "trabalhou em pelo menos um". A alternativa E é a armadilha clássica: ela parece correta à primeira vista, mas retorna qualquer pessoa que tenha trabalhado em qualquer projeto, não apenas as que trabalharam em todos. Guarde o padrão: para "todos", use NOT EXISTS com NOT EXISTS aninhado.

  1. 1Pessoa PE
  2. 2NOT EXISTS projeto PR
  3. 3NOT EXISTS vínculo PP
  4. 4Trabalhou em todos
LEVEL · soulevel.com.br

Alternativa A — ❌ Incorreta

A sintaxe WHERE USING(IdProjeto, IdPessoa) é inválida em SQL padrão. A cláusula USING é usada em JOIN (ex.: FROM A JOIN B USING (coluna)), não em WHERE. Além disso, IdProjeto = ALL (SELECT * FROM Projetos) compara uma coluna com uma subconsulta que retorna todas as colunas da tabela Projetos (não apenas IdProjeto), o que é semanticamente incorreto e provavelmente causaria erro de tipo ou de número de colunas. Mesmo que a sintaxe fosse corrigida, a lógica não expressa corretamente a divisão.

Alternativa B — ❌ Incorreta

Esta consulta retorna pessoas que têm pelo menos um registro em ProjetosPessoas para o qual exista qualquer projeto na tabela Projetos. Como a subconsulta EXISTS (SELECT * FROM Projetos) é verdadeira sempre que a tabela Projetos não estiver vazia, a condição se reduz a "a pessoa tem pelo menos um vínculo em ProjetosPessoas". Isso retorna pessoas que trabalharam em um ou mais projetos, não em todos. É o erro de confundir existência com universalidade.

Alternativa C — ✅ Correta ⟵ GABARITO

Implementa a divisão relacional via dupla negação. Para cada pessoa PE, o NOT EXISTS externo verifica se não existe um projeto PR tal que não exista um vínculo PP ligando PE.IdPessoa a PR.IdProjeto. Se não existir tal projeto, a pessoa trabalhou em todos. A sintaxe está correta e a lógica é exatamente a necessária. É a solução padrão e eficiente para consultas com quantificador universal em SQL.

Alternativa D — ❌ Incorreta

A consulta SELECT nome FROM PESSOAS PE WHERE EXISTS (SELECT IdPessoa FROM ProjetosPessoas LEFT JOIN Projetos) tem dois problemas. Primeiro, a subconsulta não é correlacionada: não há referência a PE dentro dela, então o EXISTS é avaliado uma única vez e retorna verdadeiro se a tabela resultante do LEFT JOIN tiver pelo menos uma linha. Isso faria a consulta retornar todas as pessoas, independentemente de terem trabalhado em projetos. Segundo, a sintaxe LEFT JOIN Projetos sem condição de junção (ON) é inválida em SQL padrão (embora alguns SGBDs aceitem como cross join). Mesmo corrigida, a lógica não expressa a divisão.

Alternativa E — ❌ Incorreta

Esta é a armadilha clássica. A consulta seleciona projetos PR para os quais existe uma pessoa PE que trabalhou em PR. Ou seja, retorna projetos que têm pelo menos um trabalhador — não pessoas que trabalharam em todos os projetos. Além disso, o SELECT nome vem de Projetos, então retornaria nomes de projetos, não de pessoas. A lógica de EXISTS simples verifica existência, não universalidade. Para "todos", seria necessário o NOT EXISTS duplo, como na alternativa C.

Gabarito: letra C

Link permanente: /questoes/qa537837