Apresentação e revisão
Combinados da disciplina, calendário e a base que vem do semestre anterior. Os slides da revisão só listam os tópicos, então a lista abaixo serve de roteiro de autoestudo.
- Dinâmica: explicação (teórica, prática e com participação) e no mínimo 1 exercício por aula. Exercício de hoje vence às 23:59 do 2º domingo.
- Nota de cada bimestre: Avaliação (0-5) + Atividades (0-2) + APEX (0-1) + Avaliação Integrativa (0-2). A substitutiva (0-10) troca a menor nota de bimestre.
- Ferramentas: MySQL Server + MySQL Workbench, MongoDB + Compass (NoSQL, no 2º bimestre), Visual Paradigm / Draw.io para modelagem.
- Revisão que precisa estar na ponta da língua: análise de requisitos, modelo conceitual (MER/DER), modelo lógico, normalização e modelo físico.
| Aula | Datas | Assunto |
|---|---|---|
| 01-01 | 03/08 - 04/08 | Apresentação + revisão |
| 02-02 | 10/08 - 11/08 | Intersect, except, case, having, with |
| 03-03 | 17/08 - 18/08 | Subquery + índice |
| 04-04 | 24/08 - 25/08 | View |
| 05-05 | 31/08 - 01/09 | Trigger + function |
| 06-06 | 08/09 e 14/09 | Stored procedure |
| 07-08 | 21/09 - 22/09 | Revisão + APEX 1º bimestre |
| 08-09 | 28/09 - 29/09 | Avaliação do 1º bimestre |
| 09-10 | 05/10 - 06/10 | Devolutiva + transação, concorrência e controle |
| 10 a 11 | 19/10 - 27/10 | NoSQL (MongoDB) |
Exercício 1 — modelos do e-commerce Turma A e B: 16/08
- A partir do texto crie o modelo conceitual, o modelo lógico e o modelo lógico normalizado.
- Texto: cada cliente (nome, e-mail, senha, endereço de entrega) pode fazer compras de vários produtos. Cada produto pertence a uma categoria (nome e descrição) e tem nome, preço e descrição.
- Entregar os diagramas em PDF ou JPG.
Exercício 2 — a tabela produto está na 3FN? Turma A e B: 16/08
- Tabela
produto: id (PK), nome, categoria, preco_compra, preco_venda, lucro, fornecedor, cidade_fornecedor. - Responda se está na 3FN; se não, justifique e altere para atender a 3FN.
- Entregar PDF ou JPG da normalização.
Exercício 3 — aluno, disciplina e aluno_disciplina Turma A e B: 16/08
- Tabelas:
aluno(id, nome VARCHAR(250), data_nascimento DATE, sexo VARCHAR(1), formado VARCHAR(1)),disciplina(id, nome VARCHAR(50), semestre INT) ealuno_disciplina(id_aluno, id_disciplina). - 1) criar as tabelas; 2) mín. 3 alunos, 5 disciplinas e 9 registros em aluno_disciplina; 3) todos os alunos; 4) alunos do sexo M; 5) nome e formado das alunas F; 6) mudar o nome do aluno de id 3 para "Nome alterado".
- 7) novo aluno sem disciplina; 8) nome do aluno e da disciplina só dos relacionados; 9) todos os alunos (id, nome, nascimento) e, se houver, id e nome da disciplina.
- 10) nova disciplina sem aluno; 11) todos os registros de aluno e de disciplina, relacionados ou não; 12) disciplinas de semestre ímpar; 13) id e nome das disciplinas cujo nome tem "de"; 14) alunos que cursam disciplina do 1º, 3º, 5º ou 7º semestre.
- Entregar o arquivo .sql.
UNION, INTERSECT, EXCEPT, CASE, HAVING e WITH
Como combinar consultas, criar colunas calculadas com condição, filtrar grupos e reaproveitar o resultado de uma busca.
Query
- Cada comando SQL é individual:
SELECTbusca em 1 ou + tabelas e exibe,INSERT,UPDATEeDELETEmexem em 1 tabela e acabam ali. - Para combinar comandos existem
UNION,INTERSECTeEXCEPT.
UNION, INTERSECT, EXCEPT
- Forma geral:
<SELECT> UNION ou INTERSECT ou EXCEPT <SELECT>. Os doisSELECTtêm o mesmo número de colunas. UNION: junta tudo e ignora repetidos (usado para simular oFULL JOIN).UNION ALL: junta tudo, com repetidos.INTERSECT: valores comuns aos dois.EXCEPT: o que está no 1º e não está no 2º.
SELECT valor FROM numero UNION SELECT valor FROM numero_par; -- 1..10 sem repetir SELECT valor FROM numero UNION ALL SELECT valor FROM numero_par; -- 15 linhas (2,4,6,8,10 repetem) SELECT valor FROM numero INTERSECT SELECT valor FROM numero_par; -- 2,4,6,8,10 SELECT valor FROM numero EXCEPT SELECT valor FROM numero_par; -- 1,3,5,7,9
CASE
- Funciona como o
if/elseouswitchde programação:CASE WHEN <condição> THEN <resultado> ... ELSE <resultado> END. - Dê nome à coluna com
END AS 'nome'. OWHENé avaliado de cima para baixo e vale o primeiro que for verdadeiro. - Dá para aninhar:
ELSE CASE WHEN ... END.
SELECT valor, CASE WHEN valor % 5 = 0 THEN 'Múltiplo de 5' WHEN valor % 3 = 0 THEN 'Múltiplo de 3' WHEN valor % 2 = 0 THEN 'Par' ELSE 'Ímpar' END AS 'Análise' FROM numero; -- aninhado: primeiro par/ímpar, depois só o ímpar é analisado SELECT valor, CASE WHEN valor % 2 = 0 THEN 'Par' ELSE CASE WHEN valor % 5 = 0 THEN 'Múltiplo de 5' WHEN valor % 3 = 0 THEN 'Múltiplo de 3' ELSE 'Ímpar' END END AS 'Análise' FROM numero;
GROUP BY, HAVING e ORDER BY
GROUP BYagrupa o resultado e precisa de uma função de agregação:COUNT,SUM,AVG,MAX,MIN.ORDER BY qtd DESCordena pelo apelido, do maior para o menor.- Filtrar o resultado do grupo com
WHEREnão funciona: com o apelido dá erro 1054 (Unknown column) e com a função direta dá erro 1111 (Invalid use of group function). HAVINGé oWHEREdos agrupamentos. Pode existirWHEREeHAVINGna mesma consulta.
SELECT numero, COUNT(numero) AS qtd, AVG(nota) AS media FROM exercicio GROUP BY numero HAVING media > 6 ORDER BY qtd DESC;
WITH (CTE)
- O resultado de uma busca é usado por outro comando. Funciona como uma tabela temporária com nome:
WITH nome(colunas) AS (SELECT ...). - Serve para o
SELECT, mas também para INSERT, UPDATE e DELETE.
WITH info_temp(numero, quantidade, media) AS ( SELECT numero, COUNT(numero), AVG(nota) FROM exercicio GROUP BY numero) SELECT numero, quantidade, media FROM info_temp WHERE media > 6 ORDER BY quantidade DESC; -- WITH dentro do INSERT INSERT INTO calculado(exercicio, quantidade, media) WITH info_temp(numero, quantidade, media) AS ( SELECT numero, COUNT(numero), AVG(nota) FROM exercicio GROUP BY numero) SELECT numero, quantidade, media FROM info_temp WHERE media > 6; -- WITH antes do UPDATE (com JOIN) e do DELETE WITH info_temp(numero, quantidade, media) AS ( SELECT numero, COUNT(numero), AVG(nota) FROM exercicio GROUP BY numero) UPDATE calculado INNER JOIN info_temp ON info_temp.numero = calculado.exercicio SET calculado.quantidade = info_temp.quantidade, calculado.media = info_temp.media;
Exercício 4 — pai e filho Turma A e B: 23/08
- Tabelas:
pai(id, nome VARCHAR(100)) efilho(id, nome VARCHAR(100), id_pai). Relação 1:N. - 1) criar as tabelas; 2) mín. 5 registros em cada, com nomes iguais e diferentes; 3) nome do pai e nome do filho em 2 colunas; 4) em 1 coluna só, o nome de todos os pais e filhos em ordem ascendente; 5) o mesmo sem duplicidade.
- 6) nomes comuns entre pais e filhos; 7) nomes de pais que não são nome de algum filho; 8) todos os nomes de pais e filhos, mas o nome do pai não pode ser de algum filho e vice-versa.
- 9) nome do pai e quantidade de filhos; 10) nome e quantidade de pessoas (pais + filhos) com esse nome; 11) pai com 2 ou mais filhos; 12) repetir o 11 com
WITH. - 13) criar a tabela
pai_filho(nome do pai e nome do filho); 14) usandoWITH, gravar nela todas as informações de pai e filho. - Entregar o arquivo .sql.
Subquery e índice
Uma consulta dentro de outra, em todos os comandos, e como acelerar buscas com índices.
- Subquery é uma query dentro de outra query: um
SELECTdentro de outro comando. - Tabelas dos slides:
numero_par,numero_imparenumero_texto(valor, texto em português, texto_ingles).
Subquery no SELECT (5 lugares)
| # | Onde entra | Exemplo do slide |
|---|---|---|
| 1 | Como coluna do select | SELECT valor, (SELECT texto FROM numero_texto WHERE valor = numero_par.valor) AS 'Texto Português' FROM numero_par |
| 2 | Como filtro no WHERE | WHERE valor IN (SELECT valor FROM numero_par) |
| 3 | Como uma tabela (exige apelido) | FROM (SELECT valor FROM numero_impar ORDER BY valor) AS tabela |
| 4 | Como filtro no HAVING | GROUP BY valor HAVING valor IN (SELECT valor FROM numero_impar) |
| 5 | Como campo de ordenação | ORDER BY (SELECT texto FROM numero_texto WHERE valor = numero_par.valor) |
- Se a subquery usada como coluna devolver 2 colunas (
texto, texto_ingles) dá erro 1241 (Operand should contain 1 column).
Subquery no INSERT, UPDATE e DELETE
-- INSERT: como fonte dos dados INSERT INTO numero_par_texto(valor, texto, texto_ingles) SELECT valor, texto, texto_ingles FROM numero_texto WHERE valor % 2 = 0; -- INSERT: como parte da informação INSERT INTO numero_par_texto(valor, texto, texto_ingles) VALUES ((SELECT (COUNT(valor)+1)*2 FROM numero_par), '', ''); -- UPDATE: como nova informação UPDATE numero_par_texto SET texto = (SELECT texto_ingles FROM numero_texto WHERE valor = numero_par_texto.valor); -- UPDATE e DELETE: como filtro no WHERE UPDATE numero_par_texto SET texto_ingles = 'Texto em inglês' WHERE valor IN (SELECT valor FROM numero_texto); DELETE FROM numero_par_texto WHERE valor IN (SELECT valor FROM numero_texto);
O que não pode: no INSERT, UPDATE e DELETE a subquery não pode usar a mesma tabela que está sendo alterada.
Índice e EXPLAIN ANALYZE
- Índice é como o índice de um livro: acha o assunto sem folhear página por página. Serve para otimizar buscas.
- Use nas colunas mais usadas em busca. Cuidado: índice em tudo deixa inserir, atualizar e deletar mais custoso.
EXPLAIN ANALYZEexplica como o comando roda e quanto tempo levou, para ver (ou não) o ganho do índice.- Sem índice aparece
Table scan; com índice,Covering index scanourange scan. - Com 100 linhas o ganho é mínimo (0,0054 ms). Com 121.643 linhas,
WHERE valor > 100 AND valor < 1000o índice economizou cerca de 28 ms no total: o banco leu só as 449 linhas do intervalo em vez de varrer todas.
CREATE INDEX index_numero_par ON numero_par(valor); DROP INDEX index_numero_par ON numero_par; EXPLAIN ANALYZE SELECT valor FROM numero_par WHERE valor > 100 AND valor < 1000;
Exercício 5 — pessoa e automóvel Turma A e B: 30/08
- Tabelas:
pessoa(id, nome VARCHAR(45), profissao VARCHAR(45), cnh VARCHAR(1)) eautomovel(id, carro_moto VARCHAR(1), modelo VARCHAR(45), preco DECIMAL, id_pessoa). Relação 1:N. - 1) criar as tabelas; 2) mín. 5 registros em cada, com pessoas sem automóvel e com mais de 1; 3) todas as informações de pessoa, mas mostrando 'Sim' ou 'Não' no lugar de 'S' ou 'N' na cnh; 4) nome da pessoa e quantidade de automóveis.
- 5) nome de todas as pessoas e, se tiver automóvel, se é 'Moto' ou 'Carro' e o modelo; 6) criar
pessoa_automovel(nome da pessoa, modelo e preço); 7) com subquery, inserir nela só as pessoas que possuem automóveis; 8) nome da pessoa e o preço total dos automóveis, da garagem mais cara para a mais barata. - 9) atualizar o preço de algum automóvel; 10) com subquery, atualizar na nova tabela o preço de todos os registros desse automóvel; 11) com subquery, buscar na nova tabela os automóveis de quem é 'Desenvolvedor' ou 'Médico'; 12) índice na nova tabela pela coluna modelo; 13) com nova tabela e pessoa, nome e profissão de quem não tem automóvel.
- Entregar o arquivo .sql.
View
Uma "tabela" virtual definida por um SELECT, útil para esconder colunas e guardar consultas frequentes.
- View significa visão: um jeito de olhar as informações. Parece uma tabela, mas não guarda os dados (não copia nada da tabela original).
- Para que serve: (1) dar acesso só a algumas colunas (permissão é por tabela, não por coluna) e (2) guardar uma consulta complicada e frequente, como um relatório.
- Se entra um novo aluno na tabela original, ele aparece na view se estiver no resultado da consulta que monta a view.
- A view pode ter
INNER JOIN,LEFT JOIN,RIGHT JOIN,UNION,INTERSECTeEXCEPT: oSELECTé quem decide.
CREATE VIEW view_aluno AS SELECT nome, ra FROM aluno ORDER BY nome; DESC view_aluno; SELECT * FROM view_aluno; DROP VIEW view_aluno;
A view aceita UPDATE, INSERT e DELETE?
- Depende da permissão. Numa view de uma tabela só e
SELECTsimples, sim, e mexe na tabela original. UPDATE view_aluno SET ra = '999' WHERE nome = 'Danilo'mudou o RA na tabelaaluno.INSERTna view funciona, mas avisa 1423 (Field of view ... doesn't have a default value) porque as outras colunas sãoNOT NULL. Dependendo da política pode bloquear.DELETEna view também apaga da tabela original.- View com
UNION(mais de uma fonte) é só leitura:UPDATEdá erro 1288 (target table ... is not updatable).
Exercício 6 — filme e personagem Turma A e B: 06/09
- Tabelas:
filme(id, titulo, tipo, diretor, ano_lancamento) epersonagem(id, nome, especie, nivel_importancia, id_filme). Relação 1:N. - 1) criar; 2) mín. 3 filmes e 10 personagens; 3) nome do filme e quantidade de personagens; 4) nome do diretor e quantidade de filmes dirigidos por ele; 5) nome, espécie e nível de importância de todos os personagens, ordenando pelo ano de lançamento do filme e depois do mais para o menos importante.
- 6) título do filme e nome dos personagens dos filmes de ação ou drama; 7) em 1 coluna só, o nome do diretor e dos personagens em ordem alfabética; 8) view
view_filme(título e tipo); 9) buscar nela tudo ordenado pelo título; 10) viewview_personagem(nome, espécie e id do filme); 11) com ela, nome do personagem e título do filme. - 12) view
view_filme_personagem(título, tipo, diretor, ano e nome do personagem) só dos filmes de ano de lançamento par; 13) título dos filmes de ano não par, sem usarano_lancamentono filtro; 14) título, tipo, diretor, ano e personagem que não estão naview_filme_personagem. - Entregar o arquivo .sql.
Trigger e function
Ações automáticas disparadas por INSERT, UPDATE e DELETE, e funções próprias que devolvem um valor.
Trigger (gatilho)
- Quando acontece um
INSERT,UPDATEouDELETE, o trigger executa uma ação automática. Pode ser antes ou depois de cada um dos três: 6 combinações. NEWé a linha nova (existe no INSERT e no UPDATE);OLDé a linha antiga (existe no UPDATE e no DELETE).- Os comandos de dentro têm
;, que é o fim de uma query, então troque o delimitador:DELIMITER $$...END $$...DELIMITER ;. SIGNAL SQLSTATE '45000'gera uma exceção definida pelo usuário e oMESSAGE_TEXTé a mensagem. Aparece como erro 1644.SHOW TRIGGERS;lista os triggers eSHOW CREATE TRIGGER nomemostra um deles.
DELIMITER $$ CREATE TRIGGER trigger_before_insert_salario BEFORE INSERT ON salario FOR EACH ROW BEGIN IF NEW.remuneracao < 0 THEN SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Remuneração deve ser positiva'; END IF; END $$ DELIMITER ;
DELIMITER $$ CREATE TRIGGER trigger_after_insert_salario AFTER INSERT ON salario FOR EACH ROW BEGIN INSERT INTO log_salario(id_salario, nome_funcionario, remuneracao_anterior, remuneracao_nova, datahora, usuario) VALUES (NEW.id, NEW.nome_funcionario, 0.0, NEW.remuneracao, CURRENT_TIMESTAMP(), USER()); END $$ DELIMITER ;
BEFORE UPDATE: IF OLD.remuneracao > NEW.remuneracao THEN SIGNAL ... -- não pode diminuir BEFORE DELETE: IF OLD.remuneracao < (media/2) OR OLD.remuneracao = maximo THEN SIGNAL ... AFTER DELETE: <comandos> -- só roda se o delete foi permitido
- Problemas: um trigger que faz
INSERTna mesma tabela que o disparou até pode ser criado, mas não executa: erro 1442. - Loop entre tabelas: insert na tabela 1 dispara trigger que escreve na 2, que dispara trigger que escreve na 1...
- Trigger x FK: o
DELETEdo id 2 passou pelo trigger, mas quem barrou foi a chave estrangeira (erro 1451).
Function
- Como
AVG,COUNT,MAX,MIN,SUM, mas criada por você. Recebe parâmetros e devolve um valor comRETURN. - Forma:
CREATE FUNCTION nome(parâmetros) RETURNS tipo <DETERMINISTIC> BEGIN ... RETURN ...; END$$. - Chama-se dentro de um
SELECT, linha a linha.DROP FUNCTION nomeapaga.
DELIMITER $$ CREATE FUNCTION verificar_salario_2000(salario DECIMAL(9,2)) RETURNS VARCHAR(100) DETERMINISTIC BEGIN DECLARE resultado VARCHAR(100); IF salario >= 2000 THEN SET resultado = 'Maior ou igual a 2000'; ELSE SET resultado = 'Menor que 2000'; END IF; RETURN resultado; END$$ DELIMITER ; SELECT *, verificar_salario_2000(remuneracao) FROM salario;
Exercício 7 — animal e animal_feroz Turma A e B: 13/09
- Tabelas:
animal(id, especie VARCHAR(100), nivel_feroz INT, herbivoro_carnivoro VARCHAR(1)) eanimal_feroz(id, especie, herbivoro_carnivoro, registros INT). - 1) criar as tabelas; 2) trigger antes de inserir: nivel_feroz entre 1 e 5, senão mensagem de erro; 3) trigger depois de inserir: se nivel_feroz for 4 ou 5, cadastrar em
animal_ferozcom registros = 1; 4) criar 6 animais (5 válidos e 1 com nivel inválido). - 5) trigger antes de atualizar: se mudar a espécie, não deixar; 6) atualizar um animal mudando a espécie para "espécie inválida"; 7) trigger depois de atualizar: incrementar
registrosdaquele animal se for feroz; 8) atualizar 1 animal feroz e 1 que não é feroz. - 9) trigger antes de deletar: se for herbívoro, não permitir; 10) apagar um herbívoro; 11) trigger depois de deletar: apagar o animal de
animal_feroz; 12) apagar um animal de nivel 4 ou 5. - 13) função que recebe a espécie e retorna "Animal feroz!" ou "Animal não é muito feroz!" conforme exista ou não em
animal_feroz; 14) todos os animais + a função; 15) função que recebe a letra inicial e retorna "Herbívoro" ou "Carnívoro"; 16) espécie + essa função. - Entregar o arquivo .sql.
Stored procedure
Blocos de comandos guardados no banco, com parâmetros de entrada e saída, laços e erros próprios.
- Stored procedure é um bloco de comandos guardado no banco e executado com
CALL. Diferente da function, não precisa devolver valor. - Parâmetros:
IN(entra, padrão),OUT(sai, lido depois numa variável@x) eINOUT(entra e sai). - Laços:
WHILE ... DO ... END WHILEeREPEAT ... UNTIL cond END REPEAT. Erros comSIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '...'. - Exemplos dos slides:
selecionar_1_disciplina,selecionar_prova_com_x_questoes,verificar_resposta,contagem_regressiva(WHILE) econtagem_regressiva_repeat.
DELIMITER $$ CREATE PROCEDURE contar_automoveis(OUT p_total INT) BEGIN SELECT COUNT(*) INTO p_total FROM automovel; END$$ DELIMITER ; CALL contar_automoveis(@total); SELECT @total;
A explicação completa (IN/OUT/INOUT, laços, SIGNAL e erros 1305, 1318 e 1064) está na seção Stored Procedure do guia principal.
Exercício 8 — automóvel e tabuada (stored procedures) Turma A: 27/09 · Turma B: 20/09
- Tabelas:
automovel(id, carro_moto VARCHAR(1), placa VARCHAR(7), marca, modelo, proprietario VARCHAR(100)) etabuada(id, n1, n2, resultado). - Procedure sem parâmetros que preenche a tabuada de 1 a 10; procedure com
n1en2de entrada e o id de saída; mostrar a chamada e como exibir o resultado. - Procedure com parâmetro de entrada e saída: se o número for primo devolve o próprio número; se não, multiplica pelas ocorrências do número na tabuada. Chamar 3 vezes (primo, resultado de tabuada, não resultado).
- Procedure de 7 parâmetros (ação + 6 campos) com INSERIR, ATUALIZAR e EXCLUIR, usando
SIGNALcom os textos exatos 'Erro - Placa já cadastrada', 'Erro - Id não existe' e 'Erro - Ação desconhecida'. Chamar 7 vezes (inserir OK e com erro, atualizar OK e com erro, excluir OK e com erro, ação inexistente). - Entregar um único arquivo .sql.
Revisão e APEX
Fechamento do 1º bimestre.
- Revisão geral: UNION/INTERSECT/EXCEPT, CASE, HAVING, WITH, subqueries, índices, views, triggers, functions e stored procedures.
- APEX do 1º bimestre (atividade prática de extensão, vale 0-1).
- A Avaliação do 1º bimestre é na aula 08-09, em 28/09 e 29/09.
- Depois: transação, concorrência e controle (05/10 e 06/10) e NoSQL com MongoDB (19/10 a 27/10).
Método de estudo: para cada tópico acima, abra o guia, esconda o código, escreva a consulta do zero no Workbench e só depois compare. Errou? O erro cai na tabela de erros do guia principal.