|
MC536 - Consultas Avançadas em SQL
Prof. Célio Guimarães IC - Unicamp
|
|
Atualizado em 01 Out 2022
A "consulta clássica" de SQL: fornecedores que fornecem todos os materiais
Esta consulta não trivial pode ser feita e entendida facilmente com os recursos da Álgebra Relacional,
que nada mais são que uma forma compacta e sintaticamente mais simples de
explicitar consultas do que em SQL. A lógica atrás da solução é:
considere as linhas de FxM - FM (linhas do produto cartesiano de F e M que não estão presentes
na tabela FM: caso existam,isto significa que
algum material não é fornecido por algum fornecedor; extraia os fornecedores dessas linhas:
todos eles não fornecem algum material, logo os fornecedores que sobrarem fornecem todos os materiais.
Em Álgebra Relacional ficaria assim:
projeçao em f(F) - projeção em f(FxM - FM), e em SQL:
select f from F
where f not in
(select f from F,M -- subconsulta obtem fornecedores que não fornecem "algum" material
where (f,m) not in
(select f,m from FM) -- obtem fornecedores e materiais que eles fornecem
)
Outra solução não tão compacta é:
select f from F
where f not in
(select f from
(select f,m from F,M
where (f,m) not in
(select f,m from FM)) as foo)
Obs: Mysql e Postgres requerem inserir um alias (no caso, foo) para "tabelas derivadas"
ou seja, tabelas obtidas através de uma subconsulta na cláusula from (mas não nas outras cláusulas).
Exercício(*): suponha que as tabelas F e M foram destruídas. Escreva a consulta clássica usando apenas a
tabela FM.
Sugestão: modifique a solução acima.
A consulta "super-clássica":
Usando a tabela FM(f,m) obtenha uma lista de pares de fornecedores (f1, f2)
onde f1 fornece pelo menos todos os materiais que f2 fornece.
Sugestão: como na consulta clássica, obtenha uma subconsulta mais simples.
Exercícios
Importante: todos os exercícios propostos na disciplina devem ser feitos usando
apenas subconsultas,
sem usar os recursos SQL de contagem, funções agregado, agrupamento (group by, having),
exists, not exists.
Dicas:
Onde apropriado use distinct.
Você pode usar order by para facilitar a visualização dos resultados.
Para facilitar a verificação da correção da sua consulta, coloque na última linha uma cláusula order by
apropriada, e a mesma cláusula numa consulta mais simples sobre as tabelas envolvidas.
Exercícios em cuja descrição aparece a palavra não
devem ser resolvidos com uma subconsulta do tipo not in (subconsulta) onde
(subconsulta) corresponde à parte afirmativa da questão: ela é geralmente simples de formular, por isto
você deve obtê-la (e testá-la) em primeiro lugar. Exemplo:
"Quais jogadores não venceram o torneio 1?"
obtenha primeiro os "jogadores que venceram o torneio 1".
Se a mesma tabela aparece 2 ou mais vezes na cláusula from
elas devem ser renomeadas como a seguir:
from F as F1, F as F2, onde "as" é opcional
- Escreva uma consulta SQL para "obter os materiais que não possuem fornecedores"
- Reescreva a consulta clássica para obter os números dos jogadores que ganharam
todos os torneios de Grand Slam (para isto utilize a tabela LT que só possui
os torneios de Grand Slam em vez da tabela Lista_torneios ou utilize a sub-expressão
where numt <= 4 )
- Modifique a consulta acima para obter o nome em vez do número do jogador.
- Considere a tabela FMP(f,m, preco) de fornecimentos de materiais, onde para
cada par f,m é incluído o preço do material fornecido.
Escreva uma consulta SQL com o seguinte objetivo: "para cada material m obtenha o fornecedor f
que fornece m com o menor preço dentre todos os fornecedores que fornecem m".
Sugestão: use o modificador all numa subconsulta correlacionada.
Verifique a correção de sua solução executando:
select f, m, preco
from FMP
order by m, preco, f
- Considere a consulta q37: "obter os nomes dos jogadores que não pertencem
ao grupo dos mais velhos". Suponha que o operador any esteja "quebrado".
Re-escreva a consulta usando o operador all.
É possível resolvê-la sem usar o operador not in?
- Considere agora a consulta q36: "obter os nomes dos jogadores
mais velhos que todos os outros". Suponha que o operador all esteja
"quebrado".
Re-escreva a consulta usando o operador any.
Consultas sobre a tabela de "Pais e Filhos", D(p,f) :
"Obtenha uma lista de pessoas cujos pais não estão presentes na tabela D"
Solução: pelo requisito da consulta e pela definição da tabela D essas pessoas
não podem aparecer como filhos;
logo elas são pais e não aparecem na coluna f de D; portanto:
select distinct p from D -- estes são pais
where p not in -- que não podem aparecer
(select f from D) -- na lista de filhos
Dica: na maioria das outras consultas você deverá usar o produto cartesiano de cópias da tabela D e fazer
uma seleção apropriada sobre o mesmo; para obtê-la é importante raciocinar sobre as linhas do produto cartesiano.
- Escreva uma consulta para verificar se a tabela "Pais e Filhos" D(p,f) está inconsistente no sentido de possuir
registros de filhos em que apenas um dos pais está presente na tabela, ou seja,
para um certo filho há apenas uma linha na tabela D.
- Dentre os jogadores que venceram torneios, selecione aqueles que venceram exatamente um torneio.
(Veja discussão desse problema na seção seguinte).
Consultas envolvendo o produto cartesiano de tabelas
As consultas a seguir podem ser facilmente feitas com os recursos de contagem do SQL mas suponha
que eles não estejam disponíveis numa implementação mais simples da linguagem.
"Quais fornecedores fornecem dois ou mais materiais?"
Se fizermos o produto cartesiano de duas cópias da tabela FM digamos FM1 e FM2 e examinarmos
uma linha do produto cartesiano ela terá o formato:
FM1.f Fm1.m FM2.f FM2.m
Se um certo fornecedor fornecer mais de um material, no produto cartesiano haverá pelo menos duas linhas
em que FM1.f=Fm2.f porém FM1.m != FM2.m. Daí surge a consulta SQL:
select distinct FM1.f
from FM FM1, FM FM2
where FM1.f=FM2.f and FM1.m != FM2.m
"Quais fornecedores fornecem exatamente um material?"
Na mesma linha de raciocínio, são aqueles fornecedores que não fornecem dois ou mais materiais:
-- Fornecedores que fornecem exatamente um material
select f from FM
where f not in
(select distinct FM1.f
from FM FM1, FM FM2
where FM1.f=FM2.f and FM1.m != FM2.m
)
(*) "Quais jogadores venceram exatamente um torneio de Grand Slam?"
Sugestão: por ser a tabela de torneios mais complexa do que a tabela FM, a expressão booleana na cláusula
where acima precisa ser alterada. dica: os jogadores 16, 910 e 1228 não deveriam aparecer na sua solução!.