Introdução aos SGBDs
const disciplina = "banco"
Banco de Dados
3º semestre · quinta-feira · 80 horas-aula
Visão Geral
quinta-feira · 20 aulas no semestre
Visão geral de Banco de Dados
Ementa
Características e vantagens de Sistemas Gerenciadores de Bancos de Dados (SGBDs), modelagem entidade-relacionamento, modelo relacional, normalização de relações, linguagens de consulta estruturada (Structured Query Language - SQL) e Álgebra Relacional.
Objetivo geral
O objetivo geral da disciplina é permitir que o aluno adquira os conhecimentos básicos sobre bancos de dados e SGBD, ressaltando os aspectos de modelagem e manipulação de dados.
Conteúdo programático
- Introdução aos SGBDs
- Gerência de dados antes do conceito de BD
- Conceitos de BD e SGBD
- Noções gerais de um sistema de BD
- Modelo Entidade-Relacionamento (E-R)
- Modelagem Conceitual
- Primitivas básicas do modelo E-R
- Restrições de Integridade
- Mecanismos de Abstração
- Uso de uma ferramenta de modelagem
- Modelo Relacional
- Conceitos Básicos
- Regras de Integridade
- Transformação de Diagramas ER para Modelo Relacional
- Normalização de relações até a Terceira Forma Normal
- Linguagem de Consulta Estruturada — SQL
- Linguagem de Definição de Dados (DDL): CREATE TABLE, ALTER TABLE e DROP TABLE
- Linguagem de Manipulação de Dados (DML): SELECT, INSERT, UPDATE e DELETE
- Visões e Linguagem de Controle de Dados (DCL): CREATE VIEW, DROP VIEW, GRANT e REVOKE
- Otimização de Consultas com álgebra relacional
Buscar nesta disciplina
Cronograma
Datas calculadas a partir do dia da semana da disciplina, do início do semestre e dos feriados nacionais.
julho de 2026
agosto de 2026
setembro de 2026
outubro de 2026
novembro de 2026
dezembro de 2026
Introdução aos SGBDs
Introdução aos SGBDs
Modelo Entidade-Relacionamento (E-R)
Modelo Entidade-Relacionamento (E-R)
Modelo Entidade-Relacionamento (E-R)
Modelo Entidade-Relacionamento (E-R)
Modelo Entidade-Relacionamento (E-R)
Modelo Relacional
Modelo Relacional
Modelo Relacional
Normalização de relações até a Terceira Forma Normal
Normalização de relações até a Terceira Forma Normal
Linguagem de Consulta Estruturada — SQL
Linguagem de Consulta Estruturada — SQL
Linguagem de Consulta Estruturada — SQL
Linguagem de Consulta Estruturada — SQL
Linguagem de Consulta Estruturada — SQL
Linguagem de Consulta Estruturada — SQL
Otimização de Consultas com álgebra relacional
Gerência de dados antes do conceito de banco de dados
Introdução aos SGBDs
Como os sistemas guardavam dados quando cada programa tinha seus próprios arquivos, e quais problemas dessa época deram origem ao SGBD.
Em palavras simples
Imagine uma empresa onde o setor de vendas tem um caderno de clientes e o setor de cobrança tem outro. Alguém muda de endereço e avisa só a um dos setores. A partir daí existem duas verdades sobre o mesmo cliente, e ninguém sabe qual vale. Era assim que os sistemas funcionavam antes do banco de dados: cada programa com seu arquivo, cada arquivo com sua cópia dos mesmos dados.
Tecnicamente
No processamento por arquivos, a estrutura física dos dados fica embutida no código do programa que os lê. Cada aplicação define seu próprio formato de registro, sua própria abertura de arquivo e seu próprio acesso. Isso produz quatro problemas clássicos: redundância descontrolada (o mesmo dado repetido em arquivos diferentes), inconsistência (as cópias divergem porque nada as sincroniza), dependência entre programa e dados (mudar o layout do arquivo obriga a recompilar todo programa que o usa) e dificuldade de acesso concorrente (dois programas gravando no mesmo arquivo corrompem-no, porque não há quem arbitre).
Principais conceitos
- Redundância
- O mesmo dado armazenado em mais de um lugar. Nem toda redundância é erro — a controlada é decidida de propósito; o problema é a descontrolada, que ninguém sabe que existe.
- Inconsistência
- Duas cópias do mesmo dado com valores diferentes. É consequência direta da redundância descontrolada: se nada obriga as cópias a andarem juntas, elas divergem.
- Dependência programa-dados
- Situação em que a estrutura física do arquivo está escrita dentro do programa. Acrescentar um campo ao arquivo quebra todos os programas que o leem.
Exemplos
O mesmo cliente em dois arquivos
Dois setores, dois arquivos, dois formatos. Repare que o cliente 1023 tem endereços diferentes — e nada no sistema percebe isso.
vendas/clientes.txt
1023;Maria Souza;Rua A, 100;maria@exemplo.com
cobranca/sacados.dat
1023|SOUZA, MARIA|RUA B, 250|(51)99999-0000Linha a linha
1023- O mesmo código de cliente nos dois arquivos. É a única coisa que os liga — e essa ligação só existe na cabeça de quem programou.
Rua A, 100 / RUA B, 250- A inconsistência. Um dos dois está errado, os dois programas continuam funcionando, e nenhum relatório acusa.
; versus |- Separadores diferentes: cada programa fixou o seu. Trocar o separador de um arquivo exige alterar o código que o lê — é a dependência programa-dados.
Onde isso aparece na prática
- Arquivos CSV trocados entre setores ainda reproduzem exatamente esse cenário — cada planilha vira uma cópia que envelhece sozinha.
- Sistemas legados em COBOL com arquivos indexados (ISAM/VSAM) são o retrato dessa fase e continuam em produção em bancos e órgãos públicos.
- Logs de aplicação são um uso legítimo de arquivo puro: são escritos uma vez e nunca atualizados, então nenhum dos quatro problemas se aplica.
Curiosidades
- O termo "data base" aparece pela primeira vez em documentos militares norte-americanos nos anos 1960, descrevendo bases de dados compartilhadas entre sistemas.
- O IDS (Integrated Data Store), de Charles Bachman, em 1964, é considerado o primeiro SGBD; Bachman ganhou o Turing Award por ele em 1973 — antes mesmo de o modelo relacional se popularizar.
Exercícios
Básico
Liste os quatro problemas do processamento por arquivos citados na aula e escreva, para cada um, uma frase explicando o que ele causa na prática.
Dica
Dois deles são consequência um do outro; comece por esse par.
Resolução comentada
Redundância descontrolada: o mesmo dado é gravado em vários arquivos, ocupando espaço e criando várias versões da verdade. Inconsistência: como nada sincroniza essas cópias, elas divergem — é consequência direta da redundância. Dependência programa-dados: o layout do arquivo está no código, então mudar o layout obriga a alterar e recompilar todos os programas. Dificuldade de acesso concorrente: sem um árbitro entre dois programas que gravam ao mesmo tempo, o arquivo corrompe.
Resposta
Redundância descontrolada, inconsistência, dependência programa-dados e dificuldade de acesso concorrente.
Intermediário
Uma escola tem um arquivo de alunos na secretaria e outro na biblioteca, cada um com nome e telefone do aluno. Descreva o que acontece quando um aluno troca de telefone e explique qual dos quatro problemas isso ilustra.
Dica
Pergunte-se: quem avisa o outro arquivo?
Resolução comentada
O aluno comunica a mudança ao setor com que tem contato — digamos, a secretaria. O arquivo da secretaria passa a ter o telefone novo; o da biblioteca continua com o antigo, porque não existe mecanismo que propague a alteração. A biblioteca liga para cobrar um livro atrasado e não encontra o aluno. O problema é a inconsistência, causada pela redundância descontrolada: o telefone está guardado duas vezes e nada obriga as duas cópias a andarem juntas.
Resposta
A biblioteca fica com o telefone desatualizado. É inconsistência, causada por redundância descontrolada.
Avançado
Explique por que um arquivo de log de aplicação não sofre dos problemas descritos na aula, mesmo sendo um arquivo puro sem SGBD nenhum.
Dica
Pense no que se faz com um log depois de escrito.
Resolução comentada
Os quatro problemas nascem da atualização de dados duplicados. Um log é append-only: cada linha é escrita uma vez e nunca alterada nem apagada. Sem atualização, não há como duas cópias divergirem — a inconsistência não tem como surgir. A redundância existe (o log repete dados que estão no banco), mas é redundância controlada e intencional, com finalidade de auditoria. A concorrência é resolvida pelo próprio sistema operacional, que garante atomicidade de escritas pequenas em modo append. E a dependência programa-dados é irrelevante porque o log não é lido por programas que dependem de seu layout, e sim por humanos e ferramentas tolerantes a formato.
Resposta
Porque o log é append-only: sem atualização de dado já gravado, não há divergência entre cópias — e é justamente a atualização que gera os quatro problemas.
Desafio
Um sistema de vendas guarda, em cada pedido, o nome e o endereço do cliente copiados do cadastro. Um colega diz que isso é redundância descontrolada e deve ser eliminado. Argumente a favor de manter essa cópia e diga em que condição ele teria razão.
Dica
O que deve constar numa nota fiscal emitida em 2024 se o cliente mudou de endereço em 2025?
Resolução comentada
A cópia no pedido não é redundância descontrolada: é um dado histórico. O endereço no pedido responde a "para onde esta compra foi entregue", que é uma pergunta diferente de "onde o cliente mora hoje" — respondida pelo cadastro. Se o pedido apenas apontasse para o cadastro, atualizar o endereço do cliente reescreveria o passado, e a nota fiscal de dois anos atrás passaria a mostrar um endereço que não existia na época. Isso é redundância controlada, decidida de propósito, e o nome técnico do padrão é snapshot de dados transacionais. O colega teria razão se o campo copiado fosse usado como se fosse o dado atual — por exemplo, se a tela de cadastro do cliente lesse o endereço a partir do último pedido, ou se um relatório de mala direta usasse os endereços dos pedidos em vez do cadastro. Aí passariam a existir duas fontes disputando a mesma pergunta, que é exatamente a definição do problema.
Resposta
É redundância controlada: o pedido guarda o endereço histórico da entrega, não o endereço atual do cliente. O colega só teria razão se essa cópia fosse usada para responder "onde o cliente mora hoje".
Resumo
Conceitos importantes
- Antes do SGBD, cada programa definia e mantinha seus próprios arquivos de dados.
- Os quatro problemas dessa abordagem: redundância descontrolada, inconsistência, dependência programa-dados e dificuldade de acesso concorrente.
- Redundância controlada é decisão de projeto; descontrolada é a que ninguém sabe que existe.
Checklist
- Sei explicar o que é processamento por arquivos.
- Sei nomear os quatro problemas e dar um exemplo de cada.
- Sei distinguir redundância controlada de descontrolada.
Pontos para revisão
- Por que inconsistência é consequência da redundância, e não um problema independente.
- Em que situação um arquivo simples continua sendo a escolha certa.
- redundância
- inconsistência
- dependência programa-dados
- concorrência
Conceitos de BD e SGBD
Introdução aos SGBDs
A distinção entre o banco de dados (a coleção de dados) e o SGBD (o software que a gerencia), e as vantagens que essa separação trouxe.
Em palavras simples
Banco de dados é o acervo; SGBD é o bibliotecário. O acervo são os livros guardados de forma organizada. O bibliotecário é quem sabe onde cada coisa está, quem controla quem pode pegar o quê, quem impede duas pessoas de levarem o mesmo exemplar e quem repõe tudo se houver um incêndio. Trocar de bibliotecário não muda os livros — e é exatamente essa independência que o SGBD trouxe.
Tecnicamente
Um banco de dados é uma coleção de dados inter-relacionados, com significado implícito, que representa algum aspecto do mundo real (o minimundo). Um SGBD é a camada de software que permite definir, construir, manipular e compartilhar esse banco. Ele oferece: linguagem de definição (DDL) para descrever o esquema; linguagem de manipulação (DML) para consultar e alterar; controle de concorrência para transações simultâneas; controle de acesso; e recuperação após falhas. A propriedade central que o SGBD entrega é a independência de dados — a capacidade de alterar o esquema em um nível sem alterar o nível acima. Na independência física, muda-se o armazenamento (criar um índice, trocar o disco) sem tocar no esquema lógico; na lógica, muda-se o esquema lógico (acrescentar uma coluna) sem alterar as aplicações que não usam a parte alterada.
Principais conceitos
- Banco de dados
- Coleção de dados inter-relacionados que representa um recorte do mundo real. É o conteúdo, não o programa.
- SGBD
- Sistema Gerenciador de Banco de Dados: o software que define, constrói, manipula e compartilha o banco, controlando acesso, concorrência e recuperação.
- Esquema
- A descrição da estrutura do banco — as tabelas, seus campos e suas restrições. Muda raramente.
- Instância
- Os dados efetivamente armazenados num dado momento. Muda a cada operação.
- Independência de dados
- Capacidade de alterar um nível do esquema sem afetar o nível superior. Física quando muda o armazenamento; lógica quando muda a estrutura lógica.
Exemplos
Esquema e instância — a mesma tabela em dois momentos
O esquema é a linha do CREATE TABLE; a instância é o conteúdo. O esquema muda em dias de manutenção; a instância muda o tempo todo.
-- ESQUEMA: a estrutura. Definida uma vez, alterada raramente.
CREATE TABLE aluno (
matricula INTEGER PRIMARY KEY,
nome VARCHAR(80) NOT NULL,
semestre INTEGER
);
-- INSTÂNCIA: o conteúdo num instante. Muda a cada INSERT.
INSERT INTO aluno VALUES (2026001, 'Ana Lima', 3);
INSERT INTO aluno VALUES (2026002, 'Bruno Reis', 3);Linha a linha
CREATE TABLE aluno- Comando de DDL — linguagem de definição de dados. Descreve a estrutura, não guarda dado nenhum.
matricula INTEGER PRIMARY KEY- Parte do esquema: declara o tipo e a restrição. O SGBD passa a recusar matrícula repetida ou nula, sem que nenhum programa precise verificar isso.
INSERT INTO aluno VALUES (...)- Comando de DML — linguagem de manipulação. Altera a instância; o esquema continua o mesmo.
Onde isso aparece na prática
- Criar um índice para acelerar uma consulta lenta não obriga a reescrever consulta nenhuma — é independência física em ação.
- Acrescentar uma coluna a uma tabela não quebra o sistema que já roda, desde que as consultas nomeiem colunas em vez de usar SELECT *.
- O controle de concorrência do SGBD é o que permite mil pessoas comprarem no mesmo site sem que dois pedidos levem o último item do estoque.
Curiosidades
- Edgar F. Codd, autor do modelo relacional, trabalhava na IBM e teve dificuldade para convencer a própria empresa a adotá-lo — a IBM já vendia o IMS, hierárquico, e demorou a lançar o System R.
- A sigla SGBD em inglês é DBMS (Database Management System); a distinção entre "banco" e "sistema gerenciador" é frequentemente ignorada na fala do dia a dia, quando se diz "o banco caiu" para dizer que o SGBD parou.
Exercícios
Básico
Explique com suas palavras a diferença entre banco de dados e SGBD.
Dica
Um é conteúdo, o outro é programa.
Resolução comentada
O banco de dados é a coleção de dados inter-relacionados que representa um recorte do mundo real — é conteúdo armazenado. O SGBD é o software que gerencia essa coleção: define a estrutura, executa as consultas, controla quem acessa, arbitra o acesso simultâneo e recupera o banco depois de uma falha. Um banco pode ser migrado de um SGBD para outro; são coisas separadas.
Resposta
Banco de dados é a coleção de dados; SGBD é o software que a gerencia.
Classifique cada item como esquema ou instância: (a) a definição da tabela produto; (b) as 4.000 linhas de produtos cadastrados; (c) a restrição de que preço não pode ser nulo.
Dica
Instância é o que muda quando alguém usa o sistema.
Resolução comentada
(a) Esquema — é a descrição da estrutura. (b) Instância — é o conteúdo num dado momento, e muda a cada cadastro. (c) Esquema — restrições fazem parte da descrição da estrutura, não do conteúdo.
Resposta
(a) esquema; (b) instância; (c) esquema.
Intermediário
O administrador cria um índice sobre a coluna nome da tabela cliente para acelerar as buscas. Nenhuma consulta do sistema precisou ser reescrita. Que tipo de independência de dados esse fato demonstra? Justifique.
Dica
Índice é decisão de armazenamento.
Resolução comentada
Demonstra independência física de dados. O índice é uma estrutura de armazenamento: altera como o SGBD encontra as linhas no disco, sem alterar quais linhas existem nem quais colunas a tabela tem. Como o esquema lógico permaneceu idêntico, as consultas — escritas contra o esquema lógico — continuam válidas. Quem decide usar ou não o índice é o otimizador do SGBD, em tempo de execução; o programador nem precisa saber que ele existe.
Resposta
Independência física: mudou o armazenamento, não o esquema lógico contra o qual as consultas foram escritas.
Avançado
Uma equipe acrescenta a coluna data_nascimento à tabela cliente. O relatório de vendas, que faz SELECT * FROM cliente e grava as colunas em posições fixas de um arquivo, passa a gerar saída errada. A independência lógica falhou? Explique.
Dica
Pergunte de quem é a dependência: do SGBD ou do programa?
Resolução comentada
A independência lógica não falhou — ela foi anulada pelo programa. A promessa da independência lógica é que uma aplicação continue funcionando quando o esquema muda em partes que ela não usa. Mas SELECT * não nomeia as colunas que usa: ele pede todas e assume uma ordem posicional que o esquema não garante. O relatório, portanto, depende da estrutura física do resultado, e não do conjunto de colunas de que precisa. Com SELECT nome, cpf, cidade FROM cliente, o acréscimo de data_nascimento seria invisível para ele. A conclusão prática é que independência de dados é uma capacidade oferecida pelo SGBD, não uma garantia automática: o programa precisa escrever consultas que a aproveitem.
Resposta
Não. A independência lógica existe, mas SELECT * a descarta ao depender da ordem posicional das colunas em vez de nomeá-las.
Desafio
Cite duas funções do SGBD que seriam extremamente caras de reimplementar dentro da aplicação e explique por que o custo é alto.
Dica
Pense no que acontece quando duas coisas ocorrem ao mesmo tempo, e no que acontece quando falta energia.
Resolução comentada
A primeira é o controle de concorrência. Reimplementá-lo exige tratar bloqueios, detectar e resolver impasses (deadlocks) e garantir níveis de isolamento entre transações — problemas que envolvem toda a combinação de operações simultâneas possíveis, e cujos erros aparecem só sob carga, de forma não determinística e quase impossível de reproduzir em teste. A segunda é a recuperação após falha. O SGBD mantém um log de transações que permite refazer o que estava confirmado e desfazer o que estava pela metade quando a energia caiu; garantir isso na aplicação significaria implementar escrita à frente do log, pontos de verificação e um protocolo de recuperação que funcione mesmo se a falha ocorrer durante a própria recuperação. Nos dois casos, o custo alto não está em escrever o caminho normal, e sim em cobrir corretamente todos os caminhos de exceção — e a consequência de errar é perda ou corrupção silenciosa de dados.
Resposta
Controle de concorrência e recuperação após falha. Ambas são caras porque o difícil não é o caso normal, e sim cobrir todos os casos de exceção — cujos erros são não determinísticos e corrompem dados em silêncio.
Resumo
Conceitos importantes
- Banco de dados é a coleção de dados; SGBD é o software que a gerencia.
- Esquema é a estrutura (muda raramente); instância é o conteúdo (muda sempre).
- Independência física isola mudanças de armazenamento; independência lógica isola mudanças de estrutura.
- As vantagens do SGBD: controle de redundância, de acesso, de concorrência e recuperação após falha.
Checklist
- Sei diferenciar banco de dados de SGBD.
- Sei classificar um item como esquema ou instância.
- Sei dar um exemplo de independência física e um de independência lógica.
- Sei citar quatro funções que o SGBD executa e a aplicação não precisa reimplementar.
Pontos para revisão
- Por que SELECT * anula a independência lógica.
- Quais funções do SGBD são caras demais para reimplementar na aplicação.
- SGBD
- esquema
- instância
- independência de dados
- DDL
- DML
Noções gerais de um sistema de banco de dados
Introdução aos SGBDs
A arquitetura em três esquemas (ANSI/SPARC), os papéis das pessoas envolvidas e o que acontece internamente entre a consulta e a resposta.
Em palavras simples
Um sistema de banco de dados é organizado em três camadas de descrição. Na de baixo está como os dados estão gravados no disco. No meio, quais tabelas existem e como se relacionam. Em cima, o recorte que cada usuário enxerga — o setor financeiro não precisa ver, nem deve ver, os mesmos campos que o RH. Separar essas três descrições é o que permite mexer numa sem derrubar as outras.
Tecnicamente
A arquitetura ANSI/SPARC define três níveis. O nível interno descreve o armazenamento físico: organização dos arquivos, índices, caminhos de acesso. O nível conceitual descreve a estrutura lógica completa do banco — entidades, atributos, relacionamentos e restrições — sem detalhes de armazenamento. O nível externo é o conjunto de visões, cada uma expondo a parte do conceitual que interessa a um grupo de usuários. Entre os níveis existem mapeamentos: o conceitual/interno e o externo/conceitual. É a existência desses mapeamentos que materializa a independência de dados — alterado o nível interno, refaz-se apenas o mapeamento conceitual/interno, e nada acima percebe. Os papéis envolvidos são o administrador de dados (decide o que se armazena), o administrador do banco (DBA, responsável por desempenho, segurança e backup), o projetista, o programador de aplicação e o usuário final.
Principais conceitos
- Nível interno
- Descreve como os dados estão fisicamente armazenados: arquivos, blocos, índices e caminhos de acesso.
- Nível conceitual
- Descreve a estrutura lógica completa do banco — o que existe e como se relaciona — sem dizer como está gravado.
- Nível externo
- Conjunto de visões; cada uma é o recorte do conceitual que um grupo de usuários enxerga.
- DBA
- Administrador de banco de dados: responsável por desempenho, segurança, backup e recuperação do ambiente.
- Catálogo (dicionário de dados)
- Onde o SGBD guarda a descrição do próprio banco — as tabelas, colunas, tipos e restrições. É um banco de dados sobre o banco de dados.
Exemplos
Os três níveis sobre a mesma tabela
A mesma informação descrita nos três níveis. Repare que só o nível externo muda de usuário para usuário.
-- CONCEITUAL: a estrutura lógica completa.
CREATE TABLE funcionario (
id INTEGER PRIMARY KEY,
nome VARCHAR(80) NOT NULL,
setor VARCHAR(40),
salario NUMERIC(10,2)
);
-- INTERNO: decisão de armazenamento. Não muda o que existe, muda como se acha.
CREATE INDEX idx_func_setor ON funcionario (setor);
-- EXTERNO: o recorte que a recepção enxerga. Sem salário.
CREATE VIEW ramal_interno AS
SELECT id, nome, setor FROM funcionario;Linha a linha
CREATE TABLE funcionario- Nível conceitual: declara tudo o que existe sobre funcionário, para todo o sistema.
CREATE INDEX idx_func_setor- Nível interno: cria um caminho de acesso alternativo. Nenhuma consulta precisa ser reescrita por causa dele.
CREATE VIEW ramal_interno- Nível externo: uma janela sobre o conceitual. Quem só tem permissão nesta visão não tem como ler salários.
Onde isso aparece na prática
- Uma visão que expõe funcionário sem a coluna salário é nível externo funcionando como mecanismo de segurança.
- Particionar uma tabela de 500 milhões de linhas por ano é mudança de nível interno: as consultas continuam escritas contra a mesma tabela lógica.
- O DBA que analisa o plano de execução de uma consulta lenta está trabalhando no mapeamento conceitual/interno.
Curiosidades
- A proposta ANSI/SPARC é de 1975 e nunca virou norma formal, mas seu vocabulário de três níveis se tornou universal no ensino de bancos de dados.
- O processador de consultas de um SGBD relacional costuma reescrever a consulta antes de executá-la: a ordem em que as tabelas aparecem no FROM raramente é a ordem em que serão lidas.
Exercícios
Básico
Nomeie os três níveis da arquitetura ANSI/SPARC e diga o que cada um descreve.
Dica
Vá do disco para o usuário.
Resolução comentada
Interno: como os dados estão fisicamente armazenados — arquivos, blocos, índices. Conceitual: a estrutura lógica completa do banco, com entidades, atributos, relacionamentos e restrições, sem detalhes de armazenamento. Externo: as visões, cada uma expondo a um grupo de usuários apenas o recorte do conceitual que lhe interessa.
Resposta
Interno (armazenamento), conceitual (estrutura lógica) e externo (visões).
Intermediário
Em que nível ocorre cada mudança? (a) criar um índice; (b) acrescentar a coluna cpf; (c) criar uma visão que oculta o salário.
Dica
Pergunte se a mudança altera o que existe ou só como se chega até ele.
Resolução comentada
(a) Nível interno: o índice é um caminho de acesso, não altera o que existe logicamente. (b) Nível conceitual: acrescentar coluna altera a estrutura lógica do banco. (c) Nível externo: a visão é um recorte para um grupo de usuários; a tabela por baixo continua com o salário.
Resposta
(a) interno; (b) conceitual; (c) externo.
Avançado
Explique por que o catálogo do SGBD ser, ele próprio, um conjunto de tabelas consultáveis por SQL é uma decisão de projeto útil — e não apenas uma curiosidade.
Dica
Como uma ferramenta de modelagem descobre quais tabelas existem no banco?
Resolução comentada
Se o catálogo é feito das mesmas estruturas que o resto do banco, então toda ferramenta que já sabe falar SQL sabe interrogá-lo, sem precisar de uma interface proprietária. É assim que ferramentas de modelagem fazem engenharia reversa de um banco existente, que geradores de código descobrem colunas e tipos, e que scripts de auditoria verificam se toda tabela tem chave primária. A alternativa — um formato binário fechado — obrigaria cada ferramenta a implementar um leitor específico por SGBD. Além disso, a uniformidade reduz o próprio SGBD: o mesmo processador de consultas serve para dados do usuário e para metadados, em vez de existirem dois mecanismos.
Resposta
Porque torna os metadados acessíveis pela mesma linguagem dos dados: qualquer ferramenta que fale SQL consegue inspecionar o banco, sem interface proprietária.
Desafio
Um sistema tem uma visão que junta três tabelas e é consultada milhares de vezes por minuto, sempre com desempenho ruim. O DBA propõe materializar a visão. Explique o que isso significa em termos dos três níveis e qual novo problema a decisão introduz.
Dica
Materializar é passar a guardar o resultado. O que passa a existir em dois lugares?
Resolução comentada
Uma visão comum é apenas uma consulta guardada: nada é armazenado, e a junção é refeita a cada acesso. Materializá-la significa passar a armazenar fisicamente o resultado. Em termos dos três níveis, a definição no nível externo permanece idêntica — as aplicações continuam consultando o mesmo nome, com as mesmas colunas — e a mudança acontece no nível interno, que agora guarda uma cópia pré-computada. É, portanto, um ganho obtido sem alterar nada acima, o que é exatamente a promessa da independência física. O novo problema é que o resultado passa a existir em dois lugares: nas tabelas de origem e na cópia materializada. Isso é redundância, e reintroduz o risco de inconsistência — se as tabelas base mudarem e a cópia não for atualizada, a visão devolve dado velho. A decisão a tomar passa a ser a política de atualização: sincronizar a cada alteração (correto, porém caro na escrita) ou periodicamente (barato, mas admitindo uma janela de dados desatualizados). Ou seja, troca-se tempo de consulta por consistência e custo de escrita.
Resposta
A definição externa não muda; o nível interno passa a guardar o resultado pré-computado. O problema novo é a redundância: a cópia pode divergir das tabelas base, e é preciso decidir a política de atualização.
Resumo
Conceitos importantes
- A arquitetura ANSI/SPARC separa nível interno, conceitual e externo.
- Os mapeamentos entre níveis são o que torna a independência de dados possível.
- O catálogo (dicionário de dados) descreve o próprio banco e é consultável como qualquer tabela.
- Papéis: administrador de dados, DBA, projetista, programador e usuário final.
Checklist
- Sei nomear e descrever os três níveis.
- Sei classificar uma mudança no nível correto.
- Sei explicar o papel do DBA.
- Sei dizer o que é o catálogo e para que serve.
Pontos para revisão
- Por que os mapeamentos, e não os níveis em si, são o que garante a independência.
- Que problema uma visão materializada resolve e qual ela cria.
- ANSI/SPARC
- nível interno
- nível conceitual
- nível externo
- DBA
- catálogo
Modelagem conceitual e o modelo Entidade-Relacionamento
Modelo Entidade-Relacionamento (E-R)
O que é modelar conceitualmente, por que o modelo E-R é independente de SGBD e como se lê um diagrama E-R.
Em palavras simples
Antes de construir uma casa, desenha-se a planta. A planta não é feita de tijolos, e é justamente por isso que ela é útil: corrigir uma parede no papel custa um traço, e na obra custa uma demolição. O modelo E-R é a planta do banco de dados. Ele descreve o que existe no negócio — clientes, pedidos, produtos — e como essas coisas se ligam, sem falar de tabela, coluna ou SGBD.
Tecnicamente
A modelagem conceitual produz uma descrição do minimundo independente de qualquer tecnologia de implementação. O modelo Entidade-Relacionamento, proposto por Peter Chen em 1976, é a notação mais usada para isso. Seus elementos são entidades (conjuntos de objetos do mundo real com existência própria), atributos (propriedades das entidades) e relacionamentos (associações entre entidades). O modelo é deliberadamente pobre em recursos de implementação: não tem tipo físico, não tem índice, não tem chave estrangeira — porque nada disso pertence à pergunta que ele responde, que é "o que existe e como se liga". A tradução para tabelas vem depois, no projeto lógico, e é mecânica o suficiente para ser feita por regras. Modelar conceitualmente antes é o que permite discutir o domínio com quem entende do negócio e não entende de banco.
Principais conceitos
- Minimundo
- O recorte do mundo real que o banco de dados representa. Definir suas fronteiras é a primeira decisão da modelagem.
- Entidade
- Conjunto de objetos do mundo real com existência independente e propriedades em comum — Cliente, Produto, Turma.
- Atributo
- Propriedade que descreve uma entidade ou um relacionamento — nome, preço, data.
- Relacionamento
- Associação entre ocorrências de entidades — um Cliente FAZ um Pedido.
- Projeto conceitual
- Etapa que descreve o domínio sem compromisso com tecnologia. Precede o projeto lógico (tabelas) e o físico (armazenamento).
Exemplos
Da entrevista ao modelo
Um trecho de conversa com o cliente e a leitura que dele se faz. É o exercício central da modelagem conceitual.
CLIENTE DIZ:
"Cada aluno se matricula em várias disciplinas por semestre.
Toda disciplina é dada por um professor, e um professor
pode dar mais de uma disciplina."
LEITURA:
Entidades .......... Aluno, Disciplina, Professor
Relacionamentos .... Aluno -- matricula-se --> Disciplina
Professor -- leciona ----> Disciplina
Cardinalidades ..... aluno:disciplina = N:N
professor:disciplina = 1:NLinha a linha
Aluno, Disciplina, Professor- Os substantivos sobre os quais se quer guardar informação e que têm existência própria viram entidades.
matricula-se, leciona- Os verbos que ligam dois substantivos viram relacionamentos. O nome do relacionamento sai da própria fala do cliente.
"em várias disciplinas" e "pode dar mais de uma"- As expressões de quantidade são as que determinam a cardinalidade. É por isso que se anota a frase literal do cliente: ela carrega a regra.
Onde isso aparece na prática
- A entrevista com o cliente produz frases que viram entidades e relacionamentos quase diretamente: substantivos tendem a ser entidades, verbos tendem a ser relacionamentos.
- Ferramentas como brModelo, DBDesigner e o modo de modelagem do MySQL Workbench desenham E-R e geram o script SQL a partir dele.
- Revisar o E-R com o usuário final antes de programar é a forma mais barata de descobrir que o sistema entendeu o negócio errado.
Curiosidades
- O artigo de Peter Chen, "The Entity-Relationship Model — Toward a Unified View of Data", é um dos artigos de computação mais citados de todos os tempos.
- Existem várias notações para E-R: a original de Chen (losangos para relacionamentos), a de Engenharia da Informação (pé de galinha, ou crow's foot) e a de Barker. A do pé de galinha é a mais comum em ferramentas comerciais.
Exercícios
Básico
Identifique as entidades e os relacionamentos: "Uma editora publica vários livros. Cada livro é escrito por um ou mais autores."
Dica
Substantivos sobre os quais se guarda informação; verbos que ligam dois deles.
Resolução comentada
Entidades: Editora, Livro, Autor. Relacionamentos: Editora publica Livro; Autor escreve Livro. As expressões "vários livros" e "um ou mais autores" indicam as cardinalidades — 1:N entre editora e livro, N:N entre autor e livro.
Resposta
Entidades: Editora, Livro, Autor. Relacionamentos: publica (Editora–Livro) e escreve (Autor–Livro).
Intermediário
Por que o modelo conceitual não deve conter chaves estrangeiras nem tipos como VARCHAR(80)?
Dica
A que pergunta o modelo conceitual responde?
Resolução comentada
Porque nenhum dos dois pertence à pergunta que o modelo conceitual responde. Chave estrangeira é o mecanismo com que o modelo relacional implementa um relacionamento; num modelo conceitual o relacionamento já está representado diretamente, e antecipar a chave é misturar duas etapas. VARCHAR(80) é decisão de projeto físico, que depende do SGBD escolhido — e o modelo conceitual precisa sobreviver à troca de SGBD. Além disso, há uma razão prática: o modelo conceitual é o documento que se revisa com quem entende do negócio, e essa pessoa não tem como validar um tipo de dado, mas tem plena condição de dizer se um cliente pode ou não ter dois endereços.
Resposta
Porque ambos são decisões de implementação. O conceitual descreve o domínio e precisa sobreviver à troca de SGBD e ser validável por quem entende do negócio, não de banco.
Avançado
Um analista modelou "Endereço" como atributo de Cliente. Outro modelou como entidade. Em que situação cada decisão está certa?
Dica
Pergunte se o endereço tem existência e identidade próprias no negócio.
Resolução comentada
Endereço como atributo está certo quando o negócio trata o endereço como uma propriedade simples do cliente, sem existência própria: cada cliente tem um endereço, ninguém precisa consultar endereços independentemente de clientes, e não há dado a guardar sobre o endereço em si. É o caso de um cadastro simples de correspondência. Endereço como entidade está certo quando ele adquire identidade própria — quando um cliente pode ter vários (cobrança, entrega, fiscal), quando dois clientes podem compartilhar o mesmo endereço, quando é preciso guardar dados sobre o endereço (coordenadas, zona de entrega, restrição de acesso) ou quando outras entidades além de cliente também se ligam a endereços. O critério não é estético: é a existência independente. Se a resposta a "faz sentido perguntar algo sobre este endereço sem falar de cliente nenhum?" for sim, é entidade.
Resposta
Atributo quando o endereço é propriedade simples e única do cliente; entidade quando tem existência independente — vários por cliente, compartilhado, ou com dados próprios.
Desafio
Discuta esta afirmação: "Como no final tudo vira tabela, modelar em E-R é perda de tempo — melhor já desenhar as tabelas."
Dica
O que se perde ao pular o desenho e ir direto para a obra?
Resolução comentada
A afirmação confunde o destino com o caminho, e três consequências mostram por quê. Primeira: o modelo E-R é o artefato que se valida com o especialista do domínio, que não lê DDL. Pulando-o, o erro de entendimento do negócio só aparece quando o sistema já está escrito — e é o erro mais caro que existe, porque nenhuma correção de código o resolve. Segunda: o E-R representa relacionamentos N:N diretamente, enquanto o modelo relacional exige tabela associativa. Desenhando tabelas de saída, essa tabela associativa é inventada antes de o relacionamento ter sido compreendido, e é comum ela nascer sem os atributos que o relacionamento tinha. Terceira: decisões que no E-R são explícitas — se algo é entidade ou atributo, se um relacionamento é obrigatório — ficam implícitas no DDL, escondidas em NOT NULL e em nomes de coluna, e deixam de ser discutíveis. Dito isso, a afirmação tem um fundo legítimo: em domínios pequenos e já conhecidos, um E-R cerimonioso e cheio de notação é burocracia. A resposta madura não é abandonar a modelagem conceitual, e sim ajustar seu rigor ao tamanho do problema — um rascunho de dez minutos num quadro já colhe quase todo o benefício.
Resposta
É falsa como regra: o E-R é o que se valida com o especialista do domínio, representa N:N diretamente e torna explícitas decisões que o DDL esconde. Mas o rigor da notação deve ser proporcional ao tamanho do problema.
Resumo
Conceitos importantes
- Modelagem conceitual descreve o minimundo sem compromisso com tecnologia.
- O modelo E-R (Chen, 1976) tem três elementos: entidade, atributo e relacionamento.
- O conceitual precede o lógico (tabelas) e o físico (armazenamento).
- O modelo conceitual é o artefato validável por quem entende do negócio.
Checklist
- Sei extrair entidades e relacionamentos de uma descrição em texto.
- Sei justificar por que tipos e chaves estrangeiras não entram no conceitual.
- Sei decidir entre modelar algo como atributo ou como entidade.
Pontos para revisão
- O critério da existência independente para separar entidade de atributo.
- O que se perde ao pular o modelo conceitual e desenhar tabelas direto.
- modelo E-R
- minimundo
- entidade
- atributo
- relacionamento
- projeto conceitual
Primitivas básicas do modelo E-R: entidades, atributos e relacionamentos
Modelo Entidade-Relacionamento (E-R)
As primitivas do modelo E-R em detalhe: tipos de atributo, identificadores, grau de relacionamento e entidades fracas.
Em palavras simples
Já sabemos que existem entidades, atributos e relacionamentos. Agora vêm as variações de cada um. Atributo pode ser simples ou composto (endereço se divide em rua, número e cidade), pode valer um só ou vários (uma pessoa tem vários telefones) e pode ser calculado a partir de outro (idade sai da data de nascimento). Entidade precisa de algo que a identifique. E relacionamento pode ligar duas entidades, três, ou uma entidade a ela mesma.
Tecnicamente
Os atributos classificam-se em: simples ou compostos (decomponíveis em partes com significado próprio); monovalorados ou multivalorados (admitem um ou vários valores para a mesma ocorrência); armazenados ou derivados (calculáveis a partir de outros); e identificadores ou descritivos. O identificador — atributo-chave — é o que distingue univocamente cada ocorrência da entidade; quando nenhum atributo isolado basta, usa-se um identificador composto. O grau de um relacionamento é o número de entidades que ele associa: binário (dois), ternário (três) ou de grau n. O caso do autorrelacionamento, ou relacionamento recursivo, liga uma entidade a ela própria, e nele os papéis precisam ser nomeados para que a leitura não seja ambígua. Entidade fraca é a que não possui identificador próprio e depende da existência de outra entidade — a proprietária — para ser identificada, usando um identificador parcial combinado com a chave da proprietária.
Principais conceitos
- Atributo composto
- Decomponível em partes com significado próprio, como endereço em logradouro, número, cidade e CEP.
- Atributo multivalorado
- Admite vários valores para a mesma ocorrência, como os telefones de um cliente.
- Atributo derivado
- Calculável a partir de outros dados; guardá-lo é redundância, e mantê-lo desatualizado é o risco correspondente.
- Identificador (chave)
- Atributo ou conjunto de atributos que distingue univocamente cada ocorrência da entidade.
- Grau do relacionamento
- Quantidade de entidades que o relacionamento associa: binário, ternário ou n-ário.
- Entidade fraca
- Não tem identificador próprio; é identificada pela combinação de um identificador parcial com a chave da entidade proprietária.
Exemplos
Uma entidade com todos os tipos de atributo
Cada linha marca uma classificação diferente. É o vocabulário que a prova cobra.
ENTIDADE: Cliente
cpf .................. identificador, simples, monovalorado
nome ................. descritivo, simples, monovalorado
endereco ............. descritivo, COMPOSTO
+-- logradouro
+-- numero
+-- cidade
telefone ............. descritivo, MULTIVALORADO { }
data_nascimento ...... descritivo, armazenado
idade ................ DERIVADO ( ) <- calculado de data_nascimentoLinha a linha
cpf → identificador- É o que distingue uma ocorrência de outra. Na notação de Chen aparece sublinhado.
endereco → composto- Tem partes com significado próprio. Decompor ou não é decisão: só decomponha se o negócio consultar as partes separadamente.
telefone → multivalorado { }- Vários valores para o mesmo cliente. Na tradução para o relacional isto obrigatoriamente vira outra tabela — o modelo relacional não admite campo com vários valores.
idade → derivado ( )- Calculado, não armazenado. Guardá-lo criaria um dado que fica errado sozinho na passagem de cada aniversário.
Autorrelacionamento com papéis
Uma entidade ligada a ela mesma. Sem nomear os papéis, o diagrama fica ambíguo — não se sabe quem chefia quem.
+-------------+
| Funcionario |
+------+------+
| |
(chefe)| |(subordinado)
| |
+---------------+
| supervisiona
+---------------+
Leitura: um Funcionario, no papel de CHEFE, supervisiona
N Funcionarios, no papel de SUBORDINADO.Linha a linha
(chefe) / (subordinado)- Os papéis. São obrigatórios em autorrelacionamento: sem eles, as duas pontas são indistinguíveis e o modelo não diz qual lado é qual.
1:N- Um chefe supervisiona vários subordinados; cada subordinado tem um chefe. A cardinalidade se lê ponta a ponta, como em qualquer relacionamento binário.
Onde isso aparece na prática
- Telefone modelado como atributo multivalorado é o caso que mais frequentemente vira uma tabela separada na tradução para o relacional.
- Idade como atributo derivado da data de nascimento evita o clássico bug do cadastro que envelhece só quando alguém edita o registro.
- Item de nota fiscal é o exemplo canônico de entidade fraca: o item 1 só faz sentido dentro de uma nota específica.
Curiosidades
- Relacionamentos ternários são raros e frequentemente sinal de modelagem apressada: boa parte deles decompõe-se em três binários sem perda de informação — mas nem todos, e distinguir os casos é uma das análises mais difíceis da modelagem.
- O termo "entidade fraca" não sugere fragilidade: refere-se apenas à dependência de identificação, e essas entidades costumam ser as mais numerosas do banco.
Exercícios
Básico
Classifique os atributos de Livro: isbn, titulo, autores, ano_publicacao, idade_do_livro.
Dica
Um deles é identificador, um é multivalorado e um é derivado.
Resolução comentada
isbn: identificador, simples, monovalorado — distingue univocamente cada livro. titulo: descritivo, simples, monovalorado. autores: descritivo, multivalorado — um livro pode ter vários. ano_publicacao: descritivo, simples, armazenado. idade_do_livro: derivado — calculado do ano de publicação em relação ao ano corrente, e por isso não deve ser armazenado.
Resposta
isbn = identificador; titulo e ano_publicacao = descritivos simples; autores = multivalorado; idade_do_livro = derivado.
Intermediário
Explique o que é uma entidade fraca e dê um exemplo diferente do apresentado na aula, indicando o identificador parcial.
Dica
Procure algo cujo número recomeça do 1 dentro de cada 'pai'.
Resolução comentada
Entidade fraca é a que não tem identificador próprio e depende da existência de outra entidade — a proprietária — para ser identificada. Sua chave é a combinação de um identificador parcial com a chave da proprietária. Exemplo: Dependente de um funcionário num plano de saúde. O identificador parcial é o nome do dependente, que só é único dentro de um mesmo funcionário — existem muitos dependentes chamados "João" na empresa, mas apenas um entre os dependentes do funcionário 4021. A chave completa é, portanto, (matricula_funcionario, nome_dependente). Se o funcionário for removido, seus dependentes deixam de ter sentido e são removidos junto: a dependência é existencial, não apenas de identificação.
Resposta
É a entidade sem identificador próprio, identificada por um identificador parcial mais a chave da proprietária. Ex.: Dependente, com identificador parcial nome, dentro de Funcionário.
Avançado
Um sistema guarda o campo total do pedido, que é a soma dos itens. É um atributo derivado. Em que caso guardá-lo é a decisão certa?
Dica
O que acontece com o total de um pedido quando o preço do produto muda no ano seguinte?
Resolução comentada
Guardar é a decisão certa em dois casos. O primeiro é histórico: se o total for sempre recalculado a partir dos preços atuais, um pedido fechado em 2024 passa a exibir o valor de 2026 quando os preços subirem — o sistema reescreve o passado. Guardando o total (e os preços unitários praticados), o pedido registra o que de fato foi cobrado, que é um dado legal e contábil, não um cálculo. O segundo é de desempenho: um relatório que soma o faturamento de milhões de pedidos recalculando itens toda vez faz um trabalho enorme para chegar a um número que nunca mais muda. Nos dois casos a redundância deixa de ser descontrolada e passa a ser controlada — desde que a regra seja explícita: o total é congelado no fechamento do pedido e não é recalculado depois. O erro seria guardar o total e continuar permitindo edição de itens sem recalcular, aí sim criando um dado que mente em silêncio.
Resposta
Quando o valor é histórico (o que foi efetivamente cobrado, imune a mudanças futuras de preço) ou quando o custo de recalcular em relatórios é proibitivo — sempre com a regra de congelamento explícita.
Desafio
Modele: um médico atende um paciente em uma data, e nesse atendimento pode prescrever vários medicamentos. Discuta se o relacionamento é ternário e o que a alternativa mudaria.
Dica
Pergunte se "atendimento" tem atributos e existência próprios.
Resolução comentada
A tentação é modelar um relacionamento ternário entre Médico, Paciente e Medicamento. Ele é tecnicamente possível, mas esconde um problema: a data, o diagnóstico e as observações não pertencem a nenhuma das três entidades — são propriedades do encontro. E um relacionamento ternário só admite um conjunto de atributos por combinação das três pontas, o que impede, por exemplo, que o mesmo médico atenda o mesmo paciente duas vezes prescrevendo o mesmo medicamento em datas diferentes. A alternativa melhor é promover o encontro a entidade: Atendimento, com identificador próprio, ligada a Médico (1:N) e a Paciente (1:N), e ligada a Medicamento por um relacionamento N:N que carrega dose e posologia. Isso decompõe o ternário em binários, permite repetição ao longo do tempo, dá lugar natural aos atributos do encontro e ainda torna o modelo capaz de representar um atendimento sem nenhuma prescrição — situação perfeitamente real que o ternário não conseguiria registrar, já que um relacionamento ternário exige as três pontas presentes. A regra prática que fica: quando um relacionamento tem atributos próprios e pode se repetir no tempo, ele quer ser uma entidade.
Resposta
Não deve ser ternário. Atendimento deve virar entidade, ligada a Médico e Paciente por relacionamentos binários e a Medicamento por N:N — só assim cabem os atributos do encontro, a repetição no tempo e o atendimento sem prescrição.
Resumo
Conceitos importantes
- Atributos: simples/composto, monovalorado/multivalorado, armazenado/derivado, identificador/descritivo.
- O identificador distingue univocamente cada ocorrência; pode ser composto.
- Grau do relacionamento: binário, ternário, n-ário; autorrelacionamento exige papéis nomeados.
- Entidade fraca não tem identificador próprio: usa identificador parcial + chave da proprietária.
Checklist
- Sei classificar qualquer atributo nas quatro dimensões.
- Sei identificar uma entidade fraca e sua chave.
- Sei nomear papéis num autorrelacionamento.
- Sei justificar quando um relacionamento deve virar entidade.
Pontos para revisão
- Por que atributo multivalorado sempre vira tabela no modelo relacional.
- O sinal de que um relacionamento deveria ser uma entidade.
- atributo composto
- multivalorado
- derivado
- identificador
- entidade fraca
- autorrelacionamento
Cardinalidade e restrições de integridade no modelo E-R
Modelo Entidade-Relacionamento (E-R)
Como se lê e se anota a cardinalidade de um relacionamento, a diferença entre cardinalidade máxima e mínima, e por que a mínima é a que carrega a regra de negócio mais esquecida.
Em palavras simples
Cardinalidade responde a duas perguntas sobre cada ponta do relacionamento: quantos, no máximo, e quantos, no mínimo. "Um cliente pode ter vários pedidos" responde ao máximo. "Todo pedido precisa ter um cliente" responde ao mínimo. A segunda pergunta é a que quase todo mundo esquece de fazer — e é dela que saem os campos que aceitam vazio quando não deviam.
Tecnicamente
A cardinalidade máxima indica quantas ocorrências de uma entidade podem se associar a uma ocorrência da outra: 1:1, 1:N ou N:N. A cardinalidade mínima indica quantas ocorrências devem existir — 0 quando a participação é opcional (parcial) e 1 quando é obrigatória (total). Anota-se o par (mín, máx) em cada ponta, e a leitura é cruzada: a cardinalidade anotada de um lado descreve quantas ocorrências daquele lado se ligam a UMA ocorrência do outro. Participação total desenha-se com linha dupla na notação de Chen. A cardinalidade máxima determina como o relacionamento será implementado no modelo relacional — 1:N vira chave estrangeira no lado N, N:N obriga tabela associativa. A cardinalidade mínima determina se essa chave estrangeira aceita NULL. São, portanto, decisões de modelagem com consequência direta e verificável no DDL.
Principais conceitos
- Cardinalidade máxima
- Número máximo de ocorrências de uma entidade que podem se associar a uma ocorrência da outra. Produz os tipos 1:1, 1:N e N:N.
- Cardinalidade mínima
- Número mínimo exigido: 0 para participação opcional, 1 para obrigatória. É o que determina se a chave estrangeira aceita NULL.
- Participação total
- Cardinalidade mínima 1: toda ocorrência da entidade precisa participar do relacionamento. Desenha-se com linha dupla.
- Participação parcial
- Cardinalidade mínima 0: a ocorrência pode existir sem participar do relacionamento.
- Atributo de relacionamento
- Propriedade que não pertence a nenhuma das entidades, e sim à associação entre elas — a nota de um aluno numa disciplina, por exemplo.
Exemplos
As três cardinalidades máximas, lidas em voz alta
A leitura é sempre cruzada. Escrever a frase antes de desenhar evita metade dos erros.
1:1 Funcionario -- gerencia --> Departamento
"Um funcionário gerencia no máximo UM departamento."
"Um departamento é gerenciado por no máximo UM funcionário."
1:N Cliente -- faz --> Pedido
"Um cliente faz VÁRIOS pedidos."
"Um pedido é feito por UM cliente."
N:N Aluno -- matricula-se --> Disciplina
"Um aluno se matricula em VÁRIAS disciplinas."
"Uma disciplina recebe VÁRIOS alunos."Linha a linha
1:1 gerencia- Repare que nem todo funcionário gerencia algo: a cardinalidade mínima do lado do funcionário é 0. Máxima e mínima são perguntas independentes.
1:N faz- No relacional, o lado N recebe a chave estrangeira. Pedido guarda o id do cliente — nunca o contrário.
N:N matricula-se- Nenhum dos dois lados consegue guardar a referência do outro. Obriga tabela associativa, e é nela que cabem atributos do relacionamento como a nota.
A cardinalidade mínima virando DDL
A mesma decisão de modelagem, escrita duas vezes: uma no diagrama, outra no banco.
-- (1,1) do lado do pedido: TODO pedido tem cliente.
CREATE TABLE pedido (
id INTEGER PRIMARY KEY,
cliente_id INTEGER NOT NULL REFERENCES cliente(id),
data DATE NOT NULL
);
-- (0,1): um funcionário PODE não gerenciar departamento nenhum.
CREATE TABLE departamento (
id INTEGER PRIMARY KEY,
gerente_id INTEGER NULL REFERENCES funcionario(id)
);Linha a linha
cliente_id INTEGER NOT NULL- O NOT NULL é a cardinalidade mínima 1 traduzida. Sem ele, o banco aceita pedido sem cliente — e o relatório de faturamento por cliente perde linhas em silêncio.
REFERENCES cliente(id)- A chave estrangeira é a cardinalidade máxima traduzida: um pedido aponta para no máximo um cliente, porque a coluna guarda um valor só.
gerente_id INTEGER NULL- Aqui o NULL é intencional e documentado: departamento sem gerente é situação real, e não erro de cadastro.
Onde isso aparece na prática
- A cardinalidade mínima é o que vira NOT NULL na chave estrangeira; errá-la produz o clássico pedido órfão, sem cliente.
- Descobrir que um relacionamento é N:N e não 1:N muda a estrutura do banco inteiro — é o erro de modelagem mais caro de corrigir depois.
- Perguntar "pode existir sem?" ao usuário final é a forma mais rápida de levantar a cardinalidade mínima, que ele nunca informa espontaneamente.
Curiosidades
- A notação pé de galinha (crow's foot) nasceu num relatório técnico de Gordon Everest em 1976; o "pé" representa o lado N, e os traços e círculos junto dele marcam a cardinalidade mínima.
- Um relacionamento 1:1 com participação total dos dois lados é quase sempre sinal de que as duas entidades deveriam ser uma só — a exceção legítima é quando os dados são separados por segurança ou por frequência de acesso.
Exercícios
Básico
Determine a cardinalidade máxima: (a) Autor e Livro; (b) Estado e Cidade; (c) Pessoa e CPF.
Dica
Leia dos dois lados antes de responder.
Resolução comentada
(a) N:N — um autor escreve vários livros e um livro pode ter vários autores. (b) 1:N — um estado tem várias cidades, mas cada cidade pertence a um só estado. (c) 1:1 — uma pessoa tem um CPF e um CPF pertence a uma só pessoa.
Resposta
(a) N:N; (b) 1:N; (c) 1:1.
Intermediário
Explique a diferença prática entre participação total e parcial, dizendo o que muda no banco de dados gerado.
Dica
Pense na coluna da chave estrangeira.
Resolução comentada
Participação total significa cardinalidade mínima 1: toda ocorrência da entidade obrigatoriamente participa do relacionamento. Participação parcial significa mínima 0: a ocorrência pode existir isolada. No banco, a diferença aparece na chave estrangeira: participação total vira NOT NULL, participação parcial permite NULL. Se um pedido tem participação total no relacionamento com cliente, a coluna cliente_id é NOT NULL e o SGBD passa a recusar qualquer tentativa de gravar pedido sem dono — a regra de negócio deixa de depender da aplicação lembrar de validá-la.
Resposta
Total = mínima 1 = chave estrangeira NOT NULL; parcial = mínima 0 = chave estrangeira aceita NULL.
Avançado
Uma escola quer registrar a nota do aluno em cada disciplina. Onde a nota deve ficar? Justifique.
Dica
De quem é a nota: do aluno, da disciplina, ou de outra coisa?
Resolução comentada
A nota não pertence ao aluno nem à disciplina. Colocada em Aluno, haveria uma nota só, e o aluno cursa várias disciplinas. Colocada em Disciplina, haveria uma nota só para a turma inteira. A nota pertence ao encontro entre os dois — é atributo do relacionamento matricula-se, que é N:N. No modelo E-R ela se desenha ligada ao losango do relacionamento. Na tradução para o relacional, o relacionamento N:N vira uma tabela associativa (matricula), cuja chave primária é o par (aluno_id, disciplina_id), e a nota é uma coluna comum dessa tabela. A regra geral que fica: todo atributo que só faz sentido quando as duas pontas estão presentes é atributo do relacionamento, não das entidades.
Resposta
No relacionamento matricula-se, como atributo de relacionamento — que vira coluna da tabela associativa (aluno_id, disciplina_id, nota).
Desafio
Um analista modelou Pessoa e Passaporte como 1:1 com participação total nos dois lados. Critique a decisão e diga quando ela seria correta.
Dica
Toda pessoa tem passaporte?
Resolução comentada
A crítica principal é factual: participação total do lado de Pessoa afirma que toda pessoa cadastrada tem passaporte, o que é falso na esmagadora maioria dos domínios. O correto seria (0,1) do lado da pessoa e (1,1) do lado do passaporte — todo passaporte pertence a alguém, mas nem toda pessoa tem passaporte. Modelado como está, ou o cadastro de pessoa fica impossível sem antes emitir um passaporte, ou a regra é ignorada na implementação e o modelo passa a mentir sobre o sistema. Há ainda a questão estrutural: quando um 1:1 tem participação total dos dois lados, as duas entidades sempre aparecem juntas e a recomendação usual é fundi-las numa só tabela, já que separá-las só acrescenta uma junção sem ganho. As exceções legítimas para manter separado são duas: quando os dados têm requisitos de segurança diferentes (dados sensíveis numa tabela com permissão restrita) e quando têm frequências de acesso muito diferentes (um bloco grande e raramente lido separado do núcleo consultado o tempo todo). Neste caso concreto, porém, nada disso se aplica — o problema real é que a cardinalidade mínima do lado da pessoa está simplesmente errada.
Resposta
A participação total do lado de Pessoa está errada: deveria ser (0,1), pois nem toda pessoa tem passaporte. E 1:1 total dos dois lados normalmente pede fusão numa tabela só, salvo separação por segurança ou por frequência de acesso.
Resumo
Conceitos importantes
- Cardinalidade máxima gera 1:1, 1:N e N:N e determina como o relacionamento vira tabela.
- Cardinalidade mínima (0 ou 1) determina se a chave estrangeira aceita NULL.
- Participação total = obrigatória; parcial = opcional.
- Atributo que só existe quando as duas pontas existem é atributo do relacionamento.
Checklist
- Sei ler a cardinalidade em voz alta, dos dois lados.
- Sei dizer o que muda no DDL entre participação total e parcial.
- Sei identificar um atributo de relacionamento.
- Sei justificar quando um 1:1 deveria virar uma tabela só.
Pontos para revisão
- Por que a cardinalidade mínima é a mais esquecida e a que mais gera bug.
- Por que N:N sempre exige tabela associativa.
- cardinalidade
- 1:N
- N:N
- participação total
- atributo de relacionamento
Mecanismos de abstração: generalização, especialização e agregação
Modelo Entidade-Relacionamento (E-R)
Os mecanismos que organizam o modelo quando entidades compartilham atributos ou quando um relacionamento precisa participar de outro relacionamento.
Em palavras simples
Às vezes várias entidades têm quase os mesmos atributos. Pessoa Física e Pessoa Jurídica são clientes: as duas têm nome, endereço e telefone, e cada uma tem os seus campos próprios. Repetir os campos comuns nas duas é convite a esquecer de alterar um deles. A generalização junta o que é comum numa entidade genérica, e deixa em cada especializada só o que a distingue.
Tecnicamente
Generalização é o processo de abstrair, a partir de entidades semelhantes, uma entidade genérica que reúne seus atributos comuns; especialização é o caminho inverso, partindo da genérica para as específicas. A hierarquia resultante tem herança: toda ocorrência da especializada é também ocorrência da genérica e possui todos os seus atributos. A hierarquia classifica-se por duas dimensões independentes. Quanto à cobertura, é total quando toda ocorrência da genérica pertence a alguma especializada, e parcial quando pode existir ocorrência que não pertence a nenhuma. Quanto à disjunção, é exclusiva (disjunta) quando uma ocorrência pertence a no máximo uma especializada, e compartilhada (sobreposta) quando pode pertencer a várias. Agregação é o mecanismo que trata um relacionamento inteiro como se fosse uma entidade, para que ele possa participar de outro relacionamento — necessário quando algo se relaciona não com uma entidade, mas com a associação entre duas.
Principais conceitos
- Generalização
- Abstração que reúne, numa entidade genérica, os atributos comuns a entidades semelhantes.
- Especialização
- Caminho inverso: a partir de uma entidade genérica, definem-se subconjuntos com atributos próprios.
- Cobertura total ou parcial
- Total: toda ocorrência da genérica está em alguma especializada. Parcial: pode não estar em nenhuma.
- Disjunção exclusiva ou compartilhada
- Exclusiva: a ocorrência pertence a no máximo uma especializada. Compartilhada: pode pertencer a várias ao mesmo tempo.
- Agregação
- Trata um relacionamento como entidade, para que ele possa participar de outro relacionamento.
Exemplos
Uma hierarquia com as duas dimensões declaradas
Cobertura e disjunção são perguntas separadas, e as quatro combinações existem. Não declarar é deixar a regra implícita.
+-----------+
| Pessoa | nome, endereco, telefone
+-----+-----+
|
(t, x) total e exclusiva
+-------+-------+
| |
+-------+------+ +-----+--------+
| PessoaFisica | | PessoaJuridica|
+--------------+ +---------------+
cpf, nascimento cnpj, razao_social
t = toda pessoa é física OU jurídica (cobertura total)
x = nenhuma é as duas ao mesmo tempo (exclusiva)Linha a linha
nome, endereco, telefone em Pessoa- Os atributos comuns sobem para a genérica. Alterar a regra do telefone passa a ser uma alteração só, e não duas que podem divergir.
(t, x)- As duas dimensões declaradas. Aqui a cobertura é total e a disjunção é exclusiva — o caso mais comum, mas não o único possível.
cpf apenas em PessoaFisica- O que distingue fica só na especializada. Numa tabela única, essa coluna ficaria nula em toda linha de pessoa jurídica.
Agregação: quando o relacionamento precisa se relacionar
Sem agregação, não há como dizer que o técnico atendeu a uma combinação específica de equipamento e defeito.
SEM AGREGAÇÃO (não expressa o que se quer):
Equipamento -- apresenta --> Defeito
Tecnico -- atende ----> ??? (atende o quê? o equipamento? o defeito?)
COM AGREGAÇÃO:
+------------------------------------+
| Equipamento -- apresenta --> Defeito | <- o relacionamento inteiro,
+------------------------------------+ tratado como uma entidade
|
atende
|
+---------+
| Tecnico |
+---------+Linha a linha
atende --> ???- O problema: ligar o técnico só ao equipamento perde o defeito; ligar só ao defeito perde o equipamento. É a combinação dos dois que ele atendeu.
o retângulo em volta do relacionamento- É a notação da agregação: o relacionamento apresenta passa a ser tratado como uma entidade, e como tal pode participar de outro relacionamento.
Onde isso aparece na prática
- Cliente pessoa física e jurídica é o caso mais comum de generalização em sistemas comerciais brasileiros.
- Um catálogo de produtos com tipos muito diferentes (livro, eletrônico, alimento) resolve-se por especialização, evitando uma tabela com dezenas de colunas quase sempre nulas.
- Agregação aparece quando é preciso registrar algo sobre um relacionamento: o técnico que atendeu determinada peça em determinado equipamento.
Curiosidades
- A generalização no E-R e a herança da orientação a objetos resolvem o mesmo problema conceitual, mas com uma diferença importante: no E-R a hierarquia é sobre conjuntos de ocorrências, não sobre comportamento — não há métodos a herdar.
- Existem três formas usuais de traduzir uma hierarquia para tabelas, e nenhuma é a certa em todos os casos: uma tabela por classe, uma tabela única com coluna discriminadora, ou uma tabela só para as folhas.
Exercícios
Básico
Numa universidade há Alunos e Professores, e ambos têm nome, CPF e endereço. Proponha uma generalização.
Dica
O que é comum sobe; o que distingue fica.
Resolução comentada
Cria-se a entidade genérica Pessoa, com nome, CPF e endereço. Aluno e Professor tornam-se especializações: Aluno acrescenta matrícula e semestre; Professor acrescenta titulação e regime de trabalho. A cobertura é parcial se a universidade puder cadastrar pessoas que não sejam nem aluno nem professor (um visitante, por exemplo), e a disjunção é compartilhada se alguém puder ser aluno e professor ao mesmo tempo — situação real em pós-graduação.
Resposta
Pessoa (nome, CPF, endereço) como genérica; Aluno (matrícula, semestre) e Professor (titulação, regime) como especializações.
Intermediário
Classifique quanto a cobertura e disjunção: (a) Veículo em Carro e Moto; (b) Funcionário em Gerente e Técnico, sabendo que há funcionários que não são nem um nem outro e que ninguém acumula os dois papéis.
Dica
São duas perguntas independentes.
Resolução comentada
(a) Cobertura total e disjunção exclusiva: todo veículo é carro ou moto, e nenhum é os dois. (b) Cobertura parcial, porque existem funcionários fora das duas categorias, e disjunção exclusiva, porque ninguém acumula os papéis. O enunciado de (b) informa exatamente as duas dimensões — é assim que elas devem ser levantadas com o cliente: perguntando "existe algum que não seja nenhum dos dois?" e "pode ser os dois ao mesmo tempo?".
Resposta
(a) total e exclusiva; (b) parcial e exclusiva.
Avançado
Explique quando usar agregação em vez de simplesmente transformar o relacionamento numa entidade.
Dica
Compare o que cada solução preserva do modelo original.
Resolução comentada
As duas soluções resolvem o mesmo problema e produzem, na prática, tabelas muito parecidas. A agregação é preferível quando se quer preservar no modelo a informação de que aquilo é um relacionamento entre duas entidades, e não um conceito autônomo do domínio — o vínculo entre equipamento e defeito continua sendo um vínculo, e a agregação o mantém legível como tal, com sua cardinalidade explícita. Transformar em entidade é preferível quando o conceito ganha nome próprio no vocabulário do negócio, atributos próprios e identidade independente: quando as pessoas passam a falar em "a ocorrência", "o chamado", "o atendimento", o conceito já se emancipou e insistir na agregação torna o diagrama mais difícil de ler do que precisaria. O critério prático é linguístico: se o negócio tem um substantivo para aquilo, é entidade; se só consegue descrevê-lo como "a ligação entre X e Y", é agregação.
Resposta
Agregação quando o vínculo continua sendo um relacionamento e se quer preservar isso no modelo; entidade quando o conceito ganha nome próprio, atributos e identidade no vocabulário do negócio.
Desafio
Uma hierarquia de Produto tem 12 especializações, cada uma com 3 a 8 atributos próprios. Discuta as três estratégias de tradução para tabelas e recomende uma.
Dica
Pense em quantas colunas nulas e quantas junções cada opção produz.
Resolução comentada
A primeira estratégia é a tabela única com coluna discriminadora: uma tabela Produto com o tipo e todas as colunas de todas as especializações. Com 12 especializações e até 8 atributos cada, chega-se a algo em torno de 60 colunas, das quais cada linha preenche poucas — o resto é NULL. A consulta fica simples e sem junção, mas a integridade se perde: nada impede gravar um livro com prazo de validade, porque a coluna existe para todos, e restrições NOT NULL tornam-se impossíveis nos atributos específicos. A segunda é uma tabela por especialização, sem tabela para a genérica: cada tipo com suas colunas próprias mais as comuns repetidas. Ganha-se integridade e não há coluna nula, mas perde-se a genérica — listar todos os produtos exige UNION de 12 tabelas, e nenhuma chave estrangeira consegue apontar para "um produto qualquer". A terceira é uma tabela para a genérica e uma para cada especializada, ligadas por chave primária compartilhada. Não há coluna nula, a integridade específica é declarável, existe uma tabela Produto para as chaves estrangeiras apontarem, e listar tudo é ler uma tabela só. O custo é uma junção sempre que se quer o registro completo de um tipo. Com 12 especializações e atributos numerosos, recomendo a terceira: os problemas das outras duas crescem com o número de especializações, enquanto o custo da terceira é constante — uma junção — e é o mais fácil de aceitar. A primeira só se justificaria com poucas especializações e pouquíssimos atributos próprios.
Resposta
Tabela única gera ~60 colunas quase sempre nulas e impede restrições; tabela por especializada impede referenciar "um produto qualquer" e exige UNION. Com 12 especializações, recomendo genérica + uma por especializada com chave compartilhada: custo fixo de uma junção.
Resumo
Conceitos importantes
- Generalização reúne o comum; especialização define os subconjuntos.
- Cobertura (total/parcial) e disjunção (exclusiva/compartilhada) são dimensões independentes.
- Agregação permite que um relacionamento participe de outro relacionamento.
- Há três estratégias de tradução de hierarquia para tabelas, cada uma com um custo diferente.
Checklist
- Sei propor uma generalização a partir de entidades semelhantes.
- Sei classificar uma hierarquia nas duas dimensões.
- Sei reconhecer a situação que exige agregação.
- Sei comparar as estratégias de tradução de hierarquia.
Pontos para revisão
- As duas perguntas que definem cobertura e disjunção.
- O critério linguístico entre agregação e entidade.
- generalização
- especialização
- cobertura total
- disjunção exclusiva
- agregação
- herança
Uso de uma ferramenta de modelagem
Modelo Entidade-Relacionamento (E-R)
O que uma ferramenta de modelagem faz por você, o que ela não faz, e o fluxo de trabalho entre modelo conceitual, modelo lógico e script SQL.
Em palavras simples
A ferramenta de modelagem desenha o diagrama, verifica se ele é coerente e gera o script SQL que cria as tabelas. O que ela não faz — e isto é o mais importante — é dizer se o modelo está certo. Ela garante que o desenho é válido; se ele representa o negócio corretamente, só quem conhece o negócio sabe.
Tecnicamente
Uma ferramenta de modelagem opera em pelo menos dois níveis: o modelo conceitual (entidades e relacionamentos) e o modelo lógico (tabelas, colunas, chaves), com uma função de conversão entre eles que aplica as regras de transformação. A partir do lógico, gera o DDL para o SGBD escolhido — e é aqui que o modelo deixa de ser independente de tecnologia. As ferramentas costumam oferecer também engenharia reversa: ler um banco existente e reconstruir o diagrama a partir do catálogo, o que é o caminho usual para documentar um sistema legado. Ferramentas comuns no ensino brasileiro incluem o brModelo, desenvolvido especificamente para a notação de Chen e para o método de Heuser, e o MySQL Workbench, que trabalha diretamente no nível lógico com notação pé de galinha. O ponto de atenção do fluxo é a sincronização: uma vez gerado o banco, alterações feitas direto no SGBD não voltam para o modelo, e o diagrama envelhece até virar ficção.
Principais conceitos
- Modelo conceitual (na ferramenta)
- O diagrama E-R propriamente dito: entidades, atributos, relacionamentos e cardinalidades, sem tabelas.
- Modelo lógico
- O resultado da conversão: tabelas, colunas, chaves primárias e estrangeiras, já no vocabulário do modelo relacional.
- Engenharia reversa
- Ler um banco existente e reconstruir o diagrama a partir do catálogo do SGBD.
- Forward engineering
- O caminho normal: gerar o script DDL a partir do modelo.
- Sincronização
- Comparar modelo e banco e reconciliar as diferenças. Sem ela, o diagrama vira documentação falsa.
Exemplos
O fluxo completo, do desenho ao banco
Cada seta é uma operação da ferramenta. Repare em qual delas o modelo perde a independência de SGBD.
[ Entrevista ]
|
v
[ MODELO CONCEITUAL ] entidades, relacionamentos, cardinalidades
| independente de SGBD
| conversão (regras de transformação)
v
[ MODELO LÓGICO ] tabelas, colunas, PK, FK
| ainda independente de SGBD
| geração de DDL <-- AQUI escolhe-se o SGBD
v
[ SCRIPT SQL ] CREATE TABLE ... específico do produto
|
v
[ BANCO DE DADOS ]
|
| engenharia reversa (caminho de volta, para legado)
v
[ MODELO LÓGICO reconstruído ]Linha a linha
conversão- Aplica as regras de transformação: 1:N vira chave estrangeira, N:N vira tabela associativa, atributo multivalorado vira tabela. É mecânico — por isso a ferramenta consegue fazê-lo.
geração de DDL- O único ponto em que o SGBD é escolhido. Até aqui, o mesmo modelo serve para PostgreSQL, MySQL ou Oracle.
engenharia reversa- Volta do banco para o modelo lógico — nunca para o conceitual. Generalizações e agregações não sobrevivem à ida, então não voltam.
O que a ferramenta pega e o que ela não pega
A validação automática cobre a sintaxe do modelo. A semântica continua sendo responsabilidade humana.
A FERRAMENTA ACUSA:
- entidade sem identificador
- relacionamento com uma ponta só
- nome de tabela ou coluna duplicado
- chave estrangeira apontando para tabela inexistente
- tipo incompatível entre FK e a PK referenciada
A FERRAMENTA NÃO ACUSA:
- cardinalidade errada (1:N onde o negócio exige N:N)
- entidade que faltou no modelo
- atributo no lugar errado (nota dentro de Aluno)
- participação mínima invertida
- o modelo inteiro descrever o negócio erradoLinha a linha
entidade sem identificador- Erro estrutural: é verificável só olhando o modelo, sem saber nada do negócio. Por isso a máquina pega.
cardinalidade errada- Um modelo com 1:N onde deveria haver N:N é perfeitamente válido do ponto de vista da notação. Só quem conhece o domínio percebe — e é por isso que a revisão com o usuário não tem substituto.
Onde isso aparece na prática
- Engenharia reversa é a primeira coisa a fazer ao herdar um sistema sem documentação — em minutos se tem o mapa que ninguém escreveu.
- Gerar o DDL a partir do modelo elimina erros de digitação em dezenas de CREATE TABLE e garante que toda chave estrangeira declarada no modelo exista no banco.
- Versionar o arquivo do modelo junto com o código é o que impede que o diagrama e o banco divirjam sem que ninguém perceba.
Curiosidades
- O brModelo foi criado como trabalho de conclusão de curso por Carlos Henrique Cândido, orientado por Ronaldo dos Santos Mello, e é usado até hoje em boa parte dos cursos de computação do Brasil.
- A maioria das ferramentas gera DDL diferente para cada SGBD a partir do mesmo modelo lógico — é a independência de tecnologia sendo exercida no último momento possível.
Exercícios
Básico
Cite três coisas que uma ferramenta de modelagem faz automaticamente e uma que ela não consegue fazer.
Dica
A que não consegue depende de conhecer o negócio.
Resolução comentada
Faz automaticamente: converter o modelo conceitual em lógico aplicando as regras de transformação; gerar o script DDL para o SGBD escolhido; validar a estrutura do modelo, acusando entidade sem identificador ou chave estrangeira órfã; e fazer engenharia reversa de um banco existente. Não consegue: dizer se o modelo representa corretamente o negócio — uma cardinalidade errada ou uma entidade esquecida produzem um modelo perfeitamente válido e completamente errado.
Resposta
Faz: conversão conceitual→lógico, geração de DDL, validação estrutural, engenharia reversa. Não faz: verificar se o modelo corresponde ao negócio.
Intermediário
Em que ponto do fluxo o modelo deixa de ser independente de SGBD? Explique a consequência prática.
Dica
Siga o diagrama até a última seta descendente.
Resolução comentada
Na geração do DDL. Até o modelo lógico, tudo é vocabulário do modelo relacional — tabelas, colunas, chaves — que vale para qualquer SGBD relacional. É na geração do script que se escolhe o produto, e aí entram tipos específicos (SERIAL do PostgreSQL contra AUTO_INCREMENT do MySQL), sintaxe de restrição e detalhes de armazenamento. A consequência prática é boa: como a dependência aparece só no último passo, migrar de SGBD não exige remodelar nada — basta gerar o DDL de novo escolhendo outro destino. É a independência de tecnologia sendo preservada até o momento em que deixar de preservá-la se torna inevitável.
Resposta
Na geração do DDL. Como é o último passo, trocar de SGBD não exige remodelar: basta regerar o script para o novo destino.
Avançado
Por que a engenharia reversa reconstrói o modelo lógico, mas não o conceitual?
Dica
O que existe no conceitual que não tem representação no banco?
Resolução comentada
Porque a conversão do conceitual para o lógico perde informação, e o que se perde não pode ser recuperado do banco. Uma generalização, por exemplo, vira tabelas ligadas por chave — mas uma tabela ligada a outra por chave compartilhada é indistinguível, no catálogo, de um relacionamento 1:1 comum; nada no banco diz "isto era uma hierarquia". Um relacionamento N:N vira tabela associativa, e no catálogo essa tabela é apenas mais uma tabela com duas chaves estrangeiras — pode ser um N:N traduzido ou uma entidade legítima do domínio. Um atributo multivalorado vira tabela, e no banco fica idêntico a uma entidade fraca. A engenharia reversa consegue reconstruir com fidelidade o que está declarado no catálogo — tabelas, colunas, tipos, chaves — porque isso é exatamente o modelo lógico. O conceitual exigiria adivinhar a intenção que produziu aquela estrutura, e várias intenções diferentes produzem a mesma estrutura. Na prática, ferramentas oferecem uma reconstrução conceitual aproximada, que serve de ponto de partida e precisa ser corrigida à mão por quem entende do domínio.
Resposta
Porque a conversão conceitual→lógico perde informação: generalização, N:N e atributo multivalorado produzem no banco estruturas indistinguíveis de outras coisas. Várias intenções geram o mesmo DDL, e o catálogo não guarda a intenção.
Desafio
Uma equipe gerou o banco a partir do modelo há um ano. Desde então, todas as alterações foram feitas direto no SGBD. Descreva os problemas e proponha um processo.
Dica
Qual dos dois artefatos é a verdade hoje?
Resolução comentada
O primeiro problema é que o modelo virou documentação falsa, que é pior do que não ter documentação: quem consulta o diagrama toma decisões com base numa estrutura que não existe mais, e não tem como saber disso. O segundo é que a decisão de projeto se perdeu — as alterações feitas direto no banco não têm registro de por que foram feitas, e um ano depois ninguém sabe se aquela coluna nova é essencial ou resquício de um experimento. O terceiro é que regerar o banco a partir do modelo tornou-se impossível sem destruir dados, o que na prática significa que ambientes novos (teste, homologação) não podem mais ser criados a partir do artefato oficial. Quanto ao processo, a primeira coisa é decidir qual artefato é a fonte da verdade, e para um banco em produção há um ano a resposta honesta é o banco. Portanto: fazer engenharia reversa para reconstruir o modelo lógico a partir do estado atual, revisá-lo à mão para reintroduzir o que a reversa não recupera, e versioná-lo junto com o código. Daí em diante, adotar migrations — arquivos de alteração versionados, aplicados em ordem, cada um com sua justificativa na mensagem de commit — de modo que a estrutura do banco passe a ter histórico e a ser reproduzível em qualquer ambiente. O modelo gráfico deixa de ser a fonte da verdade e passa a ser documentação gerada, atualizada por engenharia reversa a cada release, o que elimina a possibilidade de ele divergir de novo.
Resposta
O modelo virou documentação falsa, as decisões se perderam e não se cria mais ambiente novo a partir dele. Processo: reversa para reconstruir o modelo, adotar migrations versionadas como fonte da verdade, e regenerar o diagrama a cada release.
Resumo
Conceitos importantes
- A ferramenta converte conceitual → lógico → DDL, e o SGBD só é escolhido no último passo.
- Ela valida a estrutura do modelo, não a correspondência com o negócio.
- Engenharia reversa reconstrói o lógico, nunca o conceitual — a conversão perde informação.
- Sem sincronização, o modelo vira documentação falsa.
Checklist
- Sei descrever o fluxo do conceitual até o banco.
- Sei dizer o que a ferramenta acusa e o que não acusa.
- Sei explicar por que a reversa não recupera o conceitual.
- Sei propor um processo para modelo e banco não divergirem.
Pontos para revisão
- Por que documentação falsa é pior que documentação ausente.
- Quais construções do E-R não sobrevivem à tradução para tabelas.
- brModelo
- modelo lógico
- engenharia reversa
- geração de DDL
- sincronização
- migrations
Modelo relacional: conceitos básicos
Modelo Relacional
A relação como conjunto de tuplas, o vocabulário formal (relação, tupla, atributo, domínio, grau, cardinalidade) e o que distingue uma tabela de uma relação.
Em palavras simples
O modelo relacional guarda tudo em tabelas, e só em tabelas. Não há ponteiro de uma tabela para outra, não há lista dentro de uma célula, não há estrutura escondida. Cada linha é um fato, cada coluna é uma propriedade desse fato, e a ligação entre tabelas se faz repetindo um valor — nunca por um endereço de memória. Essa simplicidade radical é o que permitiu criar uma linguagem única para consultar qualquer banco.
Tecnicamente
Uma relação é um conjunto de tuplas definidas sobre um esquema de relação R(A1:D1, …, An:Dn), em que cada atributo Ai tem um domínio Di. O grau da relação é o número de atributos; a cardinalidade é o número de tuplas. Por ser um conjunto no sentido matemático, uma relação tem três propriedades que a distinguem de uma tabela qualquer: não há tuplas duplicadas, não há ordem entre as tuplas e não há ordem entre os atributos — a identificação é por nome, não por posição. Há ainda a primeira forma normal embutida na própria definição: todo valor de atributo é atômico, isto é, indivisível do ponto de vista do modelo; não existe atributo multivalorado nem composto. Na prática, um SGBD relaxa parte disso: uma tabela sem chave primária admite linhas duplicadas, e o resultado de um SELECT sem ORDER BY tem ordem indefinida, mas não arbitrária. Chamar tabela de relação é, portanto, uma aproximação — útil, desde que se saiba onde ela deixa de valer.
Principais conceitos
- Relação
- Conjunto de tuplas sobre um mesmo esquema. Corresponde, com ressalvas, à tabela.
- Tupla
- Uma linha: um conjunto de valores, um para cada atributo do esquema.
- Domínio
- Conjunto de valores permitidos para um atributo. É o antepassado conceitual do tipo de dado.
- Grau
- Número de atributos da relação — quantas colunas ela tem.
- Cardinalidade da relação
- Número de tuplas. Não confundir com a cardinalidade do relacionamento no E-R.
- Atomicidade do valor
- Todo valor é indivisível para o modelo. É o que proíbe atributo composto e multivalorado numa relação.
Exemplos
O vocabulário aplicado a uma tabela concreta
A mesma tabela descrita nos dois vocabulários. Vale a pena saber os dois: a prova usa um, o dia a dia usa o outro.
RELAÇÃO aluno (grau 3, cardinalidade 4)
matricula | nome | semestre <- atributos / colunas
----------+-------------+----------
2026001 | Ana Lima | 3 <- tupla / linha
2026002 | Bruno Reis | 3
2026003 | Carla Dias | 4
2026004 | Diego Alves | 3
domínio de "semestre" = inteiros de 1 a 8
FORMAL | PRÁTICO
----------------+-------------
relação | tabela
tupla | linha / registro
atributo | coluna / campo
domínio | tipo de dado
grau | número de colunas
cardinalidade | número de linhasLinha a linha
grau 3, cardinalidade 4- Grau conta colunas e quase nunca muda; cardinalidade conta linhas e muda a cada operação. É o par esquema/instância outra vez, agora com nome formal.
domínio de "semestre"- O domínio é mais estreito que o tipo: INTEGER admite -5, o domínio não. A diferença se implementa com CHECK.
O que a definição de relação proíbe
Três violações comuns. Todas produzem tabelas que o SGBD aceita e que o modelo relacional rejeita.
-- ERRADO: valor não atômico (viola a 1FN)
cliente(id, nome, telefones)
(1, 'Ana', '9999-0000; 8888-1111; 7777-2222')
-- CERTO: o multivalorado vira relação própria
cliente(id, nome)
telefone(cliente_id, numero)
-- ERRADO: confiar na ordem das tuplas
SELECT nome FROM aluno; -- ordem INDEFINIDA
-- CERTO: pedir a ordem que se quer
SELECT nome FROM aluno ORDER BY nome;Linha a linha
'9999-0000; 8888-1111'- Três valores numa célula. Consultar "quem tem o telefone 8888-1111" vira busca por trecho de texto, e nenhum índice ajuda.
telefone(cliente_id, numero)- Cada telefone vira uma tupla. Agora existe chave, existe índice possível, e acrescentar um quarto telefone não altera estrutura nenhuma.
SELECT sem ORDER BY- Costuma sair na ordem de inserção — até o dia em que um índice novo muda o plano de execução e a ordem muda junto, sem aviso.
Onde isso aparece na prática
- A ausência de ordem entre tuplas é por que SELECT sem ORDER BY pode devolver resultados em ordens diferentes a cada execução — e por que confiar nessa ordem é um bug esperando o dia em que o plano de execução mudar.
- A atomicidade dos valores é a regra que proíbe guardar "telefone1;telefone2;telefone3" numa coluna só — e é a razão de atributo multivalorado sempre virar tabela.
- A identificação por nome, e não por posição, é o que torna SELECT com colunas nomeadas resistente a alterações de esquema.
Curiosidades
- Codd publicou "A Relational Model of Data for Large Shared Data Banks" em 1970, e o artigo tem 11 páginas; a IBM levou quase uma década para lançar um produto baseado nele.
- Codd escreveu depois 12 regras (na verdade 13, numeradas de 0 a 12) para definir o que é um SGBD genuinamente relacional. Nenhum produto comercial da época as cumpria integralmente — e praticamente nenhum de hoje cumpre também.
Exercícios
Básico
Dada a relação produto(codigo, descricao, preco, categoria) com 250 linhas, informe o grau e a cardinalidade.
Dica
Um conta colunas, o outro conta linhas.
Resolução comentada
O grau é 4, porque a relação tem quatro atributos: codigo, descricao, preco e categoria. A cardinalidade é 250, porque há 250 tuplas. O grau faz parte do esquema e muda só quando a estrutura é alterada; a cardinalidade faz parte da instância e muda a cada INSERT ou DELETE.
Resposta
Grau 4, cardinalidade 250.
Intermediário
Explique por que a relação não ter ordem entre as tuplas é uma propriedade, e não uma limitação.
Dica
Quem decide a ordem, e quando?
Resolução comentada
Não impor ordem no armazenamento libera o SGBD para escolher, em cada consulta, a estratégia de acesso mais rápida — ler pela ordem física, por um índice, ou em paralelo por várias partições. Se a relação tivesse ordem intrínseca, toda leitura teria de respeitá-la e essas otimizações seriam impossíveis. A ordem passa a ser uma decisão de consulta, expressa por ORDER BY, e cada consulta pede a sua: o mesmo dado sai ordenado por nome num relatório e por data em outro, sem que nada seja reorganizado no disco. É separar o que o dado é do modo como ele é apresentado — e é limitação apenas para quem escreve consulta contando com uma ordem que nunca foi prometida.
Resposta
Porque libera o SGBD a escolher a melhor estratégia de acesso e transfere a ordenação para a consulta, onde cada uma pede a ordem que precisa.
Avançado
Uma tabela sem chave primária admite duas linhas idênticas. Isso contradiz a definição de relação? Como o SGBD lida com isso?
Dica
Relação é conjunto; tabela é o que o produto implementa.
Resolução comentada
Contradiz, sim, e é uma das aproximações em que tabela deixa de ser relação. Por ser conjunto, uma relação não admite elemento repetido — duas tuplas idênticas são a mesma tupla. A tabela SQL, porém, é formalmente um multiconjunto (bag): admite duplicatas, e o padrão SQL assumiu isso deliberadamente, porque eliminar duplicatas exige ordenar ou construir tabela de dispersão a cada operação, e cobrar esse custo de toda consulta seria inaceitável. Daí a linguagem oferecer o controle explícito: SELECT devolve duplicatas por padrão e SELECT DISTINCT as remove quando se quer o comportamento de conjunto; UNION elimina duplicatas e UNION ALL as preserva, sendo o segundo mais rápido justamente por não precisar verificar. A consequência prática é que duas linhas idênticas são indistinguíveis e, portanto, impossíveis de atualizar ou excluir separadamente — não há como escrever um WHERE que atinja uma e não a outra. É exatamente por isso que declarar chave primária não é formalidade: é o que devolve à tabela a propriedade que faz dela uma relação.
Resposta
Contradiz: a tabela SQL é multiconjunto, não conjunto, por decisão de desempenho. O efeito prático é que linhas idênticas não podem ser atualizadas nem excluídas separadamente — motivo pelo qual a chave primária é indispensável.
Desafio
SGBDs modernos oferecem colunas do tipo JSON e ARRAY, que guardam vários valores numa célula. Isso invalida o modelo relacional? Quando usar?
Dica
O que se ganha e o que se perde ao guardar estrutura dentro de uma célula?
Resolução comentada
Formalmente, uma coluna JSON ou ARRAY viola a atomicidade e, portanto, a primeira forma normal — o valor deixa de ser indivisível para o modelo. Na prática, isso não invalida o modelo relacional; mostra que os produtos foram além dele em pontos específicos, e cada um desses pontos tem um custo que é preciso conhecer. O que se perde é considerável: o SGBD não valida a estrutura interna do documento, então nada impede que uma linha guarde um campo com um nome e a linha seguinte com outro; não há chave estrangeira apontando para dentro do JSON, então a integridade referencial não alcança o conteúdo; consultar por um valor interno exige sintaxe específica do produto, o que reintroduz dependência de tecnologia; e a atualização parcial normalmente reescreve o documento inteiro. O que se ganha é a capacidade de armazenar estrutura genuinamente variável sem modelá-la. O critério de uso decorre disso. Use JSON quando a estrutura for realmente heterogênea e desconhecida em tempo de projeto — atributos que variam por fabricante num catálogo, corpo de webhook recebido de terceiros, respostas de formulário dinâmico — e quando o conteúdo for lido como um bloco, sem necessidade de consulta ou integridade sobre suas partes. Não use como atalho para não criar uma tabela: se você consulta o conteúdo, filtra por ele, ordena por ele ou precisa que ele referencie outra tabela, o dado é relacional e está no lugar errado. A regra prática mais útil é a pergunta: preciso de índice, chave estrangeira ou restrição sobre isso? Se sim, é tabela.
Resposta
Viola a 1FN, mas não invalida o modelo — é extensão com custo: sem validação de estrutura, sem integridade referencial interna e com sintaxe proprietária. Use para estrutura genuinamente variável lida em bloco; se precisa de índice, FK ou restrição sobre o conteúdo, é tabela.
Resumo
Conceitos importantes
- Relação é conjunto de tuplas: sem duplicatas, sem ordem de linhas, sem ordem de colunas.
- Grau = número de atributos; cardinalidade = número de tuplas.
- Todo valor é atômico — daí não existir atributo multivalorado nem composto.
- A tabela SQL é multiconjunto, e por isso a chave primária é o que a aproxima de uma relação.
Checklist
- Sei traduzir entre o vocabulário formal e o prático.
- Sei informar grau e cardinalidade de uma relação.
- Sei explicar por que SELECT sem ORDER BY não garante ordem.
- Sei justificar quando JSON é aceitável e quando é dado no lugar errado.
Pontos para revisão
- As três propriedades que distinguem relação de tabela.
- Por que linhas duplicadas não podem ser atualizadas separadamente.
- relação
- tupla
- domínio
- grau
- atomicidade
- multiconjunto
Regras de integridade do modelo relacional
Modelo Relacional
Chaves candidata, primária e estrangeira; integridade de entidade e referencial; e as ações de propagação ON DELETE e ON UPDATE.
Em palavras simples
Duas regras sustentam o modelo relacional. A primeira: toda linha tem de ser identificável, então a chave primária não pode ser nula nem repetida. A segunda: se uma linha aponta para outra, a apontada tem de existir — não se admite pedido de um cliente que não está cadastrado. Parecem óbvias, e é justamente por serem óbvias que costumam ser deixadas a cargo da aplicação, onde uma delas sempre acaba falhando.
Tecnicamente
Superchave é qualquer conjunto de atributos que identifique univocamente uma tupla. Chave candidata é uma superchave mínima — nenhum subconjunto próprio dela é superchave. Escolhida uma candidata como chave primária, as demais tornam-se chaves alternativas, declaráveis com UNIQUE. A integridade de entidade determina que nenhum atributo da chave primária pode ser nulo, o que decorre da própria definição: um valor nulo significa desconhecido, e não se pode identificar por algo desconhecido. A integridade referencial determina que o valor de uma chave estrangeira deve corresponder a alguma tupla existente na relação referenciada, ou ser inteiramente nulo. As ações referenciais definem o que ocorre quando a tupla referenciada é removida ou tem sua chave alterada: NO ACTION e RESTRICT impedem a operação, CASCADE a propaga, SET NULL e SET DEFAULT substituem o valor na tupla que referencia. A escolha entre elas é decisão de negócio, não técnica.
Principais conceitos
- Superchave
- Conjunto de atributos que identifica univocamente uma tupla, mesmo que contenha atributos supérfluos.
- Chave candidata
- Superchave mínima: retirar qualquer atributo dela faz perder a unicidade.
- Chave primária
- A candidata escolhida para identificar a relação. Não admite nulo nem repetição.
- Chave alternativa
- Candidata não escolhida como primária; declara-se com UNIQUE.
- Chave estrangeira
- Atributo que referencia a chave primária de outra relação (ou da própria).
- Integridade de entidade
- Nenhum atributo da chave primária pode ser nulo.
- Integridade referencial
- Toda chave estrangeira deve apontar para uma tupla existente, ou ser nula.
Exemplos
Identificando as chaves de uma relação
Encontrar as candidatas é o passo que antecede a escolha da primária — e é o que quase ninguém faz explicitamente.
RELAÇÃO: aluno(matricula, cpf, email, nome, semestre)
Superchaves (identificam, mas podem ter atributo supérfluo):
{matricula}
{matricula, nome} <- nome é supérfluo aqui
{cpf, semestre} <- semestre é supérfluo
{matricula, cpf, email, nome, semestre}
Chaves candidatas (superchaves MÍNIMAS):
{matricula}
{cpf}
{email}
Escolha:
PRIMÁRIA -> matricula (estável, curta, do domínio da escola)
ALTERNATIVAS -> cpf, email (UNIQUE)Linha a linha
{matricula, nome}- É superchave porque identifica, mas não é candidata: tirando nome, matricula ainda identifica sozinha. Falta a minimalidade.
{email} como candidata- Identifica univocamente, então é candidata. Mas e-mail muda com frequência — motivo suficiente para não ser a primária.
As ações referenciais e suas consequências
Cada linha é uma decisão de negócio. Escolher por hábito é como o histórico de vendas some ao excluir um cliente.
-- Item de pedido: não existe sem o pedido. CASCADE é correto.
CREATE TABLE item_pedido (
pedido_id INTEGER NOT NULL REFERENCES pedido(id) ON DELETE CASCADE,
produto_id INTEGER NOT NULL REFERENCES produto(id) ON DELETE RESTRICT,
quantidade INTEGER NOT NULL CHECK (quantidade > 0),
PRIMARY KEY (pedido_id, produto_id)
);
-- Pedido: o histórico NÃO pode sumir com o cliente.
CREATE TABLE pedido (
id INTEGER PRIMARY KEY,
cliente_id INTEGER NOT NULL REFERENCES cliente(id) ON DELETE RESTRICT,
data DATE NOT NULL
);
-- Departamento sem gerente é situação real: SET NULL.
CREATE TABLE departamento (
id INTEGER PRIMARY KEY,
gerente_id INTEGER REFERENCES funcionario(id) ON DELETE SET NULL
);Linha a linha
ON DELETE CASCADE em pedido_id- Apagar o pedido apaga seus itens. Correto porque item de pedido é entidade fraca: sem o pedido, não significa nada.
ON DELETE RESTRICT em produto_id- Impede excluir produto que já foi vendido. Sem isso, o CASCADE apagaria itens de pedidos antigos e o faturamento histórico mudaria sozinho.
ON DELETE RESTRICT em cliente_id- A diferença entre esta linha e um CASCADE é a diferença entre manter e perder o histórico de vendas. É decisão de negócio, não de banco.
ON DELETE SET NULL em gerente_id- O gerente sai da empresa, o departamento continua existindo sem gerente. Só funciona porque a coluna admite nulo — com NOT NULL, o SGBD recusaria a declaração.
Onde isso aparece na prática
- ON DELETE CASCADE em itens de pedido é correto — item sem pedido não existe; o mesmo CASCADE entre pedido e cliente apagaria o histórico de vendas ao remover um cadastro.
- Chave alternativa com UNIQUE é o que impede dois usuários com o mesmo e-mail, sem que o e-mail precise virar chave primária.
- Integridade declarada no banco continua valendo para o script de importação, para o acesso manual do DBA e para o sistema novo que ninguém avisou das regras.
Curiosidades
- Uma chave estrangeira composta é tudo-ou-nada quanto a nulos: pelo padrão SQL, ou todas as colunas são nulas ou todas devem casar. A regra parcial (MATCH PARTIAL) existe no padrão e quase nenhum produto implementa.
- Chave primária natural (CPF, ISBN) contra artificial (id sequencial) é uma das discussões mais antigas da área; o argumento decisivo contra a natural costuma ser que dados do mundo real mudam — inclusive CPF, por decisão judicial.
Exercícios
Básico
Em livro(isbn, titulo, autor, editora, ano), quais são as chaves candidatas? Qual seria a primária?
Dica
Qual atributo não repete nunca?
Resolução comentada
A única chave candidata é {isbn}, porque o ISBN identifica univocamente uma edição de um livro. Título pode repetir entre livros diferentes, autor publica vários livros, editora publica muitos e ano é compartilhado por milhares. Combinações como {titulo, autor, ano} podem parecer únicas, mas não há garantia — o mesmo autor pode lançar duas edições no mesmo ano. A chave primária é, portanto, isbn.
Resposta
Candidata única: {isbn}, que é também a chave primária.
Intermediário
Explique a diferença entre chave candidata e superchave, com um exemplo em que uma superchave não é candidata.
Dica
A palavra que diferencia é mínima.
Resolução comentada
Superchave é qualquer conjunto de atributos que identifique univocamente uma tupla, independentemente de conter atributos desnecessários. Chave candidata é uma superchave mínima: retirar qualquer atributo dela faz perder a unicidade. Em aluno(matricula, cpf, nome, semestre), o conjunto {matricula, nome} é superchave, porque conhecidos matrícula e nome identifica-se exatamente uma tupla. Não é candidata, porém, porque {matricula} sozinha já identifica — nome é supérfluo. Toda chave candidata é superchave; a recíproca é falsa.
Resposta
Superchave identifica; candidata identifica e é mínima. {matricula, nome} é superchave mas não candidata, pois {matricula} basta.
Avançado
Um sistema usa ON DELETE CASCADE entre cliente e pedido. Explique o problema e proponha a alternativa.
Dica
O que acontece com o faturamento de 2024 quando alguém apaga um cadastro?
Resolução comentada
O problema é a perda irreversível de dado histórico e financeiro. Excluir um cliente apaga automaticamente todos os seus pedidos, e com eles — se o cascade continuar propagando — os itens desses pedidos. O faturamento de exercícios passados muda retroativamente, relatórios já emitidos deixam de ser reproduzíveis e obrigações fiscais de guarda de documentos são violadas. Pior: a operação parece bem-sucedida, ninguém recebe erro e a perda só é notada quando alguém compara um relatório novo com um antigo. A alternativa correta tem duas partes. A primeira é trocar por ON DELETE RESTRICT, de modo que o banco recuse excluir cliente com pedidos — o erro aparece na hora, para quem tentou, e não meses depois. A segunda é reconhecer que o negócio raramente quer mesmo excluir um cliente: quer pará-lo de aparecer nas telas. Isso é exclusão lógica, uma coluna ativo ou excluido_em que a aplicação filtra, preservando o dado e todo o histórico. As duas juntas resolvem: o CASCADE some, o RESTRICT protege contra o acidente, e a exclusão lógica atende à necessidade real que motivava a exclusão física.
Resposta
CASCADE apaga o histórico de vendas junto com o cadastro, retroativamente e sem erro. Alternativa: ON DELETE RESTRICT para proteger, mais exclusão lógica (coluna ativo) para atender à necessidade real de "sumir da tela".
Desafio
Um colega afirma que validar integridade na aplicação é suficiente e que chaves estrangeiras "só deixam o banco lento". Responda.
Dica
Quem mais escreve nesse banco, além da aplicação?
Resolução comentada
O argumento falha no pressuposto de que a aplicação é o único caminho até o dado, e ela nunca é. Escrevem no banco também os scripts de importação e carga inicial, o DBA em manutenção emergencial, ferramentas de BI e ETL, jobs agendados, o sistema legado que ainda não foi desligado e a próxima aplicação que alguém escreverá sem ler o código desta. Cada um desses caminhos teria de reimplementar as mesmas validações, e basta um esquecer para o dado inconsistente entrar — e uma vez dentro, ele fica, porque nada o remove. Há também a concorrência: validar na aplicação significa consultar se o cliente existe e depois inserir o pedido, e entre as duas operações outra transação pode excluir o cliente. Só uma restrição verificada pelo SGBD dentro da transação fecha essa janela; código de aplicação, por mais correto que seja, não consegue. Quanto ao desempenho, o custo existe e é conhecido: a verificação de chave estrangeira exige uma busca na tabela referenciada, barata quando há índice na chave primária — que sempre há — e é por isso que o problema real costuma ser a falta de índice na coluna da chave estrangeira, não a restrição em si. Além disso, a comparação honesta não é entre validar no banco e não validar: é entre validar no banco e validar na aplicação, e a segunda faz a mesma consulta, só que sem a garantia transacional e com uma ida e volta de rede a mais. O ganho de tirar a restrição é, portanto, menor do que parece, e o preço é abrir mão da única garantia que vale para todos os caminhos. A conclusão prática: valide nos dois lugares — na aplicação para dar mensagem de erro decente ao usuário, no banco porque é lá que a garantia é real.
Resposta
A aplicação nunca é o único caminho até o dado (importações, DBA, ETL, jobs, sistemas futuros), e só a restrição no SGBD fecha a janela de concorrência entre verificar e inserir. O custo é uma busca por índice que já existe. Valide nos dois: na aplicação pela mensagem, no banco pela garantia.
Resumo
Conceitos importantes
- Superchave identifica; candidata é superchave mínima; primária é a candidata escolhida.
- Integridade de entidade: chave primária não admite nulo.
- Integridade referencial: chave estrangeira aponta para tupla existente, ou é nula.
- As ações referenciais (CASCADE, RESTRICT, SET NULL) são decisão de negócio.
Checklist
- Sei listar superchaves e candidatas de uma relação.
- Sei justificar a escolha da chave primária.
- Sei escolher a ação referencial adequada a cada relacionamento.
- Sei argumentar por que a integridade pertence ao banco.
Pontos para revisão
- Por que CASCADE entre cliente e pedido destrói histórico.
- A janela de concorrência que só a restrição no SGBD fecha.
- chave candidata
- chave primária
- chave estrangeira
- integridade referencial
- CASCADE
- RESTRICT
Transformação de diagramas E-R para o modelo relacional
Modelo Relacional
As regras mecânicas que convertem entidades, relacionamentos, atributos multivalorados, entidades fracas e hierarquias em tabelas.
Em palavras simples
Esta é a etapa em que o desenho vira banco. E a boa notícia é que ela é quase toda mecânica: existe uma regra para cada construção do diagrama, e seguir as regras produz o esquema relacional. As decisões difíceis já foram tomadas na modelagem conceitual — aqui só se aplica o que ficou decidido.
Tecnicamente
As regras de transformação, na ordem em que convém aplicá-las: toda entidade vira uma relação, com seu identificador como chave primária. Atributo composto é substituído por suas partes, ou mantido como um único atributo se o negócio nunca consultar as partes isoladamente. Atributo multivalorado vira uma relação própria, cuja chave é o par (chave da entidade, valor). Relacionamento 1:N gera uma chave estrangeira na relação do lado N, apontando para o lado 1; a cardinalidade mínima do lado N determina se essa coluna é NOT NULL. Relacionamento N:N gera obrigatoriamente uma relação própria, cuja chave primária é a combinação das chaves das duas entidades, e que hospeda os atributos do relacionamento. Relacionamento 1:1 admite três tratamentos: fundir as duas entidades numa relação quando a participação é total dos dois lados, ou colocar a chave estrangeira no lado de participação total — nunca no lado opcional, para não produzir coluna majoritariamente nula. Entidade fraca vira relação cuja chave primária combina a chave da proprietária com o identificador parcial. Hierarquia de generalização admite as três estratégias já vistas.
Principais conceitos
- Regra da entidade
- Toda entidade vira uma relação; seu identificador vira a chave primária.
- Regra do 1:N
- Chave estrangeira no lado N, apontando para o lado 1. Nunca o contrário.
- Regra do N:N
- Relação própria (associativa) com chave composta pelas chaves das duas entidades, hospedando os atributos do relacionamento.
- Regra do multivalorado
- Vira relação própria com a chave da entidade mais o valor. É consequência direta da atomicidade.
- Regra da entidade fraca
- Chave primária composta pela chave da proprietária mais o identificador parcial.
Exemplos
Um modelo completo, convertido regra a regra
Cinco construções diferentes num modelo só. Acompanhe qual regra produziu cada tabela.
MODELO E-R
Cliente (cpf, nome, {telefone}) <- multivalorado
Pedido (numero, data)
Produto (codigo, descricao, preco)
Cliente --(1,1)-- faz --(0,N)-- Pedido <- 1:N
Pedido --contem[quantidade]-- Produto <- N:N com atributo
ESQUEMA RELACIONAL RESULTANTE
cliente(cpf, nome)
telefone_cliente(cpf, numero) <- regra do multivalorado
PK (cpf, numero) / FK cpf -> cliente
pedido(numero, data, cpf) <- regra do 1:N
FK cpf -> cliente, NOT NULL
produto(codigo, descricao, preco)
item_pedido(numero, codigo, quantidade) <- regra do N:N
PK (numero, codigo)
FK numero -> pedido / FK codigo -> produtoLinha a linha
telefone_cliente(cpf, numero)- O multivalorado virou tabela. A chave é o par, porque o mesmo cliente não repete o mesmo número — e clientes diferentes podem ter números iguais.
pedido(..., cpf) FK NOT NULL- A chave estrangeira foi para o lado N (pedido). O NOT NULL veio da cardinalidade mínima (1,1) do lado do cliente: todo pedido tem dono.
item_pedido com quantidade- O N:N virou tabela, e o atributo do relacionamento encontrou seu lugar natural. Não havia onde colocar quantidade em pedido nem em produto.
O 1:1 e o lado certo da chave estrangeira
A mesma regra, aplicada errado e certo. A diferença aparece no número de nulos.
-- Funcionario (0,1) -- gerencia -- (1,1) Departamento
-- Todo departamento tem gerente; nem todo funcionário gerencia.
-- ERRADO: FK no lado opcional
CREATE TABLE funcionario (
id INTEGER PRIMARY KEY,
nome VARCHAR(80),
departamento_id INTEGER UNIQUE NULL -- nulo em 95% das linhas
);
-- CERTO: FK no lado de participação total
CREATE TABLE departamento (
id INTEGER PRIMARY KEY,
nome VARCHAR(60),
gerente_id INTEGER NOT NULL UNIQUE REFERENCES funcionario(id)
);Linha a linha
departamento_id ... NULL- Numa empresa com 500 funcionários e 20 departamentos, 480 linhas ficam nulas. A coluna existe para quase ninguém.
gerente_id INTEGER NOT NULL UNIQUE- 20 linhas, nenhuma nula. O NOT NULL implementa a participação total e o UNIQUE implementa a cardinalidade máxima 1 — os dois juntos é que fazem o 1:1.
Onde isso aparece na prática
- A regra do 1:N é a mais usada de todas: praticamente todo sistema é feito de tabelas ligadas por chave estrangeira no lado N.
- Reconhecer que um N:N precisa de tabela associativa é o que evita a gambiarra de colunas produto1, produto2, produto3.
- A regra do 1:1 explica por que colocar a chave estrangeira no lado errado enche a tabela de nulos.
Curiosidades
- As regras são determinísticas o bastante para as ferramentas as aplicarem sozinhas — mas a escolha entre as três opções do 1:1 e as três da hierarquia continua sendo humana, e é aí que as ferramentas pedem confirmação.
- Um relacionamento ternário sempre vira tabela própria com três chaves estrangeiras, independentemente das cardinalidades — é a razão de ternários produzirem esquemas difíceis de consultar.
Exercícios
Básico
Converta: Departamento (1,1) — possui — (0,N) Funcionario, com Departamento(codigo, nome) e Funcionario(matricula, nome).
Dica
É 1:N. De que lado vai a chave estrangeira?
Resolução comentada
É um relacionamento 1:N, com o lado N em Funcionario. Pela regra do 1:N, a chave estrangeira vai na relação do lado N. Resultado: departamento(codigo, nome) e funcionario(matricula, nome, codigo_departamento), com codigo_departamento sendo chave estrangeira para departamento(codigo). Como a cardinalidade mínima do lado do funcionário indica que todo funcionário pertence a um departamento, a coluna é NOT NULL.
Resposta
departamento(codigo, nome); funcionario(matricula, nome, codigo_departamento NOT NULL → departamento).
Intermediário
Converta um N:N entre Medico e Paciente com atributos data e diagnostico do relacionamento. Qual a chave primária da tabela gerada?
Dica
Cuidado: o par (medico, paciente) basta como chave?
Resolução comentada
O N:N gera uma relação própria: consulta(crm, cpf_paciente, data, diagnostico), com crm referenciando medico e cpf_paciente referenciando paciente. A chave primária, porém, não pode ser apenas (crm, cpf_paciente): isso impediria que o mesmo médico atendesse o mesmo paciente mais de uma vez, o que é irreal. A data precisa entrar na chave, resultando em (crm, cpf_paciente, data). Se houver mais de uma consulta no mesmo dia, nem isso basta, e o caminho é reconhecer que Consulta é uma entidade com identidade própria e dar-lhe uma chave artificial (id), mantendo as duas chaves estrangeiras como colunas comuns. Esse é o sinal, visto na aula 5, de que o relacionamento quer ser entidade.
Resposta
consulta(crm, cpf_paciente, data, diagnostico) com PK (crm, cpf_paciente, data) — e, se houver mais de uma no mesmo dia, promover Consulta a entidade com id próprio.
Avançado
Converta a entidade fraca Dependente (identificador parcial: nome) da proprietária Funcionario(matricula). Escreva o CREATE TABLE.
Dica
A chave da proprietária entra na chave da fraca.
Resolução comentada
A chave primária da entidade fraca combina a chave da proprietária com o identificador parcial: (matricula, nome). A chave estrangeira para funcionario deve ter ON DELETE CASCADE, porque a dependência é existencial — dependente sem funcionário não significa nada.
CREATE TABLE dependente (
matricula INTEGER NOT NULL,
nome VARCHAR(80) NOT NULL,
data_nascimento DATE,
parentesco VARCHAR(30),
PRIMARY KEY (matricula, nome),
FOREIGN KEY (matricula) REFERENCES funcionario(matricula)
ON DELETE CASCADE
);Repare que matricula desempenha dois papéis ao mesmo tempo: é parte da chave primária e é chave estrangeira. É exatamente isso que caracteriza a tradução de uma entidade fraca.
Resposta
PK composta (matricula, nome), com matricula sendo também FK para funcionario e ON DELETE CASCADE pela dependência existencial.
Desafio
Um relacionamento ternário liga Fornecedor, Produto e Projeto, registrando a quantidade fornecida. Converta e explique por que ele não pode ser decomposto em três binários.
Dica
Tente decompor e veja qual informação some.
Resolução comentada
A conversão é direta: um relacionamento ternário sempre vira uma relação própria com as chaves das três entidades. Resulta fornecimento(cnpj_fornecedor, codigo_produto, id_projeto, quantidade), com chave primária composta pelas três colunas e três chaves estrangeiras. Quanto à decomposição, a tentativa produziria três tabelas binárias: fornecedor_produto (quem fornece o quê), produto_projeto (o que vai para qual projeto) e fornecedor_projeto (quem atende qual projeto). O problema é que essas três tabelas juntas não conseguem reconstruir o fato original. Suponha que o fornecedor A forneça parafusos e porcas, que o projeto X use parafusos e porcas, e que A atenda X e Y. As três binárias ficam satisfeitas, mas não há como distinguir se A forneceu parafusos para X e porcas para Y, ou parafusos para Y e porcas para X, ou tudo para os dois. A informação que se perde é justamente a associação simultânea das três pontas, que é a única coisa que o ternário afirma. Há ainda um segundo problema: a quantidade é atributo da combinação dos três, e nas binárias não haveria onde colocá-la sem repeti-la ou perdê-la. A regra prática que fica: um ternário só é decomponível quando a associação das três pontas for consequência das associações duas a duas — o que é raro, e precisa ser verificado caso a caso, não presumido.
Resposta
fornecimento(cnpj_fornecedor, codigo_produto, id_projeto, quantidade) com PK tripla. Não decompõe porque as três binárias não distinguem qual produto foi de qual fornecedor para qual projeto, e não há onde alojar a quantidade.
Resumo
Conceitos importantes
- Entidade vira relação; identificador vira chave primária.
- 1:N gera chave estrangeira no lado N; N:N gera tabela associativa obrigatoriamente.
- Atributo multivalorado e entidade fraca viram relações próprias.
- No 1:1, a chave estrangeira vai no lado de participação total.
Checklist
- Sei converter um modelo E-R completo em esquema relacional.
- Sei dizer de que lado fica a chave estrangeira num 1:N e num 1:1.
- Sei montar a chave primária de uma tabela associativa e de uma entidade fraca.
- Sei justificar por que um ternário normalmente não se decompõe.
Pontos para revisão
- Por que a chave estrangeira do 1:1 no lado errado enche a tabela de nulos.
- Que informação se perde ao decompor um ternário em binários.
- regras de transformação
- tabela associativa
- chave composta
- entidade fraca
- modelo lógico
Dependências funcionais e Primeira Forma Normal
Normalização de relações até a Terceira Forma Normal
O que é dependência funcional, quais anomalias a redundância provoca e o que exige a Primeira Forma Normal.
Em palavras simples
Normalizar é separar em tabelas o que fala de coisas diferentes. Quando uma tabela mistura dados de pedido com dados de cliente, o nome do cliente se repete em cada pedido dele — e aí três problemas aparecem: alterar o nome exige alterar em muitos lugares, apagar o último pedido apaga o cliente junto, e não há onde cadastrar um cliente que ainda não comprou. As formas normais são as regras que evitam isso, aplicadas em etapas.
Tecnicamente
Uma dependência funcional X → Y existe quando, para cada valor de X, há exatamente um valor de Y. É a formalização de "X determina Y". A dependência é total quando Y depende de todo o X (relevante apenas quando X é composto) e parcial quando depende de apenas parte dele; é transitiva quando X → Y e Y → Z, com Y não sendo chave. A redundância decorrente de dependências mal alocadas produz três anomalias. A anomalia de atualização ocorre quando um mesmo fato está em várias tuplas e a alteração precisa atingir todas, sob pena de inconsistência. A de exclusão ocorre quando remover uma tupla elimina um fato não relacionado que só existia ali. A de inserção ocorre quando não se consegue registrar um fato por faltar outro que a chave exige. A Primeira Forma Normal exige que todos os valores sejam atômicos e que não haja grupos repetitivos — nem lista dentro de célula, nem colunas numeradas como telefone1, telefone2, telefone3.
Principais conceitos
- Dependência funcional
- X → Y: para cada valor de X existe exatamente um valor de Y. Lê-se "X determina Y".
- Dependência parcial
- Y depende de apenas parte de uma chave composta X. Só existe quando a chave é composta.
- Dependência transitiva
- X → Y e Y → Z, com Y não sendo chave. Z depende da chave por intermédio de Y.
- Anomalia de atualização
- Um mesmo fato repetido em várias tuplas obriga a alterar todas; esquecer uma gera inconsistência.
- Anomalia de exclusão
- Remover uma tupla apaga junto um fato não relacionado que só existia ali.
- Anomalia de inserção
- Não se consegue registrar um fato porque a chave exige outro que ainda não existe.
- Primeira Forma Normal
- Todos os valores atômicos e sem grupos repetitivos.
Exemplos
As três anomalias numa tabela só
Uma tabela que mistura pedido, cliente e produto. Cada anomalia é visível numa operação diferente.
pedido_completo(num_pedido, data, cpf, nome_cliente, cidade,
cod_prod, desc_prod, preco, qtd)
num | data | cpf | nome_cliente | cod | desc_prod | preco | qtd
----+-------+-----+--------------+-----+-----------+-------+----
101 | 03/08 | 111 | Ana Lima | P1 | Teclado | 120 | 2
101 | 03/08 | 111 | Ana Lima | P2 | Mouse | 80 | 1
102 | 04/08 | 111 | Ana Lima | P1 | Teclado | 120 | 1
ATUALIZAÇÃO: Ana muda de nome -> alterar 3 linhas. Esquecer uma
cria duas Anas com o mesmo CPF.
EXCLUSÃO: apagar o pedido 102 (única linha de Ana? não, mas se
fosse) apagaria o cadastro dela junto.
INSERÇÃO: não há como cadastrar um produto novo que ainda não
foi vendido — faltaria num_pedido, que é parte da chave.Linha a linha
'Ana Lima' repetido em 3 linhas- O nome do cliente é fato sobre o CLIENTE, não sobre o item de pedido. Está na tabela errada, e por isso se repete.
desc_prod e preco repetidos- Mesma coisa com o produto. Repare que preco aqui é ambíguo: é o preço atual do produto ou o praticado na venda? A tabela não distingue, e isso é outro defeito.
não há como cadastrar produto novo- A anomalia de inserção. A chave da tabela envolve num_pedido, então nenhum fato consegue entrar sem um pedido existir.
Violações de 1FN e suas correções
As duas formas de violar a 1FN: valor não atômico e grupo repetitivo.
-- VIOLAÇÃO 1: valor não atômico
cliente(id, nome, telefones)
(1, 'Ana', '9999-0000; 8888-1111')
-- VIOLAÇÃO 2: grupo repetitivo (colunas numeradas)
cliente(id, nome, telefone1, telefone2, telefone3)
(1, 'Ana', '9999-0000', '8888-1111', NULL)
-- CORREÇÃO (serve para as duas)
cliente(id, nome)
telefone(cliente_id, numero)
PK (cliente_id, numero)Linha a linha
telefone1, telefone2, telefone3- Parece atômico, mas é o mesmo defeito: impõe um limite artificial de três, desperdiça colunas nulas, e buscar um número exige testar as três colunas.
telefone(cliente_id, numero)- Sem limite de quantidade, sem coluna nula, indexável, e a busca vira um WHERE simples. A tabela cresce em linhas, que é como banco de dados foi feito para crescer.
Onde isso aparece na prática
- Colunas numeradas (produto1, produto2, produto3) são a violação de 1FN mais comum em planilhas migradas para banco.
- As três anomalias são o argumento a usar quando alguém propõe "deixar tudo numa tabela só para não precisar de junção".
- Identificar as dependências funcionais é o passo que torna a normalização mecânica em vez de intuitiva.
Curiosidades
- A 1FN é a única forma normal que faz parte da própria definição de relação de Codd — as demais são propriedades desejáveis de um esquema, não requisitos para ser relacional.
- Normalizar não é sempre o objetivo: bancos analíticos (data warehouses) usam esquema estrela, deliberadamente desnormalizado, porque ali o padrão de uso é ler muito e escrever pouco, e as anomalias de atualização quase não se aplicam.
Exercícios
Básico
A tabela aluno(matricula, nome, curso, nome_curso) está em 1FN? Justifique.
Dica
1FN pergunta só sobre atomicidade e grupos repetitivos.
Resolução comentada
Sim, está em 1FN: todos os valores são atômicos e não há grupos repetitivos nem colunas numeradas. A tabela tem outros problemas — nome_curso depende de curso, e não da matrícula, o que é uma dependência transitiva e viola a 3FN —, mas a 1FN pergunta apenas sobre atomicidade. É um lembrete importante: estar em 1FN não significa estar bem modelado, significa apenas ter passado pela primeira das três verificações.
Resposta
Sim, está em 1FN (valores atômicos, sem grupos repetitivos), embora viole a 3FN por dependência transitiva.
Intermediário
Em item_pedido(num_pedido, cod_produto, desc_produto, quantidade), com chave (num_pedido, cod_produto), liste as dependências funcionais.
Dica
Pergunte, para cada atributo, de que ele depende de fato.
Resolução comentada
As dependências são: (num_pedido, cod_produto) → quantidade, porque a quantidade depende da combinação — é a quantidade daquele produto naquele pedido; e cod_produto → desc_produto, porque a descrição depende apenas do produto, independentemente do pedido. A segunda é uma dependência parcial: desc_produto depende de parte da chave, não dela inteira. É exatamente essa dependência parcial que viola a 2FN e que motiva separar produto numa tabela própria.
Resposta
(num_pedido, cod_produto) → quantidade (total) e cod_produto → desc_produto (parcial, viola a 2FN).
Avançado
Explique por que telefone1, telefone2, telefone3 viola a 1FN, se cada célula contém um único valor atômico.
Dica
O que a 1FN proíbe além de valor não atômico?
Resolução comentada
A 1FN proíbe duas coisas: valores não atômicos e grupos repetitivos. As três colunas são atômicas individualmente, mas formam um grupo repetitivo — o mesmo atributo conceitual, telefone, replicado em posições numeradas. Os sintomas mostram que é o mesmo defeito de guardar tudo numa célula. Primeiro, há um limite arbitrário: o cliente com quatro telefones não cabe, e acrescentar telefone4 é alteração de esquema para um fato que deveria ser um simples INSERT. Segundo, há desperdício: a maioria das linhas deixa colunas nulas. Terceiro, e mais grave, a consulta fica antinatural — buscar quem tem determinado número exige WHERE telefone1 = ? OR telefone2 = ? OR telefone3 = ?, que precisa de três índices e ainda assim é difícil de otimizar. Quarto, a posição passa a ter significado que ninguém definiu: telefone1 é o principal? E se o cliente apagar o primeiro, o segundo sobe? A raiz de tudo é a mesma: a multiplicidade foi codificada na estrutura da tabela, em vez de virar linhas, que é o único lugar onde o modelo relacional sabe representar quantidade variável.
Resposta
Porque a 1FN proíbe também grupos repetitivos, e as três colunas são o mesmo atributo replicado. O resultado é limite artificial, colunas nulas, consulta com OR entre colunas e significado indefinido para a posição.
Desafio
Um data warehouse guarda tabelas deliberadamente desnormalizadas. Como conciliar isso com tudo o que se disse sobre anomalias?
Dica
As anomalias dependem de uma operação específica. Qual?
Resolução comentada
As três anomalias são anomalias de escrita: a de atualização acontece ao alterar, a de exclusão ao apagar, a de inserção ao inserir. Nenhuma delas se manifesta em leitura. Isso explica a aparente contradição, porque os dois ambientes têm padrões de uso opostos. Um banco transacional (OLTP) recebe escritas o tempo todo, vindas de muitas transações concorrentes, e é exatamente ali que as anomalias custam caro — por isso se normaliza. Um data warehouse (OLAP) é carregado em lote, por um processo de ETL controlado, e depois é só lido, por consultas que agregam milhões de linhas. Ali, a anomalia de atualização praticamente não existe, porque ninguém atualiza linha a linha; e o custo que domina é o das junções, que a normalização multiplica. Desnormalizar troca um problema que não se tem por um ganho que se tem. Há três condições que tornam a troca legítima, e vale enunciá-las porque é a ausência delas que transforma desnormalização em bagunça: a carga precisa ser controlada por um processo único e reproduzível, de modo que a consistência seja garantida na origem e não na tabela; o dado precisa ser histórico e imutável, para que não haja atualização a propagar; e é preciso existir a fonte normalizada da qual o warehouse é derivado, para que ele possa ser reconstruído se estiver errado. Um banco transacional desnormalizado "para evitar junção" não cumpre nenhuma das três, e por isso a comparação com o warehouse não o justifica.
Resposta
As três anomalias são de escrita, e o warehouse quase só lê: é carregado em lote por ETL e depois consultado. A troca é legítima porque há carga controlada, dado histórico imutável e uma fonte normalizada da qual ele deriva — condições que um OLTP desnormalizado não cumpre.
Resumo
Conceitos importantes
- X → Y significa que X determina Y; a dependência pode ser parcial ou transitiva.
- As três anomalias (atualização, exclusão, inserção) são todas de escrita.
- 1FN exige valores atômicos e proíbe grupos repetitivos.
- Colunas numeradas são grupo repetitivo, mesmo sendo atômicas uma a uma.
Checklist
- Sei escrever as dependências funcionais de uma tabela.
- Sei apontar cada uma das três anomalias num exemplo.
- Sei reconhecer as duas formas de violar a 1FN.
- Sei justificar quando desnormalizar é legítimo.
Pontos para revisão
- Por que as anomalias não se manifestam em leitura.
- As três condições que tornam a desnormalização legítima.
- dependência funcional
- 1FN
- grupo repetitivo
- anomalia de atualização
- desnormalização
Segunda e Terceira Formas Normais
Normalização de relações até a Terceira Forma Normal
A 2FN elimina dependências parciais da chave; a 3FN elimina dependências transitivas. O processo completo, aplicado a um caso.
Em palavras simples
Depois da 1FN vêm duas perguntas. A segunda forma normal pergunta: cada coluna depende da chave inteira, ou só de um pedaço dela? A terceira pergunta: cada coluna depende da chave, ou depende de outra coluna que não é chave? Em ambos os casos, o que estiver no lugar errado sai para uma tabela própria. Feito isso, cada tabela fala de uma coisa só.
Tecnicamente
Uma relação está na Segunda Forma Normal se está em 1FN e todo atributo não-chave depende funcionalmente da chave primária inteira, e não de parte dela. A verificação só é necessária quando a chave é composta: com chave simples, não existe "parte da chave", e uma relação em 1FN com chave simples está automaticamente em 2FN. Uma relação está na Terceira Forma Normal se está em 2FN e nenhum atributo não-chave depende de outro atributo não-chave — isto é, não há dependência transitiva. O procedimento de normalização é o mesmo nos dois casos: identifica-se a dependência indevida, extrai-se para uma nova relação o determinante junto com os atributos que ele determina, e deixa-se na relação original o determinante como chave estrangeira. A Forma Normal de Boyce-Codd (FNBC) é um reforço da 3FN que exige que todo determinante seja superchave; ela só difere da 3FN em relações com múltiplas chaves candidatas sobrepostas, e está fora do escopo do plano desta disciplina.
Principais conceitos
- Segunda Forma Normal
- Em 1FN e sem dependências parciais: todo atributo não-chave depende da chave inteira.
- Terceira Forma Normal
- Em 2FN e sem dependências transitivas: nenhum atributo não-chave depende de outro atributo não-chave.
- Determinante
- O lado esquerdo de uma dependência funcional — o atributo que determina outro.
- Procedimento de decomposição
- Extrair o determinante e o que ele determina para uma nova relação, deixando o determinante como chave estrangeira na original.
Exemplos
Normalização completa, de 1FN até 3FN
O mesmo esquema atravessando as três etapas. Acompanhe qual dependência motivou cada decomposição.
PARTIDA (em 1FN, chave composta)
item(num_pedido, cod_prod, desc_prod, cod_categ, nome_categ, qtd)
DFs:
(num_pedido, cod_prod) -> qtd total OK
cod_prod -> desc_prod, cod_categ PARCIAL viola 2FN
cod_categ -> nome_categ (dentro de produto)
APÓS 2FN (extrai o que depende só de cod_prod)
item(num_pedido, cod_prod, qtd)
produto(cod_prod, desc_prod, cod_categ, nome_categ)
DF restante:
cod_categ -> nome_categ TRANSITIVA viola 3FN
APÓS 3FN (extrai o que depende de não-chave)
item(num_pedido, cod_prod, qtd)
produto(cod_prod, desc_prod, cod_categ)
categoria(cod_categ, nome_categ)Linha a linha
cod_prod -> desc_prod (parcial)- A descrição depende só do produto, não do par. Por isso se repetia em cada item de cada pedido — a anomalia de atualização em estado puro.
cod_categ -> nome_categ (transitiva)- Já dentro de produto, o nome da categoria não depende do produto: depende da categoria. Renomear uma categoria exigiria alterar todos os produtos dela.
categoria(cod_categ, nome_categ)- Cada tabela agora fala de uma coisa só: item fala do item, produto do produto, categoria da categoria. Renomear a categoria virou uma linha alterada.
Verificando a 3FN com o caso do CEP
A dependência transitiva mais comum de todas — e a exceção que o negócio às vezes impõe.
-- VIOLA a 3FN: cep -> cidade, estado (transitiva)
CREATE TABLE cliente (
id INTEGER PRIMARY KEY,
nome VARCHAR(80),
cep CHAR(8),
cidade VARCHAR(60), -- depende do cep, não do id
estado CHAR(2) -- idem
);
-- EM 3FN
CREATE TABLE endereco_cep (
cep CHAR(8) PRIMARY KEY,
cidade VARCHAR(60) NOT NULL,
estado CHAR(2) NOT NULL
);
CREATE TABLE cliente (
id INTEGER PRIMARY KEY,
nome VARCHAR(80),
cep CHAR(8) REFERENCES endereco_cep(cep)
);Linha a linha
cidade e estado dentro de cliente- Dependem do CEP, que não é chave. Consequência: dois clientes com o mesmo CEP podem ter cidades diferentes cadastradas, e nada acusa.
endereco_cep como tabela própria- O CEP passa a determinar cidade e estado num lugar só. Corrigir um CEP errado corrige para todos os clientes de uma vez.
Onde isso aparece na prática
- A dependência parcial aparece quase sempre em tabelas associativas que absorveram atributos de uma das entidades.
- A dependência transitiva é o caso do CEP determinando cidade e estado dentro da tabela de clientes.
- Normalizar até a 3FN é o padrão da indústria para bancos transacionais; ir além raramente compensa.
Curiosidades
- A frase mnemônica clássica é que todo atributo deve depender "da chave, da chave inteira e de nada além da chave" — atribuída a Bill Kent, resume 1FN, 2FN e 3FN nessa ordem.
- Codd definiu a 3FN em 1971 e a reforçou com Boyce em 1974 justamente porque encontrou relações em 3FN que ainda apresentavam anomalias.
Exercícios
Básico
Uma relação em 1FN com chave primária simples pode violar a 2FN? Justifique.
Dica
O que é preciso existir para haver dependência parcial?
Resolução comentada
Não pode. A 2FN proíbe dependência parcial, que é a dependência de parte da chave. Com chave simples, não existe "parte da chave" — ou o atributo depende da chave inteira, ou não depende dela. Portanto toda relação em 1FN com chave primária simples está automaticamente em 2FN. Isso torna a verificação da 2FN necessária apenas em relações com chave composta, o que é uma economia útil ao normalizar um esquema grande.
Resposta
Não. Sem chave composta não há "parte da chave", logo não há dependência parcial possível.
Intermediário
Normalize até a 3FN: funcionario(matricula, nome, cod_depto, nome_depto, cod_cargo, nome_cargo, salario_base), sabendo que salario_base depende do cargo.
Dica
Procure o que depende de coluna que não é chave.
Resolução comentada
As dependências são: matricula → nome, cod_depto, cod_cargo; cod_depto → nome_depto; cod_cargo → nome_cargo, salario_base. A chave é simples (matricula), então a 2FN já está satisfeita. As duas últimas dependências são transitivas e violam a 3FN. Decompondo:
funcionario(matricula, nome, cod_depto, cod_cargo)
departamento(cod_depto, nome_depto)
cargo(cod_cargo, nome_cargo, salario_base)O ganho é imediato: reajustar o salário-base de um cargo passa a ser uma linha alterada em cargo, e não uma alteração em todos os funcionários daquele cargo — com o risco de deixar um para trás.
Resposta
funcionario(matricula, nome, cod_depto, cod_cargo); departamento(cod_depto, nome_depto); cargo(cod_cargo, nome_cargo, salario_base).
Avançado
Um sistema de vendas guarda preco_unitario na tabela item_pedido, embora o preço dependa do produto. Isso viola a 2FN? Justifique.
Dica
O preço do item é o mesmo que o preço do produto?
Resolução comentada
Não viola, e a razão é que são dois atributos diferentes com nomes parecidos. O preço na tabela de produto é o preço atual de venda; o preço no item de pedido é o preço praticado naquela venda, naquele momento. Este último depende genuinamente do par (num_pedido, cod_produto): o mesmo produto vendido em dois pedidos diferentes pode ter preços diferentes, se houve reajuste ou desconto entre eles. Portanto a dependência é total, não parcial, e a 2FN está satisfeita. Este caso é importante porque mostra o limite da verificação puramente mecânica: olhando só os nomes das colunas, preco_unitario parece depender de cod_produto, e um normalizador desatento o extrairia — destruindo o histórico de preços e fazendo notas fiscais antigas mudarem de valor quando a tabela de produtos fosse atualizada. A dependência funcional é uma afirmação sobre o significado dos dados no domínio, não sobre os nomes das colunas, e só quem conhece o domínio consegue determiná-la. O sinal de que a modelagem está correta aqui é justamente o oposto do que a intuição sugere: a repetição do preço entre pedidos não é redundância, é registro histórico.
Resposta
Não viola: o preço praticado na venda depende do par (pedido, produto), não só do produto. É dependência total, e a repetição entre pedidos é registro histórico, não redundância.
Desafio
Aplicar cegamente a 3FN ao CEP separaria cidade e estado numa tabela própria. Em que situação manter os dados na tabela de cliente é a decisão certa?
Dica
Quem garante que a tabela de CEP está completa e correta?
Resolução comentada
A situação em que manter é correto tem a ver com a origem e a completude do dado. A dependência cep → cidade, estado só vale se o sistema tiver uma base de CEPs completa e mantida atualizada. Se ela não existe — e manter uma base de CEPs nacional exige atualização periódica junto aos Correios —, então extrair a tabela cria uma chave estrangeira que não consegue ser satisfeita: o cliente informa um CEP novo, que não está na base, e ou o cadastro é bloqueado por um dado que não é responsabilidade dele, ou a chave estrangeira precisa aceitar nulo, e aí a normalização não entregou a integridade que a justificava. Há um segundo argumento, de natureza histórica: o endereço registrado num cadastro pode precisar preservar o que foi informado na época, e faixas de CEP mudam. Nesse caso cidade e estado no cadastro não são derivados do CEP atual, e sim registro do que se declarou — o mesmo raciocínio do preço praticado na venda. Um terceiro argumento é operacional: se o sistema é local e atende uma cidade só, a tabela de CEP resolve um problema que não existe. A decisão madura costuma ser híbrida e vale a pena enunciá-la: manter cidade e estado na tabela de cliente como o que foi declarado, e usar a base de CEPs, quando houver, como serviço de preenchimento e validação na entrada, e não como chave estrangeira obrigatória. Assim se ganha a conveniência sem transformar a completude de uma base externa em pré-requisito para cadastrar cliente.
Resposta
Quando não há base de CEPs completa e mantida (a FK ficaria insatisfazível), quando o endereço precisa registrar o que foi declarado na época, ou quando o alcance é local. O caminho usual é manter os campos e usar a base de CEP como validação na entrada, não como FK.
Resumo
Conceitos importantes
- 2FN: sem dependência parcial — só verificável em chave composta.
- 3FN: sem dependência transitiva entre atributos não-chave.
- Decompor é extrair o determinante com o que ele determina, deixando-o como chave estrangeira.
- "Da chave, da chave inteira e de nada além da chave."
Checklist
- Sei verificar 2FN e 3FN a partir das dependências funcionais.
- Sei decompor uma relação preservando as ligações.
- Sei distinguir redundância de registro histórico.
- Sei argumentar quando não normalizar.
Pontos para revisão
- Por que chave simples dispensa a verificação da 2FN.
- Por que o preço no item de pedido não viola a 2FN.
- 2FN
- 3FN
- dependência parcial
- dependência transitiva
- decomposição
SQL — DDL: CREATE TABLE, ALTER TABLE e DROP TABLE
Linguagem de Consulta Estruturada — SQL
A linguagem de definição de dados: criar tabelas com tipos e restrições, alterar estrutura existente e remover objetos.
Em palavras simples
Até aqui, o esquema existia no papel. A DDL é a parte do SQL que o transforma em banco de verdade: CREATE TABLE cria, ALTER TABLE altera e DROP TABLE apaga. É onde tudo o que foi decidido na modelagem — chaves, obrigatoriedade, tipos, relacionamentos — vira regra que o SGBD passa a fazer cumprir sozinho.
Tecnicamente
CREATE TABLE declara nome da tabela, colunas com seus tipos e as restrições. As restrições de coluna incluem NOT NULL, UNIQUE, DEFAULT, CHECK, PRIMARY KEY e REFERENCES; as de tabela, declaradas separadamente, permitem chaves compostas e restrições que envolvem várias colunas. Nomear as restrições com CONSTRAINT nome é boa prática porque a mensagem de erro do SGBD passa a citar esse nome, e porque removê-las depois exige conhecê-lo. Os tipos mais frequentes são INTEGER, NUMERIC(p,s) para valores exatos, VARCHAR(n) e CHAR(n), DATE, TIMESTAMP e BOOLEAN; para dinheiro usa-se NUMERIC, nunca FLOAT, porque o ponto flutuante binário não representa exatamente frações decimais. ALTER TABLE permite acrescentar, alterar e remover colunas e restrições, e é o comando que atravessa a vida do sistema — acrescentar coluna NOT NULL a uma tabela com dados exige DEFAULT ou uma sequência de três passos. DROP TABLE remove a tabela e seus dados; com tabelas referenciadas, o SGBD recusa a operação por integridade referencial, a menos que se use CASCADE, que remove também as restrições dependentes.
Principais conceitos
- DDL
- Data Definition Language: a parte do SQL que define estruturas — CREATE, ALTER, DROP.
- Restrição de coluna
- Declarada junto à coluna e válida sobre ela: NOT NULL, UNIQUE, DEFAULT, CHECK, PRIMARY KEY, REFERENCES.
- Restrição de tabela
- Declarada separadamente; necessária para chaves compostas e para regras que envolvem várias colunas.
- CHECK
- Restrição que impõe uma condição booleana aos valores. É onde a regra de negócio entra no esquema.
- DROP ... CASCADE
- Remove o objeto e tudo que depende dele. Poderoso e perigoso: apaga mais do que se digitou.
Exemplos
Um CREATE TABLE com todas as restrições em uso
Cada restrição aqui é uma regra de negócio que a aplicação não precisa lembrar de validar.
CREATE TABLE pedido (
id SERIAL PRIMARY KEY,
cliente_id INTEGER NOT NULL,
data DATE NOT NULL DEFAULT CURRENT_DATE,
situacao VARCHAR(10) NOT NULL DEFAULT 'aberto',
desconto NUMERIC(5,2) NOT NULL DEFAULT 0,
CONSTRAINT fk_pedido_cliente
FOREIGN KEY (cliente_id) REFERENCES cliente (id)
ON DELETE RESTRICT,
CONSTRAINT ck_pedido_situacao
CHECK (situacao IN ('aberto', 'pago', 'enviado', 'cancelado')),
CONSTRAINT ck_pedido_desconto
CHECK (desconto >= 0 AND desconto <= 100)
);Linha a linha
SERIAL PRIMARY KEY- Chave artificial autoincrementada. Escolha usual quando não há identificador natural estável no domínio.
DEFAULT CURRENT_DATE- O banco preenche a data quando o INSERT não a informa. Menos um campo para a aplicação errar, e vale para qualquer origem de escrita.
CONSTRAINT fk_pedido_cliente- Restrição nomeada. Quando alguém tentar excluir um cliente com pedidos, o erro cita fk_pedido_cliente — e quem lê o log sabe imediatamente o que aconteceu.
CHECK (situacao IN (...))- Impede grafias inventadas como 'ABERTO' ou 'pendente'. Sem isso, a coluna acumula variações e todo relatório precisa tratá-las.
NUMERIC(5,2)- Cinco dígitos no total, dois decimais. Exato, ao contrário de FLOAT — obrigatório em qualquer coisa que vire dinheiro.
ALTER TABLE: acrescentar coluna obrigatória a uma tabela com dados
A operação que mais dá errado em produção, feita nos três passos que a tornam segura.
-- ERRADO em tabela que já tem linhas: as existentes ficariam nulas
ALTER TABLE cliente ADD COLUMN email VARCHAR(120) NOT NULL;
-- ERRO: column "email" contains null values
-- CERTO, em três passos
-- 1. acrescenta permitindo nulo
ALTER TABLE cliente ADD COLUMN email VARCHAR(120);
-- 2. preenche as linhas existentes
UPDATE cliente SET email = 'sem-email@exemplo.local' WHERE email IS NULL;
-- 3. agora sim, impõe a obrigatoriedade
ALTER TABLE cliente ALTER COLUMN email SET NOT NULL;
-- Outras alterações comuns
ALTER TABLE cliente ADD CONSTRAINT uq_cliente_email UNIQUE (email);
ALTER TABLE cliente DROP CONSTRAINT uq_cliente_email;
ALTER TABLE cliente RENAME COLUMN email TO email_principal;Linha a linha
ADD COLUMN ... NOT NULL (sem DEFAULT)- Falha porque as linhas existentes não têm valor para a coluna nova, e NOT NULL proíbe nulo. O SGBD recusa a operação inteira — corretamente.
UPDATE ... WHERE email IS NULL- O passo que decide qual valor as linhas antigas recebem. É decisão de negócio, e por isso não pode ser automatizada por um DEFAULT escolhido às pressas.
ALTER COLUMN ... SET NOT NULL- Só agora a restrição entra, com a certeza de que nenhuma linha a viola. O SGBD varre a tabela para conferir antes de aceitar.
Onde isso aparece na prática
- Declarar CHECK (quantidade > 0) no banco garante a regra mesmo para importações e acessos que não passam pela aplicação.
- NUMERIC em vez de FLOAT para dinheiro evita o clássico total de 0,30 virando 0,29999999999999993.
- ALTER TABLE em tabela grande pode bloquear a tabela durante a operação — motivo pelo qual migrations em produção se planejam, não se improvisam.
Curiosidades
- DROP TABLE é DDL e, em vários SGBDs, provoca commit implícito — o que significa que um ROLLBACK depois dele não desfaz nada. PostgreSQL é exceção: lá a DDL é transacional.
- VARCHAR(n) e TEXT têm desempenho praticamente idêntico no PostgreSQL; o limite em VARCHAR vale como restrição de domínio, não como otimização de armazenamento.
Exercícios
Básico
Escreva o CREATE TABLE de categoria(id, nome), com id como chave primária e nome obrigatório e único.
Dica
Três restrições ao todo.
Resolução comentada
CREATE TABLE categoria (
id SERIAL PRIMARY KEY,
nome VARCHAR(60) NOT NULL UNIQUE
);PRIMARY KEY já implica NOT NULL e UNIQUE em id, então não é preciso declará-los. Em nome, NOT NULL e UNIQUE são independentes e ambos necessários: sem NOT NULL, seria possível gravar categoria sem nome; sem UNIQUE, duas categorias com o mesmo nome.
Resposta
CREATE TABLE categoria (id SERIAL PRIMARY KEY, nome VARCHAR(60) NOT NULL UNIQUE);
Intermediário
Por que usar NUMERIC(10,2) e não FLOAT para valores monetários?
Dica
Some 0,10 + 0,20 em ponto flutuante.
Resolução comentada
Porque FLOAT é ponto flutuante binário e não representa exatamente frações decimais. O valor 0,1 não tem representação finita em base 2, do mesmo modo que 1/3 não tem em base 10; o que se armazena é uma aproximação. Somando 0,10 + 0,20 obtém-se 0,30000000000000004, e o erro se acumula a cada operação. Em dinheiro isso é inaceitável: totais não fecham, comparações de igualdade falham e o balanço fica com centavos de diferença que ninguém consegue explicar. NUMERIC (ou DECIMAL) é aritmética decimal exata: armazena os dígitos e a posição da vírgula, e 0,10 + 0,20 dá exatamente 0,30. A regra é usar NUMERIC para qualquer valor que precise ser exato — dinheiro, quantidades contábeis — e reservar FLOAT para grandezas científicas, em que a aproximação já faz parte da medida.
Resposta
FLOAT é binário e aproxima frações decimais, acumulando erro (0,10+0,20 = 0,30000000000000004). NUMERIC é decimal exato — obrigatório para dinheiro.
Avançado
Escreva a sequência de comandos para acrescentar a coluna cpf, obrigatória e única, à tabela cliente que já tem 5.000 linhas.
Dica
Obrigatória e única exigem cuidados diferentes.
Resolução comentada
Não é possível acrescentar de uma vez, e o UNIQUE traz uma dificuldade que o NOT NULL não tem: não existe valor de preenchimento genérico, porque preencher todas as linhas com o mesmo texto violaria a unicidade. A sequência é:
-- 1. acrescenta permitindo nulo (UNIQUE aceita vários nulos)
ALTER TABLE cliente ADD COLUMN cpf CHAR(11);
-- 2. já declara a unicidade: nulos não conflitam entre si
ALTER TABLE cliente ADD CONSTRAINT uq_cliente_cpf UNIQUE (cpf);
-- 3. preenche as 5.000 linhas com os CPFs reais
-- (carga a partir de outra fonte; não há valor genérico possível)
-- 4. só depois de tudo preenchido
ALTER TABLE cliente ALTER COLUMN cpf SET NOT NULL;O ponto que costuma ser esquecido é o passo 3: ele não é um comando, é um projeto. Enquanto ele não termina, o passo 4 falha, e o esquema fica num estado intermediário em que a aplicação precisa tolerar cpf nulo. Por isso essa alteração se planeja com a área de negócio antes de tocar no banco.
Resposta
ADD COLUMN cpf CHAR(11) nulo; ADD CONSTRAINT UNIQUE (nulos não conflitam); carregar os CPFs reais de uma fonte externa; e só então SET NOT NULL.
Desafio
Discuta: até que ponto regras de negócio devem ser declaradas como CHECK no banco, em vez de ficarem na aplicação?
Dica
Compare uma regra estável com uma que muda a cada trimestre.
Resolução comentada
O critério decisivo é a estabilidade da regra, e não sua natureza. Regras que decorrem do significado do dado — quantidade positiva, percentual entre 0 e 100, data de fim posterior à de início, situação dentro de um conjunto fechado — praticamente não mudam ao longo da vida do sistema, e declará-las como CHECK dá três ganhos: valem para todos os caminhos de escrita, inclusive importações e correções manuais; são verificadas dentro da transação, sem janela de concorrência; e documentam o domínio no próprio esquema, onde quem chega depois vai olhar. Regras voláteis são o caso oposto. Um CHECK que fixa o desconto máximo em 30% precisa de ALTER TABLE toda vez que a diretoria mudar a política, e ALTER TABLE em produção é evento planejado, não configuração — a regra estaria codificada no lugar mais caro de alterar. Pior: regras assim costumam ter exceções (desconto maior mediante aprovação), e um CHECK não sabe de aprovações. Essas pertencem à aplicação, ou a uma tabela de parâmetros. Há ainda um terceiro grupo que não cabe em CHECK por limitação técnica: regras que dependem de outras tabelas ou do estado anterior — "o total do pedido não pode exceder o limite de crédito do cliente" envolve consulta a outra tabela, algo que CHECK não faz de forma confiável em nenhum SGBD. Essas exigem gatilho ou lógica de aplicação dentro de transação. A síntese prática: declare no banco o que é invariante do dado, deixe na aplicação o que é política de negócio, e reconheça que regras entre tabelas são um terceiro caso, que nenhum dos dois resolve sozinho.
Resposta
Declare como CHECK o que é invariante do dado (quantidade positiva, percentual 0–100, domínio fechado): vale para todo caminho de escrita e documenta o esquema. Deixe na aplicação as políticas voláteis e as que admitem exceção. Regras que dependem de outras tabelas não cabem em CHECK e exigem gatilho ou transação.
Resumo
Conceitos importantes
- CREATE TABLE declara colunas, tipos e restrições; ALTER TABLE altera; DROP TABLE remove.
- Nomear restrições com CONSTRAINT faz a mensagem de erro do SGBD ser legível.
- NUMERIC para dinheiro; FLOAT nunca.
- Acrescentar coluna NOT NULL a tabela com dados exige três passos.
Checklist
- Sei escrever um CREATE TABLE com PK, FK, CHECK, DEFAULT e UNIQUE.
- Sei acrescentar coluna obrigatória a uma tabela populada.
- Sei justificar NUMERIC em vez de FLOAT.
- Sei decidir qual regra vai para CHECK e qual fica na aplicação.
Pontos para revisão
- Por que ADD COLUMN NOT NULL falha em tabela com linhas.
- O critério de estabilidade para decidir onde a regra de negócio mora.
- DDL
- CREATE TABLE
- ALTER TABLE
- CHECK
- NUMERIC
- CONSTRAINT
SQL — DML: INSERT, UPDATE e DELETE
Linguagem de Consulta Estruturada — SQL
Os três comandos que alteram dados, o papel do WHERE em cada um e a transação como unidade de trabalho.
Em palavras simples
Três comandos alteram o conteúdo do banco: INSERT acrescenta linhas, UPDATE altera linhas existentes e DELETE remove linhas. Os dois últimos têm uma característica que assusta com razão: sem WHERE, eles agem sobre a tabela inteira. Um UPDATE esquecido de cláusula altera todos os registros — e o banco obedece, porque o comando é sintaticamente perfeito.
Tecnicamente
INSERT insere tuplas, com a forma INSERT INTO tabela (colunas) VALUES (valores). Nomear as colunas é obrigatório na prática: sem a lista, a ordem posicional passa a importar e acrescentar uma coluna à tabela quebra todos os INSERT existentes. UPDATE altera atributos das tuplas que satisfazem o WHERE, e DELETE remove as tuplas que o satisfazem; em ambos, a ausência de WHERE faz o comando valer para toda a relação. As alterações ocorrem dentro de uma transação, que é a unidade atômica de trabalho: ou todos os comandos dela se efetivam, com COMMIT, ou nenhum, com ROLLBACK. As propriedades ACID descrevem as garantias — Atomicidade (tudo ou nada), Consistência (o banco sai de um estado válido para outro), Isolamento (transações concorrentes não enxergam estados intermediários umas das outras) e Durabilidade (o que foi confirmado sobrevive a falha). Vale distinguir DELETE de TRUNCATE: o primeiro é DML, aceita WHERE, dispara gatilhos e é transacional; o segundo é DDL, esvazia a tabela inteira e costuma ser irreversível.
Principais conceitos
- DML
- Data Manipulation Language: a parte do SQL que manipula dados — INSERT, UPDATE, DELETE e SELECT.
- Transação
- Sequência de operações tratada como unidade indivisível: confirma-se inteira com COMMIT ou desfaz-se inteira com ROLLBACK.
- ACID
- Atomicidade, Consistência, Isolamento e Durabilidade — as quatro garantias que o SGBD dá às transações.
- Autocommit
- Modo em que cada comando é confirmado automaticamente, sem transação explícita. Cômodo e perigoso.
- TRUNCATE
- Comando DDL que esvazia a tabela inteira. Mais rápido que DELETE e normalmente não desfazível.
Exemplos
Os três comandos, com e sem os cuidados
A diferença entre a versão correta e a perigosa costuma ser uma linha — ou a falta dela.
-- INSERT: sempre com a lista de colunas
INSERT INTO produto (codigo, descricao, preco, categoria_id)
VALUES ('P100', 'Teclado mecânico', 289.90, 3);
-- Várias linhas de uma vez
INSERT INTO produto (codigo, descricao, preco, categoria_id) VALUES
('P101', 'Mouse óptico', 79.90, 3),
('P102', 'Monitor 24"', 899.00, 4);
-- UPDATE: o WHERE não é opcional na prática
UPDATE produto
SET preco = preco * 1.10
WHERE categoria_id = 3; -- reajuste só da categoria 3
-- SEM o WHERE, reajusta o catálogo inteiro:
-- UPDATE produto SET preco = preco * 1.10; <- não faça isso
-- DELETE: confira antes com SELECT
SELECT * FROM produto WHERE categoria_id = 9; -- 1. veja o que vai sumir
DELETE FROM produto WHERE categoria_id = 9; -- 2. só então apagueLinha a linha
INSERT INTO produto (codigo, descricao, ...)- A lista de colunas desacopla o comando da ordem física da tabela. Sem ela, acrescentar uma coluna quebra este INSERT em silêncio ou com erro de tipo.
SET preco = preco * 1.10- O valor novo pode ser calculado a partir do antigo. O SGBD lê o valor corrente da linha e grava o resultado — não é preciso consultar antes.
WHERE categoria_id = 3- Delimita as linhas atingidas. É a única coisa entre o reajuste pretendido e o reajuste de tudo.
SELECT antes do DELETE- Mesmo WHERE, comando inofensivo. Se o SELECT trouxe 4.000 linhas quando você esperava 3, o DELETE não chega a ser digitado.
Transação: a transferência que não pode ficar pela metade
O exemplo canônico de atomicidade. Sem transação, uma falha entre os dois comandos faz dinheiro desaparecer.
BEGIN;
UPDATE conta SET saldo = saldo - 500 WHERE id = 1;
UPDATE conta SET saldo = saldo + 500 WHERE id = 2;
-- Se algo falhar aqui no meio, ROLLBACK desfaz os dois.
-- Nenhum outro usuário chega a ver o estado intermediário.
COMMIT;
-- Desfazendo explicitamente:
BEGIN;
DELETE FROM pedido WHERE data < '2020-01-01';
-- olha o resultado, percebe que apagou demais
ROLLBACK; -- nada foi perdidoLinha a linha
BEGIN- Abre a transação. Daqui até o COMMIT, as alterações existem só para esta sessão.
os dois UPDATE- Atomicidade: ou os dois valem, ou nenhum. Uma queda de energia entre eles não deixa os 500 reais em lugar nenhum.
ROLLBACK- Desfaz tudo desde o BEGIN. É a rede que transforma um DELETE errado em susto em vez de incidente — desde que a transação esteja aberta.
Onde isso aparece na prática
- INSERT com lista de colunas explícita é o que faz uma carga continuar funcionando depois de a tabela ganhar uma coluna nova.
- Rodar o SELECT com o mesmo WHERE antes do DELETE é a prática que evita o acidente mais comum da profissão.
- Transação é o que garante que debitar de uma conta e creditar em outra aconteçam juntos, ou não aconteçam.
Curiosidades
- A ordem das cláusulas no UPDATE — SET antes de WHERE — engana quem lê da esquerda para a direita: o WHERE é avaliado primeiro, e o SET aplica-se apenas às linhas que ele selecionou.
- Vários clientes SQL abrem transação automaticamente e exigem COMMIT explícito; outros trabalham em autocommit, em que cada comando se confirma sozinho. Não saber em qual modo se está é a origem de "apaguei e não consigo desfazer".
Exercícios
Básico
Escreva o INSERT para cadastrar a categoria de id 7 e nome 'Periféricos'.
Dica
Nomeie as colunas.
Resolução comentada
INSERT INTO categoria (id, nome) VALUES (7, 'Periféricos');O texto vai entre aspas simples, que é o delimitador de literal do SQL padrão; aspas duplas identificam objetos, não valores. Se a coluna id fosse SERIAL, o correto seria omiti-la e deixar o banco gerar: INSERT INTO categoria (nome) VALUES ('Periféricos').
Resposta
INSERT INTO categoria (id, nome) VALUES (7, 'Periféricos');
Intermediário
Escreva o UPDATE que dá 5% de desconto em todos os produtos da categoria 2 com preço acima de 500.
Dica
São duas condições no WHERE.
Resolução comentada
UPDATE produto
SET preco = preco * 0.95
WHERE categoria_id = 2
AND preco > 500;O cálculo usa o valor corrente da própria coluna, então não é preciso consultar antes. As duas condições ligadas por AND restringem a linhas que satisfaçam ambas. Convém rodar antes o SELECT com o mesmo WHERE para conferir quantas linhas serão afetadas.
Resposta
UPDATE produto SET preco = preco * 0.95 WHERE categoria_id = 2 AND preco > 500;
Avançado
Explique a diferença entre DELETE FROM pedido; e TRUNCATE TABLE pedido; quanto a efeito, desempenho e reversibilidade.
Dica
Um é DML, o outro é DDL.
Resolução comentada
Quanto ao efeito imediato, ambos deixam a tabela vazia, mas por caminhos diferentes. DELETE é DML: percorre as linhas, registra cada remoção no log de transações, dispara gatilhos de exclusão e respeita chaves estrangeiras, recusando a operação se houver linhas dependentes. TRUNCATE é DDL: descarta as páginas de dados de uma vez, sem percorrer linha a linha, sem disparar gatilhos e, em vários SGBDs, sem verificar dependências a menos que se peça CASCADE. Quanto ao desempenho, a diferença é grande em tabelas volumosas — DELETE de dez milhões de linhas gera dez milhões de entradas de log e pode levar minutos, enquanto TRUNCATE é praticamente instantâneo porque não registra linha nenhuma. Quanto à reversibilidade, DELETE é transacional em qualquer SGBD e um ROLLBACK o desfaz; TRUNCATE, por ser DDL, provoca commit implícito na maioria dos produtos e é irreversível — PostgreSQL é a exceção notável, onde TRUNCATE é transacional. Há ainda um detalhe frequentemente esquecido: TRUNCATE costuma reiniciar sequências de autoincremento, e DELETE não. Na prática, use DELETE quando houver WHERE, quando gatilhos precisarem disparar ou quando a operação precisar ser desfeita; use TRUNCATE para esvaziar tabelas de carga e de teste, onde o volume importa e a reversibilidade não.
Resposta
DELETE é DML: linha a linha, com log, gatilhos, respeito a FK e ROLLBACK possível. TRUNCATE é DDL: descarta tudo de uma vez, sem gatilhos, muito mais rápido, normalmente irreversível e reinicia sequências.
Desafio
Um sistema debita estoque com um SELECT para conferir a quantidade e depois um UPDATE para subtrair. Sob acesso simultâneo, o estoque fica negativo. Explique e corrija.
Dica
O que acontece entre o SELECT e o UPDATE?
Resolução comentada
O problema é uma condição de corrida clássica. Com uma unidade em estoque e duas transações simultâneas, ambas executam o SELECT e leem 1; ambas concluem que há estoque suficiente; ambas executam o UPDATE subtraindo 1; o estoque termina em -1. Nenhuma das duas fez nada errado isoladamente — o defeito está na janela entre a leitura e a escrita, durante a qual a informação lida deixou de ser verdadeira sem que a transação soubesse. Envolver os dois comandos numa transação não resolve sozinho: em nível de isolamento READ COMMITTED, que é o padrão da maioria dos SGBDs, a transação B ainda enxerga o valor confirmado antes de A escrever. Há três correções válidas. A primeira, e a mais robusta, é declarar a regra no esquema: CHECK (quantidade >= 0). Assim o segundo UPDATE falha com erro de restrição, e o estoque negativo torna-se impossível por construção, para qualquer caminho de escrita. A segunda é eliminar a janela fazendo a verificação dentro do próprio UPDATE: UPDATE estoque SET quantidade = quantidade - 1 WHERE produto_id = 10 AND quantidade >= 1 — a condição é avaliada no momento da escrita, com a linha bloqueada, e o comando afeta zero linhas quando não há estoque, o que a aplicação detecta pela contagem de linhas afetadas. A terceira é bloquear a linha na leitura com SELECT ... FOR UPDATE, forçando a segunda transação a esperar a primeira terminar; funciona, mas serializa o acesso e custa concorrência. A recomendação prática é combinar a primeira com a segunda: o CHECK como garantia final e o UPDATE condicional como caminho normal, verificando sempre quantas linhas foram afetadas antes de confirmar a venda.
Resposta
Condição de corrida: as duas transações leem 1 antes de qualquer escrita e ambas subtraem. Corrija com CHECK (quantidade >= 0) no esquema e UPDATE ... WHERE quantidade >= 1, conferindo as linhas afetadas — ou SELECT ... FOR UPDATE, ao custo de serializar.
Resumo
Conceitos importantes
- INSERT, UPDATE e DELETE alteram dados; sem WHERE, os dois últimos atingem a tabela inteira.
- Sempre nomeie as colunas no INSERT.
- Transação é a unidade atômica: COMMIT confirma tudo, ROLLBACK desfaz tudo.
- ACID: Atomicidade, Consistência, Isolamento, Durabilidade.
Checklist
- Sei escrever os três comandos com WHERE adequado.
- Sei usar BEGIN, COMMIT e ROLLBACK.
- Sei diferenciar DELETE de TRUNCATE.
- Sei reconhecer e corrigir uma condição de corrida entre leitura e escrita.
Pontos para revisão
- Por que o SELECT com o mesmo WHERE antes do DELETE é hábito de sobrevivência.
- Por que envolver em transação não elimina sozinho a condição de corrida.
- DML
- INSERT
- UPDATE
- DELETE
- transação
- ACID
- ROLLBACK
SQL — SELECT: projeção, seleção e ordenação
Linguagem de Consulta Estruturada — SQL
A forma básica do SELECT, os operadores do WHERE, o tratamento de NULL e a ordenação dos resultados.
Em palavras simples
SELECT é o comando que responde perguntas. Ele tem três partes essenciais: quais colunas você quer (a projeção), de qual tabela (o FROM) e quais linhas interessam (a seleção, no WHERE). O resto — ordenar, limitar, remover repetidos — são refinamentos sobre essas três.
Tecnicamente
A forma básica é SELECT colunas FROM tabela WHERE condição ORDER BY colunas. A projeção corresponde ao operador π da álgebra relacional e escolhe atributos; a seleção corresponde a σ e escolhe tuplas. O WHERE admite operadores de comparação, os lógicos AND, OR e NOT, e os especiais BETWEEN, IN, LIKE e IS NULL. O tratamento de NULL é a fonte de erro mais frequente: NULL significa desconhecido, e qualquer comparação com ele resulta em desconhecido, não em verdadeiro nem falso — por isso coluna = NULL nunca é verdadeiro e é preciso usar IS NULL. A lógica é ternária (verdadeiro, falso, desconhecido), e o WHERE só aceita a linha quando a condição é verdadeira; desconhecido é descartado como se fosse falso. LIKE compara padrões, com % para qualquer sequência e _ para um caractere. DISTINCT elimina tuplas duplicadas do resultado. ORDER BY define a ordenação, ASC por padrão e DESC quando pedido; sem ORDER BY, nenhuma ordem é garantida. LIMIT (ou FETCH FIRST, no padrão) restringe a quantidade de linhas devolvidas.
Principais conceitos
- Projeção
- Escolha de colunas — a lista após o SELECT. Corresponde a π na álgebra relacional.
- Seleção
- Escolha de linhas — a condição do WHERE. Corresponde a σ na álgebra relacional.
- NULL
- Ausência de valor conhecido. Não é zero nem texto vazio, e qualquer comparação com ele dá desconhecido.
- Lógica ternária
- Verdadeiro, falso e desconhecido. O WHERE só aceita a linha quando a condição é verdadeira.
- DISTINCT
- Remove tuplas duplicadas do resultado, dando-lhe comportamento de conjunto.
- LIKE
- Comparação por padrão textual: % é qualquer sequência, _ é exatamente um caractere.
Exemplos
A forma básica e os operadores do WHERE
Cada consulta demonstra um operador. Repare no par BETWEEN/IN, que substitui condições longas.
-- Projeção e seleção
SELECT descricao, preco
FROM produto
WHERE categoria_id = 3;
-- Faixa: BETWEEN inclui os extremos
SELECT descricao, preco FROM produto
WHERE preco BETWEEN 100 AND 500;
-- Conjunto: IN evita uma sequência de OR
SELECT descricao FROM produto
WHERE categoria_id IN (1, 3, 7);
-- Padrão textual
SELECT descricao FROM produto WHERE descricao LIKE 'Teclado%';
SELECT descricao FROM produto WHERE descricao LIKE '%mecânico%';
-- Valores distintos, ordenados, limitados
SELECT DISTINCT categoria_id FROM produto ORDER BY categoria_id;
SELECT descricao, preco FROM produto
ORDER BY preco DESC
LIMIT 10;Linha a linha
BETWEEN 100 AND 500- Equivale a preco >= 100 AND preco <= 500. Os dois extremos entram — esquecer isso produz erro de um item em relatórios de faixa.
IN (1, 3, 7)- Substitui categoria_id = 1 OR categoria_id = 3 OR categoria_id = 7. Mais legível e mais fácil de o otimizador tratar.
LIKE 'Teclado%'- Prefixo fixo: pode usar índice. Já '%mecânico%', com curinga no início, obriga varredura completa da tabela.
ORDER BY preco DESC LIMIT 10- Ordena e corta no servidor. Trazer tudo para a aplicação e ordenar lá transfere megabytes para descartar quase todos.
NULL: onde as consultas silenciosamente erram
Três armadilhas. Todas devolvem resultado — só que o resultado errado.
-- 1. Comparação com NULL nunca é verdadeira
SELECT * FROM cliente WHERE telefone = NULL; -- 0 linhas, SEMPRE
SELECT * FROM cliente WHERE telefone IS NULL; -- correto
-- 2. Negação não recupera os nulos
-- Cliente com cidade NULL NÃO aparece em nenhuma das duas:
SELECT * FROM cliente WHERE cidade = 'Porto Alegre';
SELECT * FROM cliente WHERE cidade <> 'Porto Alegre';
-- Para incluí-los, é preciso dizer explicitamente:
SELECT * FROM cliente
WHERE cidade <> 'Porto Alegre' OR cidade IS NULL;
-- 3. NULL em expressão contamina o resultado
SELECT preco + frete FROM pedido; -- NULL se frete for NULL
SELECT preco + COALESCE(frete, 0) FROM pedido; -- corretoLinha a linha
telefone = NULL- Sintaticamente válido, semanticamente inútil: compara com desconhecido e o resultado é desconhecido, que o WHERE descarta. Devolve zero linhas mesmo havendo mil telefones nulos.
cidade <> 'Porto Alegre'- A armadilha mais cara. Quem não é de Porto Alegre "deveria" incluir quem não tem cidade — mas desconhecido não é diferente de nada, e essas linhas somem do relatório sem aviso.
COALESCE(frete, 0)- Devolve o primeiro argumento não nulo. É como se declara o que NULL significa naquele cálculo: aqui, frete desconhecido conta como zero.
Onde isso aparece na prática
- IS NULL em vez de = NULL é a correção que faz relatórios pararem de perder as linhas justamente onde falta informação.
- DISTINCT resolve a lista de valores únicos para preencher um filtro de tela.
- ORDER BY com LIMIT é como se obtém "os dez produtos mais caros" sem trazer a tabela inteira para a aplicação.
Curiosidades
- A ordem em que se escreve o SELECT não é a ordem em que ele é executado: o FROM vem primeiro, depois WHERE, depois a projeção do SELECT e por último ORDER BY. É por isso que não se pode usar no WHERE um apelido definido no SELECT.
- NULL não é igual a NULL. Duas linhas com NULL na mesma coluna não são consideradas iguais pela comparação, embora UNIQUE e GROUP BY as tratem como iguais — inconsistência que está no padrão SQL, não nos produtos.
Exercícios
Básico
Escreva a consulta que traz descrição e preço dos produtos com preço maior que 200, do mais caro para o mais barato.
Dica
Projeção, seleção e ordenação.
Resolução comentada
SELECT descricao, preco
FROM produto
WHERE preco > 200
ORDER BY preco DESC;A lista após o SELECT é a projeção, o WHERE é a seleção e DESC inverte a ordem padrão, que é crescente.
Resposta
SELECT descricao, preco FROM produto WHERE preco > 200 ORDER BY preco DESC;
Por que SELECT * FROM cliente WHERE email = NULL não devolve os clientes sem e-mail?
Dica
O que resulta de comparar algo com desconhecido?
Resolução comentada
Porque NULL representa valor desconhecido, e comparar qualquer coisa com desconhecido produz desconhecido — nem verdadeiro nem falso. O WHERE só aceita a linha quando a condição é verdadeira, então linhas com e-mail nulo são descartadas junto com todas as outras, e a consulta devolve zero linhas sempre. O correto é WHERE email IS NULL, operador criado exatamente para testar a ausência de valor sem recorrer a comparação.
Resposta
Porque comparação com NULL resulta em desconhecido, que o WHERE descarta. Use IS NULL.
Intermediário
Escreva a consulta que lista os produtos das categorias 2, 5 ou 8 com preço entre 50 e 300, em ordem alfabética de descrição.
Dica
IN e BETWEEN deixam isso curto.
Resolução comentada
SELECT descricao, preco, categoria_id
FROM produto
WHERE categoria_id IN (2, 5, 8)
AND preco BETWEEN 50 AND 300
ORDER BY descricao;IN substitui três condições ligadas por OR e BETWEEN substitui duas comparações, incluindo os extremos 50 e 300. ORDER BY sem qualificador é ascendente, que é a ordem alfabética pedida.
Resposta
SELECT descricao, preco, categoria_id FROM produto WHERE categoria_id IN (2,5,8) AND preco BETWEEN 50 AND 300 ORDER BY descricao;
Avançado
Um relatório de "clientes que não são de Porto Alegre" usa WHERE cidade <> 'Porto Alegre' e o total não bate com o cadastro. Explique e corrija.
Dica
Quantos clientes têm cidade em branco?
Resolução comentada
Os clientes com cidade nula desapareceram do relatório. A comparação cidade <> 'Porto Alegre' avalia, nessas linhas, desconhecido <> 'Porto Alegre', cujo resultado é desconhecido — e o WHERE descarta o que não é verdadeiro. O resultado é que o cliente sem cidade cadastrada não aparece nem entre os de Porto Alegre nem entre os que não são, e a soma dos dois relatórios fica menor que o total do cadastro. A correção depende do que o negócio quer dizer. Se cidade desconhecida deve contar como "não é de Porto Alegre", escreve-se WHERE cidade <> 'Porto Alegre' OR cidade IS NULL. Se deve ser tratada à parte, o relatório precisa de uma terceira categoria, explícita. O que não se pode é deixar como está, porque a omissão é silenciosa: ninguém recebe erro, e o problema só aparece quando alguém confere os totais. A lição geral é que toda condição de desigualdade sobre coluna que admite nulo precisa de uma decisão consciente sobre os nulos — e é bom motivo para declarar NOT NULL sempre que o domínio permitir.
Resposta
Linhas com cidade NULL somem, porque a desigualdade avalia desconhecido e o WHERE a descarta. Corrija com OR cidade IS NULL, ou trate os nulos como categoria própria.
Desafio
Explique por que LIKE '%teclado%' costuma ser lento e o que fazer quando a busca por trecho é requisito.
Dica
Como um índice ordenado encontra uma palavra que pode estar no meio?
Resolução comentada
Um índice B-tree armazena os valores ordenados, e essa ordenação só ajuda quando se conhece o início do texto procurado. Com LIKE 'Teclado%', o SGBD desce a árvore até o primeiro valor que começa com "Teclado" e percorre a partir dali — trabalho proporcional ao número de acertos. Com LIKE '%teclado%', o trecho pode estar em qualquer posição, o prefixo é desconhecido e não há por onde começar a descida; resta ler todas as linhas e testar uma a uma, o que é varredura completa e cresce linearmente com o tamanho da tabela. Quando a busca por trecho é requisito, há três caminhos. O primeiro, e o mais indicado quando se busca palavras, é usar busca textual de verdade: índice de texto completo, que tokeniza o conteúdo em palavras e indexa cada uma, permitindo encontrar "teclado" onde quer que esteja, com tratamento de radicais e acentos. O segundo, para busca por trecho arbitrário e não por palavra, é um índice de trigramas, que indexa todas as sequências de três caracteres e transforma a busca por substring em busca indexada. O terceiro, quando o volume é pequeno ou a busca é rara, é aceitar a varredura — otimizar o que não dói é desperdício. O que não funciona é criar um índice B-tree comum na coluna e esperar que ele ajude: ele será simplesmente ignorado pelo otimizador, que sabe que não serve.
Resposta
Porque o curinga inicial impede usar a ordenação do índice B-tree, forçando varredura completa. Soluções: índice de texto completo para busca por palavra, índice de trigramas para trecho arbitrário, ou aceitar a varredura se o volume for pequeno.
Resumo
Conceitos importantes
- SELECT projeta colunas, WHERE seleciona linhas, ORDER BY ordena.
- NULL é desconhecido: use IS NULL, nunca = NULL.
- Desigualdade sobre coluna que admite nulo descarta os nulos silenciosamente.
- LIKE com curinga no início impede o uso de índice.
Checklist
- Sei escrever SELECT com projeção, seleção e ordenação.
- Sei usar BETWEEN, IN, LIKE e IS NULL.
- Sei prever o efeito de NULL numa condição e num cálculo.
- Sei explicar por que a ordem de escrita não é a ordem de execução.
Pontos para revisão
- A lógica ternária e por que desconhecido é descartado.
- Quando LIKE consegue usar índice e quando não consegue.
- SELECT
- projeção
- seleção
- NULL
- IS NULL
- DISTINCT
- ORDER BY
SQL — junções entre tabelas
Linguagem de Consulta Estruturada — SQL
Como reunir dados espalhados por tabelas: junção interna, externas, autojunção e o efeito de esquecer a condição de junção.
Em palavras simples
A normalização separou o cliente do pedido e o produto do item. Consultar continua exigindo os dois juntos — e é isso que a junção faz: casa as linhas de duas tabelas pela coluna que elas têm em comum. A junção interna traz só o que casa dos dois lados; a externa traz também o que ficou sem par, que costuma ser justamente o que se quer descobrir.
Tecnicamente
A junção interna (INNER JOIN) produz o conjunto de pares de tuplas que satisfazem a condição de junção, tipicamente a igualdade entre chave estrangeira e chave primária. As junções externas preservam as tuplas sem correspondência: LEFT OUTER JOIN mantém todas as da tabela à esquerda, preenchendo com NULL as colunas da direita quando não há par; RIGHT faz o simétrico; FULL mantém os dois lados. O produto cartesiano (CROSS JOIN) combina cada tupla de uma tabela com todas as da outra, e é o que se obtém acidentalmente ao listar duas tabelas no FROM sem condição de junção — com 1.000 e 5.000 linhas, o resultado tem cinco milhões. A autojunção é a junção de uma tabela com ela mesma, necessária em autorrelacionamentos, e exige apelidos distintos para diferenciar as duas ocorrências. A sintaxe explícita com JOIN ... ON é preferível à antiga, com tabelas separadas por vírgula e a condição no WHERE, porque separa a condição de junção da condição de filtro — distinção que se torna essencial em junções externas, onde pôr a condição no lugar errado transforma silenciosamente um LEFT JOIN em INNER JOIN.
Principais conceitos
- INNER JOIN
- Devolve apenas os pares de tuplas que satisfazem a condição de junção. É o padrão quando se escreve só JOIN.
- LEFT OUTER JOIN
- Mantém todas as tuplas da tabela à esquerda; onde não há par, as colunas da direita vêm nulas.
- Produto cartesiano
- Combinação de cada tupla de uma tabela com todas as da outra. Resultado de esquecer a condição de junção.
- Autojunção
- Junção de uma tabela com ela mesma, com apelidos distintos. Necessária em autorrelacionamentos.
- Apelido (alias)
- Nome curto dado a uma tabela na consulta. Obrigatório na autojunção e útil para legibilidade.
Exemplos
Interna e externa: a diferença aparece nos sem par
As mesmas duas tabelas, dois resultados. Repare em quem some na primeira consulta.
-- INTERNA: só clientes que TÊM pedido
SELECT c.nome, p.id, p.data
FROM cliente c
JOIN pedido p ON p.cliente_id = c.id;
-- EXTERNA: todos os clientes, com ou sem pedido
SELECT c.nome, p.id, p.data
FROM cliente c
LEFT JOIN pedido p ON p.cliente_id = c.id;
-- clientes sem pedido aparecem com p.id e p.data nulos
-- O uso mais valioso da externa: achar os órfãos
SELECT c.nome
FROM cliente c
LEFT JOIN pedido p ON p.cliente_id = c.id
WHERE p.id IS NULL; -- clientes que NUNCA compraram
-- Três tabelas
SELECT c.nome, pr.descricao, i.quantidade
FROM cliente c
JOIN pedido p ON p.cliente_id = c.id
JOIN item_pedido i ON i.pedido_id = p.id
JOIN produto pr ON pr.id = i.produto_id
WHERE p.data >= '2026-01-01';Linha a linha
JOIN pedido p ON p.cliente_id = c.id- A condição de junção casa a chave estrangeira com a chave primária. É sempre essa a forma quando o modelo está normalizado.
LEFT JOIN- Preserva a esquerda. O cliente sem pedido continua na saída, com as colunas de pedido nulas — a informação "não tem" passa a ser visível.
WHERE p.id IS NULL- O truque do órfão: só ficam nulas as linhas que não acharam par. Filtrar por isso devolve exatamente quem não tem correspondência.
c, p, i, pr- Apelidos curtos. Com quatro tabelas, qualificar cada coluna deixa de ser preciosismo e passa a ser o que torna a consulta legível.
Duas armadilhas: cartesiano acidental e filtro no lugar errado
Ambas devolvem resultado sem erro. A segunda é a mais traiçoeira.
-- ARMADILHA 1: faltou a condição de junção
SELECT c.nome, p.id FROM cliente c, pedido p;
-- 1.000 clientes x 5.000 pedidos = 5.000.000 de linhas
-- ARMADILHA 2: filtro da tabela externa no WHERE
SELECT c.nome, p.id
FROM cliente c
LEFT JOIN pedido p ON p.cliente_id = c.id
WHERE p.data >= '2026-01-01'; -- vira INNER JOIN!
-- Por quê: quem não tem pedido fica com p.data NULL,
-- e NULL >= '2026-01-01' é desconhecido -> linha descartada.
-- CERTO: condição da tabela externa vai no ON
SELECT c.nome, p.id
FROM cliente c
LEFT JOIN pedido p
ON p.cliente_id = c.id
AND p.data >= '2026-01-01';Linha a linha
FROM cliente c, pedido p- Sem condição, é produto cartesiano. A sintaxe com vírgula facilita esse esquecimento; com JOIN explícito, o ON cobrado pela sintaxe evita o acidente.
WHERE p.data >= ...- O WHERE age depois da junção. Como as linhas sem par têm p.data nula, a condição as descarta — e o LEFT JOIN vira INNER JOIN sem que nada acuse.
ON ... AND p.data >= ...- No ON, a condição participa do casamento. Clientes sem pedido em 2026 continuam aparecendo, com as colunas de pedido nulas, que é o que se queria.
Autojunção: cada funcionário e seu chefe
A mesma tabela, duas vezes, com apelidos diferentes. Sem os apelidos, a consulta é ambígua.
-- funcionario(id, nome, chefe_id -> funcionario.id)
SELECT f.nome AS funcionario,
c.nome AS chefe
FROM funcionario f
LEFT JOIN funcionario c ON c.id = f.chefe_id
ORDER BY c.nome, f.nome;Linha a linha
funcionario f ... funcionario c- A mesma tabela aparece duas vezes, com apelidos distintos. Para o SGBD são duas ocorrências independentes, e é isso que torna a comparação possível.
LEFT JOIN, e não JOIN- O presidente não tem chefe: chefe_id nulo. Com junção interna ele desapareceria da lista — junto com o topo de qualquer hierarquia.
AS funcionario / AS chefe- Apelidos de coluna. Sem eles, o resultado traria duas colunas chamadas nome e ninguém saberia qual é qual.
Onde isso aparece na prática
- LEFT JOIN com IS NULL é o padrão para encontrar órfãos: clientes sem pedido, produtos nunca vendidos, alunos sem matrícula.
- Autojunção resolve a consulta de hierarquia: listar cada funcionário ao lado do nome do seu chefe.
- Reconhecer um produto cartesiano acidental pelo número absurdo de linhas devolvidas é diagnóstico de rotina.
Curiosidades
- A sintaxe com vírgula no FROM é herança do SQL-86; o JOIN explícito entrou no SQL-92 e levou mais de uma década para se tornar o estilo dominante.
- RIGHT JOIN é raro na prática: quase todo mundo prefere inverter a ordem das tabelas e usar LEFT, porque a leitura da esquerda para a direita fica mais natural.
Exercícios
Básico
Escreva a consulta que lista a descrição do produto e o nome da sua categoria.
Dica
Uma junção interna pela chave estrangeira.
Resolução comentada
SELECT p.descricao, c.nome AS categoria
FROM produto p
JOIN categoria c ON c.id = p.categoria_id;A junção casa a chave estrangeira categoria_id com a chave primária de categoria. Como se usou JOIN sem qualificador, a junção é interna: produtos sem categoria não apareceriam.
Resposta
SELECT p.descricao, c.nome FROM produto p JOIN categoria c ON c.id = p.categoria_id;
Intermediário
Escreva a consulta que lista os produtos que nunca foram vendidos.
Dica
LEFT JOIN mais IS NULL.
Resolução comentada
SELECT p.codigo, p.descricao
FROM produto p
LEFT JOIN item_pedido i ON i.produto_id = p.id
WHERE i.produto_id IS NULL;O LEFT JOIN preserva todos os produtos; os que nunca apareceram em item de pedido ficam com as colunas de item nulas, e o WHERE filtra exatamente esses. É o padrão de busca de órfãos, e funciona com qualquer par de tabelas ligadas por chave estrangeira.
Resposta
SELECT p.codigo, p.descricao FROM produto p LEFT JOIN item_pedido i ON i.produto_id = p.id WHERE i.produto_id IS NULL;
Avançado
Um LEFT JOIN entre cliente e pedido, com WHERE p.situacao = 'pago', deixou de trazer os clientes sem pedido. Explique e corrija.
Dica
Em que momento o WHERE é avaliado?
Resolução comentada
O WHERE é avaliado depois da junção, sobre o resultado dela. Os clientes sem pedido chegam a esse resultado, mas com todas as colunas de pedido nulas, inclusive situacao. A condição p.situacao = 'pago' avalia então NULL = 'pago', que é desconhecido, e o WHERE descarta a linha. O efeito prático é que o LEFT JOIN foi anulado: o resultado é idêntico ao de um INNER JOIN, sem que nenhum erro seja emitido. A correção é mover a condição para o ON:
SELECT c.nome, p.id
FROM cliente c
LEFT JOIN pedido p
ON p.cliente_id = c.id
AND p.situacao = 'pago';No ON, a condição faz parte do critério de casamento: clientes sem pedido pago simplesmente não encontram par e permanecem na saída com colunas nulas. A regra geral que vale memorizar: numa junção externa, condição sobre a tabela preservada vai no WHERE; condição sobre a tabela opcional vai no ON. Trocar isso de lugar muda o resultado sem mudar a aparência da consulta.
Resposta
O WHERE age após a junção, e as linhas sem par têm situacao NULL, que a comparação descarta — anulando o LEFT JOIN. Mova a condição para o ON.
Desafio
Uma consulta que soma o total vendido por cliente passou a devolver valores inflados depois que alguém acrescentou uma junção com a tabela de telefones. Explique.
Dica
Quantas linhas passa a ter cada pedido se o cliente tem três telefones?
Resolução comentada
O problema é a multiplicação de linhas provocada por junções com relacionamentos de cardinalidade N em ramos independentes. Antes, cada item de pedido gerava uma linha, e a soma dos valores estava correta. Ao acrescentar a junção com telefone, cada linha existente passou a ser combinada com cada telefone daquele cliente: um cliente com três telefones faz cada item aparecer três vezes, e a soma triplica. O ponto importante é que não há erro de sintaxe nem de condição de junção — a junção com telefone está correta, casando cliente_id com cliente_id. O que aconteceu é que a consulta passou a percorrer dois caminhos N a partir do mesmo cliente (itens de um lado, telefones do outro), e a junção produz o produto cartesiano entre eles dentro de cada cliente. É uma armadilha especialmente perigosa porque o valor errado é plausível: ninguém desconfia de um faturamento maior. Há três saídas. A mais simples é não juntar: se os telefones não são usados na agregação, tirá-los da consulta e buscá-los separadamente. Se forem necessários na mesma saída, a segunda opção é agregar antes de juntar, calculando o total por cliente numa subconsulta e só então juntar com telefone — assim o total já está pronto e a multiplicação não o atinge. A terceira, quando se quer apenas um telefone, é reduzir o ramo a no máximo uma linha, escolhendo o telefone principal com uma subconsulta correlacionada. O diagnóstico geral: sempre que uma agregação envolver mais de um caminho N a partir da mesma entidade, o resultado está inflado até prova em contrário.
Resposta
Dois ramos N a partir do mesmo cliente (itens e telefones) geram produto cartesiano entre si: cada item se repete uma vez por telefone e a soma multiplica. Corrija removendo a junção desnecessária ou agregando numa subconsulta antes de juntar.
Resumo
Conceitos importantes
- INNER JOIN traz só o que casa; LEFT JOIN preserva a tabela da esquerda.
- LEFT JOIN + IS NULL é o padrão para encontrar registros órfãos.
- Numa junção externa, condição sobre a tabela opcional vai no ON, não no WHERE.
- Junção sem condição gera produto cartesiano.
Checklist
- Sei escrever junções de duas e de várias tabelas.
- Sei encontrar órfãos com LEFT JOIN.
- Sei prever quando o WHERE anula um LEFT JOIN.
- Sei reconhecer inflação de agregação por múltiplos caminhos N.
Pontos para revisão
- Por que condição no WHERE transforma LEFT em INNER.
- Por que duas junções N a partir da mesma entidade inflam somas.
- INNER JOIN
- LEFT JOIN
- ON
- produto cartesiano
- autojunção
- alias
SQL — agrupamento e funções de agregação
Linguagem de Consulta Estruturada — SQL
As funções de agregação, o GROUP BY, a diferença entre WHERE e HAVING e o comportamento das agregações diante de NULL.
Em palavras simples
Até agora as consultas devolviam linhas. Agregação devolve resumos: quantos, quanto somam, qual a média. O GROUP BY divide as linhas em grupos — um por cliente, um por categoria — e a função de agregação calcula um valor para cada grupo. E há duas cláusulas de filtro, não uma: o WHERE escolhe quais linhas entram nos grupos, e o HAVING escolhe quais grupos ficam no resultado.
Tecnicamente
As funções de agregação são COUNT, SUM, AVG, MIN e MAX. Aplicadas sem GROUP BY, reduzem a relação inteira a uma única tupla; com GROUP BY, produzem uma tupla por grupo. A ordem lógica de execução explica tudo o mais: FROM e JOIN montam o conjunto, WHERE filtra tuplas individuais, GROUP BY forma os grupos, as agregações são calculadas, HAVING filtra grupos, o SELECT projeta e ORDER BY ordena. Daí decorre que WHERE não pode referenciar agregação — ela ainda não foi calculada — e que HAVING pode. Toda coluna do SELECT que não esteja dentro de uma função de agregação precisa constar do GROUP BY, pois de outro modo não haveria valor único a exibir para o grupo. Quanto a NULL, todas as agregações o ignoram, com uma exceção decisiva: COUNT(*) conta tuplas e inclui as que têm nulos, enquanto COUNT(coluna) conta apenas os valores não nulos daquela coluna. AVG divide pela quantidade de valores não nulos, não pelo total de linhas — diferença que muda o resultado sempre que houver ausências.
Principais conceitos
- Função de agregação
- Calcula um valor único a partir de um conjunto de tuplas: COUNT, SUM, AVG, MIN, MAX.
- GROUP BY
- Divide as tuplas em grupos pelos valores das colunas indicadas; a agregação é calculada por grupo.
- HAVING
- Filtra grupos após a agregação. É ao grupo o que o WHERE é à tupla.
- COUNT(*) versus COUNT(coluna)
- O primeiro conta tuplas, inclusive com nulos; o segundo conta apenas valores não nulos daquela coluna.
- Ordem lógica de execução
- FROM → WHERE → GROUP BY → agregação → HAVING → SELECT → ORDER BY.
Exemplos
Agregação com e sem grupos, e os dois filtros
A mesma pergunta em graus crescentes de refinamento. Note onde cada filtro entra.
-- Sem GROUP BY: a tabela inteira vira uma linha
SELECT COUNT(*) AS total,
AVG(preco) AS preco_medio,
MAX(preco) AS mais_caro
FROM produto;
-- Com GROUP BY: uma linha por categoria
SELECT categoria_id,
COUNT(*) AS qtd,
AVG(preco) AS medio
FROM produto
GROUP BY categoria_id;
-- WHERE filtra LINHAS (antes de agrupar)
-- HAVING filtra GRUPOS (depois de agregar)
SELECT c.nome,
COUNT(p.id) AS pedidos,
SUM(p.total) AS faturado
FROM cliente c
JOIN pedido p ON p.cliente_id = c.id
WHERE p.data >= '2026-01-01' -- só pedidos de 2026 entram
GROUP BY c.id, c.nome
HAVING COUNT(p.id) > 10 -- só clientes com mais de 10
ORDER BY faturado DESC;Linha a linha
GROUP BY c.id, c.nome- Agrupa por id e inclui nome porque ele aparece no SELECT. Agrupar por id garante que homônimos não sejam somados juntos.
WHERE p.data >= '2026-01-01'- Age antes do agrupamento: pedidos de 2025 nem chegam a ser contados. Trocar isto por HAVING daria resultado diferente e mais lento.
HAVING COUNT(p.id) > 10- Age depois da agregação, sobre o valor calculado. Impossível no WHERE, porque ali a contagem ainda não existe.
NULL nas agregações: onde o número engana
Uma tabela com ausências e as três contagens possíveis. Os números diferem, e cada um responde a uma pergunta.
-- cliente: 100 linhas, das quais 30 têm email NULL
SELECT COUNT(*) AS linhas, -- 100
COUNT(email) AS com_email, -- 70
COUNT(DISTINCT cidade) AS cidades -- distintas, nulos fora
FROM cliente;
-- AVG divide pelos NÃO NULOS
-- notas: 10 alunos, 2 não fizeram a prova (nota NULL)
SELECT AVG(nota) FROM prova; -- soma / 8
SELECT AVG(COALESCE(nota, 0)) FROM prova; -- soma / 10
-- SUM de conjunto vazio é NULL, não zero
SELECT SUM(total) FROM pedido WHERE data = '1900-01-01'; -- NULL
SELECT COALESCE(SUM(total), 0) FROM pedido
WHERE data = '1900-01-01'; -- 0Linha a linha
COUNT(*) = 100 e COUNT(email) = 70- A diferença entre os dois é exatamente o número de nulos. Usar um pelo outro num relatório de preenchimento inverte a conclusão.
AVG(nota) divide por 8- Quem não fez a prova é ignorado, e a média da turma sobe. Se a regra é que falta vale zero, é preciso dizer isso com COALESCE.
SUM devolvendo NULL- Sem linhas, não há o que somar, e o resultado é desconhecido — não zero. Em relatório mensal sem movimento, isso vira célula vazia em vez de R$ 0,00.
Onde isso aparece na prática
- COUNT(*) versus COUNT(coluna) é a distinção que faz um relatório de preenchimento de cadastro dizer a verdade.
- HAVING é o que responde a perguntas como "quais clientes compraram mais de dez vezes".
- AVG ignorando nulos explica por que a média de notas sobe quando alguém não fez a prova — e por que às vezes se quer COALESCE antes.
Curiosidades
- COUNT(*) e COUNT(1) têm exatamente o mesmo desempenho nos SGBDs modernos; a crença de que um é mais rápido sobreviveu a décadas de otimizadores que os tratam de forma idêntica.
- SUM de um conjunto vazio devolve NULL, não zero — o que costuma surpreender em relatórios de período sem movimento e se resolve com COALESCE(SUM(...), 0).
Exercícios
Básico
Escreva a consulta que devolve a quantidade de produtos e o preço médio por categoria.
Dica
Uma linha por categoria.
Resolução comentada
SELECT categoria_id,
COUNT(*) AS quantidade,
AVG(preco) AS preco_medio
FROM produto
GROUP BY categoria_id;categoria_id aparece no SELECT fora de função de agregação, então precisa constar do GROUP BY — sem isso o SGBD recusa a consulta, porque não haveria valor único a exibir para o grupo.
Resposta
SELECT categoria_id, COUNT(*), AVG(preco) FROM produto GROUP BY categoria_id;
Intermediário
Explique a diferença entre WHERE e HAVING e dê um exemplo em que trocar um pelo outro muda o resultado.
Dica
Pense na ordem de execução.
Resolução comentada
WHERE filtra tuplas individuais antes do agrupamento; HAVING filtra grupos depois da agregação. A consulta que conta pedidos por cliente somente de 2026 ilustra a diferença. Com WHERE p.data >= '2026-01-01', apenas pedidos de 2026 entram nos grupos, e COUNT devolve quantos pedidos cada cliente fez em 2026 — clientes sem pedido em 2026 nem aparecem. Se a mesma condição fosse posta em HAVING, ela seria avaliada sobre grupos já formados com todos os pedidos de todos os anos, e p.data nem estaria disponível como valor único do grupo, resultando em erro ou em um filtro sobre um valor arbitrário. Além da diferença de resultado, há a de desempenho: o WHERE reduz o volume antes de agrupar, e o HAVING agrupa tudo para descartar depois.
Resposta
WHERE filtra linhas antes de agrupar; HAVING filtra grupos após agregar. Filtrar data no HAVING agruparia todos os anos antes de descartar — resultado diferente e mais lento.
Avançado
Uma tabela cliente tem 100 linhas, 30 com email nulo. Qual o valor de COUNT(*), COUNT(email) e COUNT(DISTINCT email), sabendo que entre os 70 preenchidos há 5 repetidos?
Dica
Cada forma conta uma coisa diferente.
Resolução comentada
COUNT(*) devolve 100: conta tuplas, sem olhar o conteúdo de coluna nenhuma, então os nulos entram. COUNT(email) devolve 70: conta apenas os valores não nulos da coluna, descartando os 30 nulos. COUNT(DISTINCT email) devolve 65: parte dos 70 não nulos e elimina as 5 repetições, contando cada valor uma única vez. A distinção importa muito em relatórios: perguntar "quantos clientes temos" pede COUNT(*), "quantos informaram e-mail" pede COUNT(email) e "quantos e-mails diferentes temos para envio" pede COUNT(DISTINCT email). Usar um pelo outro produz números plausíveis e errados.
Resposta
COUNT(*) = 100; COUNT(email) = 70; COUNT(DISTINCT email) = 65.
Desafio
Um relatório de "faturamento por cliente" usa JOIN e mostra apenas clientes que compraram. A diretoria quer todos, com zero para quem não comprou. Escreva a consulta e explique os dois cuidados necessários.
Dica
São dois problemas diferentes: quem aparece e o que aparece na coluna.
Resolução comentada
A consulta é:
SELECT c.nome,
COUNT(p.id) AS pedidos,
COALESCE(SUM(p.total),0) AS faturado
FROM cliente c
LEFT JOIN pedido p ON p.cliente_id = c.id
GROUP BY c.id, c.nome
ORDER BY faturado DESC;O primeiro cuidado é a junção externa. Com INNER JOIN, o cliente sem pedido não encontra par e desaparece antes mesmo do agrupamento; o LEFT JOIN o preserva, com as colunas de pedido nulas. O segundo cuidado é o tratamento do nulo resultante. Para esse cliente, o grupo contém uma única linha, com p.id e p.total nulos: COUNT(p.id) devolve 0 corretamente, porque conta valores não nulos — e é por isso que se usa COUNT(p.id) e não COUNT(*), que devolveria 1 e diria que o cliente fez um pedido. Já SUM(p.total) devolve NULL, não zero, porque não há valor algum a somar; COALESCE converte isso no zero que a diretoria pediu. Vale notar que os dois cuidados são independentes e ambos silenciosos: esquecer o LEFT JOIN omite clientes sem aviso, e usar COUNT(*) inventa um pedido que não existe. Um terceiro cuidado, se houver mais junções, é o da aula anterior: nenhum outro ramo N pode entrar na consulta, sob pena de multiplicar as somas.
Resposta
LEFT JOIN para preservar quem não comprou; COUNT(p.id) — nunca COUNT(*), que devolveria 1 — e COALESCE(SUM(p.total), 0), porque SUM de conjunto vazio é NULL.
Resumo
Conceitos importantes
- COUNT, SUM, AVG, MIN e MAX resumem conjuntos; GROUP BY define os grupos.
- Ordem lógica: FROM → WHERE → GROUP BY → agregação → HAVING → SELECT → ORDER BY.
- WHERE filtra linhas; HAVING filtra grupos.
- Agregações ignoram NULL; COUNT(*) é a exceção, e SUM de vazio é NULL.
Checklist
- Sei escrever agregações com e sem GROUP BY.
- Sei escolher entre WHERE e HAVING.
- Sei prever COUNT(*), COUNT(col) e COUNT(DISTINCT col).
- Sei montar um relatório que inclui quem não tem movimento.
Pontos para revisão
- Por que toda coluna não agregada do SELECT precisa estar no GROUP BY.
- Por que COUNT(*) devolveria 1 para um cliente sem pedidos num LEFT JOIN.
- GROUP BY
- HAVING
- COUNT
- SUM
- AVG
- COALESCE
Visões e DCL: CREATE VIEW, DROP VIEW, GRANT e REVOKE
Linguagem de Consulta Estruturada — SQL
Visões como consultas nomeadas e como mecanismo de segurança, e os comandos que concedem e revogam privilégios.
Em palavras simples
Uma visão é uma consulta com nome. Depois de criada, ela se usa como se fosse uma tabela — mas não guarda dado nenhum: toda vez que alguém a consulta, o SGBD executa a consulta original. Isso resolve dois problemas de uma vez: encapsula consultas complicadas e permite mostrar a alguém parte de uma tabela sem dar acesso ao resto dela.
Tecnicamente
CREATE VIEW nome AS consulta define uma relação derivada, armazenada como definição e não como dado; DROP VIEW a remove. A visão corresponde exatamente ao nível externo da arquitetura ANSI/SPARC vista na aula 3: é o recorte que um grupo de usuários enxerga. Suas três finalidades usuais são simplificação (encapsular junções e cálculos recorrentes), segurança (expor colunas e linhas selecionadas, ocultando o restante) e independência lógica (preservar a interface das aplicações quando as tabelas por baixo mudam). Uma visão é atualizável apenas sob condições restritas — tipicamente uma única tabela base, sem agregação, sem DISTINCT, sem GROUP BY e incluindo as colunas obrigatórias —, porque de outro modo não há como mapear a alteração de volta às tuplas de origem. A DCL controla privilégios: GRANT concede, REVOKE retira, e os privilégios usuais são SELECT, INSERT, UPDATE, DELETE e REFERENCES, concedidos a usuários ou a papéis (roles). A cláusula WITH GRANT OPTION permite que quem recebeu repasse o privilégio, o que forma uma cadeia de concessões que o REVOKE precisa desfazer em cascata.
Principais conceitos
- Visão (view)
- Relação derivada definida por uma consulta. Armazena a definição, não os dados.
- Visão atualizável
- Aquela sobre a qual INSERT, UPDATE e DELETE são possíveis: exige tabela única, sem agregação nem DISTINCT.
- DCL
- Data Control Language: a parte do SQL que controla acesso — GRANT e REVOKE.
- Privilégio
- Autorização para uma operação sobre um objeto: SELECT, INSERT, UPDATE, DELETE, REFERENCES.
- Papel (role)
- Conjunto nomeado de privilégios concedido a usuários. Evita administrar permissão pessoa a pessoa.
Exemplos
Visões para simplificar e para proteger
As duas finalidades principais, lado a lado. A segunda só funciona junto com a DCL.
-- SIMPLIFICAÇÃO: encapsula a junção usada em vários relatórios
CREATE VIEW vw_venda_detalhada AS
SELECT p.id AS pedido,
p.data,
c.nome AS cliente,
pr.descricao AS produto,
i.quantidade,
i.preco_unitario,
i.quantidade * i.preco_unitario AS subtotal
FROM pedido p
JOIN cliente c ON c.id = p.cliente_id
JOIN item_pedido i ON i.pedido_id = p.id
JOIN produto pr ON pr.id = i.produto_id;
-- Agora os relatórios ficam triviais:
SELECT cliente, SUM(subtotal) FROM vw_venda_detalhada
GROUP BY cliente;
-- SEGURANÇA: recorte sem a coluna sensível
CREATE VIEW vw_ramal AS
SELECT id, nome, setor, ramal FROM funcionario;
REVOKE ALL ON funcionario FROM recepcao;
GRANT SELECT ON vw_ramal TO recepcao;
DROP VIEW vw_ramal;Linha a linha
CREATE VIEW vw_venda_detalhada- A junção de quatro tabelas passa a ter um nome. Dez relatórios que a repetiam agora compartilham uma definição só — e corrigir um erro nela corrige nos dez.
i.quantidade * i.preco_unitario AS subtotal- Coluna calculada. Não existe em tabela nenhuma: é computada a cada consulta, e por isso nunca fica desatualizada.
REVOKE ALL ON funcionario- Passo indispensável. Criar a visão não protege nada se o usuário continuar podendo consultar a tabela base diretamente.
GRANT SELECT ON vw_ramal TO recepcao- Só agora a proteção existe: a recepção enxerga quatro colunas e não tem caminho até o salário.
Quando a visão aceita alteração e quando não
A regra decorre de haver ou não como mapear a alteração de volta para as tuplas de origem.
-- ATUALIZÁVEL: uma tabela, sem agregação
CREATE VIEW vw_produto_ativo AS
SELECT id, descricao, preco FROM produto WHERE ativo = true;
UPDATE vw_produto_ativo SET preco = 99.90 WHERE id = 5; -- OK
-- NÃO ATUALIZÁVEL: agregação
CREATE VIEW vw_faturamento AS
SELECT cliente_id, SUM(total) AS faturado
FROM pedido GROUP BY cliente_id;
UPDATE vw_faturamento SET faturado = 5000 WHERE cliente_id = 1;
-- ERRO: qual pedido deveria mudar? E em quanto?
-- NÃO ATUALIZÁVEL na prática: junção com colunas obrigatórias fora
CREATE VIEW vw_pedido_cliente AS
SELECT p.id, c.nome FROM pedido p JOIN cliente c ON c.id = p.cliente_id;
-- INSERT aqui não sabe em qual das duas tabelas inserirLinha a linha
WHERE ativo = true- Filtro não impede a atualização: cada linha da visão corresponde a exatamente uma linha da tabela, e o mapeamento é direto.
SUM(total) AS faturado- Uma linha da visão resume muitas da origem. Alterar o resumo é ambíguo — não há regra que diga como distribuir a mudança pelos pedidos.
JOIN em vw_pedido_cliente- Uma linha vem de duas tabelas. Um INSERT precisaria decidir sozinho se cria pedido, cliente ou ambos, e com quais valores para as colunas ausentes.
Onde isso aparece na prática
- Visão que expõe funcionário sem a coluna salário é o modo padrão de dar acesso à lista de ramais sem expor a folha.
- Encapsular numa visão a junção de quatro tabelas usada em dez relatórios evita que a mesma consulta seja reescrita — e divirja — dez vezes.
- Papéis (roles) evitam conceder privilégios usuário a usuário: concede-se ao papel, e o usuário entra no papel.
Curiosidades
- Visão materializada é outra coisa: ela armazena o resultado e precisa de política de atualização, trocando tempo de consulta por consistência — exatamente o dilema discutido no exercício da aula 3.
- REVOKE com CASCADE pode retirar privilégios de gente que você nem sabia que tinha acesso, se houve repasse por WITH GRANT OPTION. É o motivo prático para evitar essa cláusula.
Exercícios
Básico
Crie uma visão chamada vw_produto_caro com código, descrição e preço dos produtos acima de 1.000.
Dica
CREATE VIEW ... AS SELECT.
Resolução comentada
CREATE VIEW vw_produto_caro AS
SELECT codigo, descricao, preco
FROM produto
WHERE preco > 1000;A visão não guarda linha nenhuma: guarda esta consulta. Um produto que sofra reajuste e ultrapasse 1.000 passa a aparecer nela automaticamente, sem que nada precise ser recalculado.
Resposta
CREATE VIEW vw_produto_caro AS SELECT codigo, descricao, preco FROM produto WHERE preco > 1000;
Intermediário
Um usuário precisa consultar nome e setor dos funcionários, mas não pode ver salários. Escreva os comandos completos.
Dica
Não basta criar a visão.
Resolução comentada
CREATE VIEW vw_funcionario_publico AS
SELECT id, nome, setor FROM funcionario;
REVOKE ALL ON funcionario TO consulta; -- ou: FROM consulta
GRANT SELECT ON vw_funcionario_publico TO consulta;O passo que costuma ser esquecido é o REVOKE. Criar a visão não restringe nada por si só: se o usuário mantiver privilégio de SELECT sobre a tabela funcionario, basta consultá-la diretamente para ver os salários, e a visão vira apenas uma conveniência. A proteção existe justamente na combinação — negar a tabela e conceder a visão.
Resposta
CREATE VIEW com as três colunas; REVOKE ALL ON funcionario do usuário; GRANT SELECT na visão. Sem o REVOKE, não há proteção alguma.
Avançado
Por que uma visão com GROUP BY não pode receber UPDATE? Explique com um exemplo concreto.
Dica
Quantas linhas de origem cada linha da visão representa?
Resolução comentada
Porque não existe mapeamento único de volta para as tuplas de origem. Tome vw_faturamento(cliente_id, faturado), definida como SUM(total) agrupado por cliente. Se o cliente 1 tem cinco pedidos somando 4.000 e alguém executa UPDATE ... SET faturado = 5000 WHERE cliente_id = 1, o SGBD precisaria decidir como distribuir os 1.000 de diferença entre os cinco pedidos: tudo no primeiro? no último? proporcionalmente? criando um sexto pedido? Todas as opções são defensáveis e nenhuma está na consulta — a informação necessária para desfazer a agregação simplesmente não existe. É por isso que o padrão SQL restringe a atualizabilidade a visões em que cada linha da visão corresponde a exatamente uma linha de uma única tabela base: só nesse caso a alteração tem destino inequívoco. Quando é preciso oferecer escrita através de uma visão complexa, o caminho é declarar explicitamente a regra de mapeamento por meio de um gatilho INSTEAD OF, que intercepta a operação e executa, em seu lugar, o comando que o programador determinou. Aí a ambiguidade deixa de existir porque alguém a resolveu à mão.
Resposta
Porque cada linha da visão resume várias da origem e não há como saber como distribuir a alteração entre elas. Só é resolvível declarando a regra num gatilho INSTEAD OF.
Desafio
Discuta o uso de visões como camada de segurança comparado ao controle de acesso feito na aplicação.
Dica
Quem mais fala com o banco além da aplicação?
Resolução comentada
O argumento a favor da visão é o mesmo da integridade declarada no banco, discutido na aula 10: a aplicação não é o único caminho até o dado. Ferramentas de BI, scripts de extração, o cliente SQL do analista e o sistema seguinte que ninguém previu falam com o banco diretamente, e um controle implementado apenas na aplicação não os alcança. Com visão mais GRANT/REVOKE, a restrição vale para toda conexão, qualquer que seja a ferramenta, porque é o SGBD que a aplica. Há um segundo ganho: a regra fica declarada num lugar inspecionável — é possível auditar quem tem acesso a quê consultando o catálogo, o que não se consegue lendo código de aplicação espalhado. As limitações também são reais. Visões controlam bem o acesso por coluna e por linha estática, mas ficam desconfortáveis quando a regra depende do usuário conectado de forma dinâmica — "cada vendedor vê apenas seus clientes" exige funções de sessão dentro da visão, e a solução moderna para isso é segurança em nível de linha, não visão. Além disso, uma camada espessa de visões sobre visões dificulta a otimização e a depuração, e o número de objetos a manter cresce rápido. Também não substituem controle de acesso funcional: quem pode aprovar um pedido, quem pode cancelar uma venda, isso é regra de aplicação e não se expressa como privilégio de tabela. A conclusão prática é que não são alternativas concorrentes, e sim camadas complementares: o banco garante o que é acesso a dado, a aplicação garante o que é permissão de ação, e a primeira é a que continua valendo quando alguém abre um cliente SQL.
Resposta
Visão + GRANT vale para toda conexão, inclusive BI, scripts e acesso manual, e é auditável no catálogo — a aplicação só protege o próprio caminho. Mas não cobre regra dinâmica por usuário (caso de row-level security) nem permissão de ação. São camadas complementares.
Resumo
Conceitos importantes
- Visão é consulta nomeada; guarda definição, não dados. É o nível externo do ANSI/SPARC.
- Serve para simplificar, proteger e preservar independência lógica.
- Só é atualizável quando cada linha mapeia para uma linha de uma única tabela base.
- GRANT concede e REVOKE retira privilégios; papéis evitam administrar usuário a usuário.
Checklist
- Sei criar e remover uma visão.
- Sei montar a combinação REVOKE + GRANT que realmente protege uma coluna.
- Sei dizer se uma visão é atualizável e por quê.
- Sei comparar segurança por visão e por aplicação.
Pontos para revisão
- Por que criar a visão sem revogar a tabela não protege nada.
- Por que agregação impede a atualização através da visão.
- CREATE VIEW
- visão atualizável
- DCL
- GRANT
- REVOKE
- papel
Álgebra relacional e otimização de consultas
Otimização de Consultas com álgebra relacional
Os operadores da álgebra relacional, sua correspondência com o SQL e como o otimizador reescreve consultas usando as equivalências entre expressões.
Em palavras simples
SQL diz o que você quer; a álgebra relacional descreve como obter. Entre um e outro está o otimizador, que traduz sua consulta numa expressão algébrica e depois a reescreve em formas equivalentes até achar a mais barata. Entender a álgebra é entender por que duas consultas que devolvem o mesmo resultado podem levar tempos completamente diferentes — e por que muitas vezes levam exatamente o mesmo.
Tecnicamente
A álgebra relacional é uma linguagem procedimental cujos operandos e resultados são relações, o que permite compor expressões. Os operadores fundamentais são: seleção (σ), que escolhe tuplas por um predicado; projeção (π), que escolhe atributos e elimina duplicatas; produto cartesiano (×); união (∪); diferença (−); e renomeação (ρ). Derivados deles estão a interseção (∩) e a junção (⋈), que é a composição de produto cartesiano com seleção. União, interseção e diferença exigem compatibilidade de união: mesmo número de atributos e domínios correspondentes. A otimização baseia-se em equivalências algébricas — transformações que preservam o resultado. As duas mais importantes são a antecipação da seleção, empurrando σ para o mais perto possível das folhas da árvore de expressão, e a antecipação da projeção, descartando atributos desnecessários cedo; ambas reduzem o volume de tuplas antes das operações caras. A otimização baseada em custo, além disso, usa estatísticas do catálogo — cardinalidades, seletividade, distribuição de valores — para escolher entre planos algebricamente equivalentes, decidindo ordem de junção e método de acesso. O plano escolhido é inspecionável com EXPLAIN.
Principais conceitos
- Seleção (σ)
- Escolhe tuplas que satisfazem um predicado. Corresponde ao WHERE.
- Projeção (π)
- Escolhe atributos e elimina duplicatas. Corresponde ao SELECT com DISTINCT implícito.
- Junção (⋈)
- Produto cartesiano seguido de seleção pela condição de junção. Operador derivado.
- Compatibilidade de união
- Mesmo número de atributos e domínios correspondentes — exigida por ∪, ∩ e −.
- Equivalência algébrica
- Transformação de uma expressão em outra que produz o mesmo resultado, base da otimização.
- Otimização baseada em custo
- Escolha entre planos equivalentes usando estatísticas do catálogo sobre volume e distribuição dos dados.
Exemplos
Os operadores e seus equivalentes em SQL
Cada linha mostra a mesma operação nas duas linguagens. A projeção tem uma diferença sutil que vale notar.
ÁLGEBRA SQL
------------------------------- ----------------------------------
σ preco > 100 (produto) SELECT * FROM produto
WHERE preco > 100
π descricao, preco (produto) SELECT DISTINCT descricao, preco
FROM produto
produto × categoria SELECT * FROM produto, categoria
produto ⋈ p.cat_id = c.id SELECT * FROM produto p
categoria JOIN categoria c ON c.id = p.cat_id
r ∪ s SELECT ... UNION SELECT ...
r ∩ s SELECT ... INTERSECT SELECT ...
r − s SELECT ... EXCEPT SELECT ...
ρ p (produto) FROM produto AS pLinha a linha
π corresponde a SELECT DISTINCT- A projeção da álgebra elimina duplicatas, porque relação é conjunto. O SELECT do SQL não elimina, porque tabela é multiconjunto — a correspondência exata exige o DISTINCT.
⋈ é derivado- Junção é açúcar sintático sobre × seguido de σ. Saber disso explica por que esquecer a condição de junção devolve o produto cartesiano: você escreveu só a primeira metade.
∪, ∩, −- Exigem compatibilidade de união. Em SQL, UNION elimina duplicatas e UNION ALL não — de novo a diferença entre conjunto e multiconjunto.
A equivalência que mais economiza: antecipar a seleção
As duas expressões dão o mesmo resultado. A diferença está em quantas tuplas cada operação processa.
Consulta: itens de pedidos feitos em 2026.
pedido = 1.000.000 de tuplas, das quais 50.000 são de 2026
item_pedido = 5.000.000 de tuplas
PLANO RUIM — junta tudo, filtra depois
σ data >= '2026-01-01' ( pedido ⋈ item_pedido )
junção processa 1.000.000 x -> ~5.000.000 de tuplas
depois descarta 99% delas
PLANO BOM — filtra antes, junta o que sobrou
( σ data >= '2026-01-01' (pedido) ) ⋈ item_pedido
seleção reduz a 50.000 tuplas
junção processa 20x menos dados
Em SQL as duas se escrevem IGUAL:
SELECT * FROM pedido p JOIN item_pedido i ON i.pedido_id = p.id
WHERE p.data >= '2026-01-01';
É o otimizador que escolhe — e é por isso que SQL é declarativo.Linha a linha
σ aplicada antes da ⋈- A regra de ouro: reduza o volume o quanto antes. Junção é cara e seu custo cresce com o tamanho das entradas; filtrar primeiro encolhe as entradas.
"em SQL as duas se escrevem igual"- O ponto central da aula. Você declara o resultado desejado; a escolha do plano é do otimizador, que aplica esta equivalência sem que ninguém peça.
Lendo o plano com EXPLAIN
Como confirmar o que o otimizador realmente fez, em vez de supor.
EXPLAIN ANALYZE
SELECT c.nome, SUM(i.quantidade * i.preco_unitario)
FROM cliente c
JOIN pedido p ON p.cliente_id = c.id
JOIN item_pedido i ON i.pedido_id = p.id
WHERE p.data >= '2026-01-01'
GROUP BY c.id, c.nome;
-- O que procurar na saída:
-- Seq Scan -> varredura completa; suspeite se a tabela é grande
-- Index Scan -> usou índice
-- Hash Join / Nested Loop -> método de junção escolhido
-- rows= -> estimativa do otimizador
-- actual rows= -> quantidade real (só com ANALYZE)Linha a linha
EXPLAIN ANALYZE- EXPLAIN mostra o plano estimado; com ANALYZE, executa de fato e mostra os números reais ao lado das estimativas.
rows= contra actual rows=- A comparação mais útil da saída. Divergência grande entre estimado e real significa estatísticas desatualizadas — e um otimizador decidindo com informação errada.
Seq Scan- Nem sempre é problema: varrer é mais rápido que usar índice quando a consulta devolve boa parte da tabela. Vira problema quando a tabela é grande e o filtro é seletivo.
Onde isso aparece na prática
- EXPLAIN é a ferramenta de diagnóstico de consulta lenta: mostra o plano que o otimizador escolheu e onde está o custo.
- Estatísticas desatualizadas fazem o otimizador escolher mal — daí a manutenção periódica com ANALYZE.
- Saber que o otimizador antecipa seleções explica por que reescrever a consulta "na ordem certa" quase nunca ajuda.
Curiosidades
- Codd provou que a álgebra relacional e o cálculo relacional têm o mesmo poder de expressão; essa equivalência é a base teórica que permite ao SQL ser declarativo e ainda assim executável.
- A ordem de junção de N tabelas tem número de possibilidades que cresce fatorialmente; por isso os otimizadores usam programação dinâmica até certo número de tabelas e passam a heurísticas depois — em consultas com muitas junções, o plano escolhido pode não ser o ótimo.
Exercícios
Básico
Escreva em álgebra relacional: descrição e preço dos produtos com preço acima de 500.
Dica
Uma seleção dentro de uma projeção.
Resolução comentada
π descricao, preco ( σ preco > 500 (produto) ). Lê-se de dentro para fora: primeiro a seleção escolhe as tuplas com preço acima de 500, depois a projeção escolhe os dois atributos. Escrever na ordem inversa — projetar antes e selecionar depois — seria inválido aqui, porque após projetar apenas descricao e preco o atributo preco ainda existiria, mas em geral projetar antes pode remover justamente o atributo de que a seleção precisa.
Resposta
π descricao, preco ( σ preco > 500 (produto) )
Intermediário
Por que a projeção da álgebra relacional elimina duplicatas e o SELECT do SQL não?
Dica
Relação é conjunto; tabela é o quê?
Resolução comentada
Porque operam sobre estruturas diferentes. Na álgebra, o resultado de qualquer operação é uma relação, e relação é conjunto no sentido matemático — não admite elemento repetido. Projetar apenas a coluna cidade de uma tabela de clientes devolve, portanto, cada cidade uma única vez. Em SQL, a tabela e o resultado de consulta são multiconjuntos, que admitem repetição, e o SELECT preserva todas as ocorrências. A razão da escolha do SQL é de desempenho: eliminar duplicatas exige ordenar o resultado ou construir uma tabela de dispersão, custo que seria cobrado de toda consulta, inclusive das que não se importam com repetição. Por isso a eliminação é opcional e explícita, com DISTINCT — e o mesmo raciocínio explica UNION eliminar duplicatas enquanto UNION ALL, mais rápido, as preserva.
Resposta
Porque a álgebra opera sobre conjuntos e o SQL sobre multiconjuntos. Eliminar duplicatas custa ordenação ou dispersão, e o SQL tornou esse custo opcional via DISTINCT.
Avançado
Explique por que aplicar a seleção antes da junção é quase sempre melhor, e cite um caso em que não faz diferença.
Dica
O custo da junção depende de quê?
Resolução comentada
O custo de uma junção cresce com o tamanho das relações de entrada — no pior caso, uma varredura aninhada processa o produto das cardinalidades. Aplicar a seleção antes reduz uma das entradas, e a redução se propaga: com um milhão de pedidos dos quais cinquenta mil são de 2026, filtrar primeiro faz a junção trabalhar com vinte vezes menos tuplas, e o ganho aparece também em memória, em leitura de disco e no volume intermediário que precisa ser mantido. Filtrar depois obriga a construir o resultado completo da junção para então descartar quase tudo — trabalho feito para ser jogado fora. Há situações em que não faz diferença. A primeira, e a mais comum na prática, é que o otimizador já aplica essa transformação sozinho: escrever a consulta "na ordem certa" não muda nada, porque o plano é o mesmo. A segunda é quando a seleção é pouco seletiva — se o filtro mantém 95% das tuplas, antecipá-lo economiza pouco e ainda custa uma passagem a mais. A terceira é quando a condição não pode ser antecipada por definição: um predicado que envolve colunas das duas tabelas, como p.data > c.data_cadastro, só é avaliável depois que as tuplas foram casadas. E há o caso já visto na aula 17, em que antecipar não é apenas inútil mas errado: numa junção externa, mover a condição da tabela opcional para antes da junção muda o resultado, transformando LEFT em INNER.
Resposta
Porque o custo da junção cresce com o tamanho das entradas, e filtrar antes encolhe o que será processado. Não faz diferença quando o otimizador já faz isso, quando o filtro é pouco seletivo, ou quando o predicado envolve colunas das duas tabelas e só é avaliável depois do casamento.
Desafio
Uma consulta que rodava em 200 ms passou a levar 40 segundos, sem que a consulta ou os índices mudassem. Liste hipóteses e como investigar cada uma.
Dica
O que muda num banco sem ninguém alterar código?
Resolução comentada
A primeira hipótese, e a mais provável, é a mudança de plano por estatísticas desatualizadas. O volume dos dados cresceu ou sua distribuição mudou, e o otimizador passou a decidir com informação velha — trocando, por exemplo, um Index Scan por um Seq Scan, ou invertendo a ordem de junção. Investiga-se com EXPLAIN ANALYZE, comparando as linhas estimadas com as reais: divergência grande confirma a hipótese, e a correção é atualizar as estatísticas com ANALYZE. A segunda é o crescimento puro do volume: a consulta sempre foi ineficiente, mas com poucos dados isso não aparecia. Compara-se a cardinalidade atual das tabelas com a de meses atrás e observa-se se o plano contém alguma operação cujo custo cresce mais que linearmente. A terceira é a degradação física: em bancos com controle de versão de linhas, atualizações e exclusões deixam versões mortas que continuam sendo lidas; o sintoma é uma tabela ocupando muito mais espaço do que o volume de dados justifica, e a correção é a manutenção de rotina (VACUUM ou equivalente) e a reconstrução de índices fragmentados. A quarta é contenção: a consulta não está lenta, está esperando — bloqueada por outra transação de longa duração. Investiga-se olhando as sessões ativas e os bloqueios no momento em que a lentidão ocorre, e o sinal característico é a lentidão ser intermitente em vez de constante. A quinta é ambiental: menos memória disponível para cache, disco mais lento, concorrência maior no servidor. Compara-se com métricas de sistema no período. O método para separar as hipóteses é começar sempre pelo EXPLAIN ANALYZE, porque ele distingue de imediato entre "o plano mudou" e "o plano é o mesmo e ficou lento" — e essa bifurcação elimina metade das hipóteses de uma vez.
Resposta
Hipóteses: estatísticas desatualizadas mudando o plano (a mais provável), crescimento de volume expondo ineficiência antiga, degradação física por versões mortas e índices fragmentados, contenção por bloqueio, e mudança ambiental. Comece por EXPLAIN ANALYZE: ele separa "o plano mudou" de "o mesmo plano ficou lento".
Resumo
Conceitos importantes
- Operadores fundamentais: σ, π, ×, ∪, − e ρ; ⋈ e ∩ são derivados.
- π elimina duplicatas porque relação é conjunto; SELECT não, porque tabela é multiconjunto.
- Antecipar seleções e projeções é a principal equivalência de otimização.
- O otimizador escolhe o plano com base em estatísticas; EXPLAIN mostra o que ele decidiu.
Checklist
- Sei escrever consultas simples em álgebra relacional.
- Sei traduzir entre álgebra e SQL.
- Sei justificar por que antecipar a seleção reduz custo.
- Sei investigar uma consulta que ficou lenta sem mudar.
Pontos para revisão
- Por que SQL é declarativo e a álgebra é procedimental.
- Os três casos em que antecipar a seleção não ajuda — e o caso em que é errado.
- álgebra relacional
- seleção
- projeção
- junção
- equivalência
- otimizador
- EXPLAIN
Exercícios
Todos os exercícios da disciplina reunidos, na ordem das aulas.
Básico (22)
Liste os quatro problemas do processamento por arquivos citados na aula e escreva, para cada um, uma frase explicando o que ele causa na prática.
Dica
Dois deles são consequência um do outro; comece por esse par.
Resolução comentada
Redundância descontrolada: o mesmo dado é gravado em vários arquivos, ocupando espaço e criando várias versões da verdade. Inconsistência: como nada sincroniza essas cópias, elas divergem — é consequência direta da redundância. Dependência programa-dados: o layout do arquivo está no código, então mudar o layout obriga a alterar e recompilar todos os programas. Dificuldade de acesso concorrente: sem um árbitro entre dois programas que gravam ao mesmo tempo, o arquivo corrompe.
Resposta
Redundância descontrolada, inconsistência, dependência programa-dados e dificuldade de acesso concorrente.
Explique com suas palavras a diferença entre banco de dados e SGBD.
Dica
Um é conteúdo, o outro é programa.
Resolução comentada
O banco de dados é a coleção de dados inter-relacionados que representa um recorte do mundo real — é conteúdo armazenado. O SGBD é o software que gerencia essa coleção: define a estrutura, executa as consultas, controla quem acessa, arbitra o acesso simultâneo e recupera o banco depois de uma falha. Um banco pode ser migrado de um SGBD para outro; são coisas separadas.
Resposta
Banco de dados é a coleção de dados; SGBD é o software que a gerencia.
Classifique cada item como esquema ou instância: (a) a definição da tabela produto; (b) as 4.000 linhas de produtos cadastrados; (c) a restrição de que preço não pode ser nulo.
Dica
Instância é o que muda quando alguém usa o sistema.
Resolução comentada
(a) Esquema — é a descrição da estrutura. (b) Instância — é o conteúdo num dado momento, e muda a cada cadastro. (c) Esquema — restrições fazem parte da descrição da estrutura, não do conteúdo.
Resposta
(a) esquema; (b) instância; (c) esquema.
Nomeie os três níveis da arquitetura ANSI/SPARC e diga o que cada um descreve.
Dica
Vá do disco para o usuário.
Resolução comentada
Interno: como os dados estão fisicamente armazenados — arquivos, blocos, índices. Conceitual: a estrutura lógica completa do banco, com entidades, atributos, relacionamentos e restrições, sem detalhes de armazenamento. Externo: as visões, cada uma expondo a um grupo de usuários apenas o recorte do conceitual que lhe interessa.
Resposta
Interno (armazenamento), conceitual (estrutura lógica) e externo (visões).
Identifique as entidades e os relacionamentos: "Uma editora publica vários livros. Cada livro é escrito por um ou mais autores."
Dica
Substantivos sobre os quais se guarda informação; verbos que ligam dois deles.
Resolução comentada
Entidades: Editora, Livro, Autor. Relacionamentos: Editora publica Livro; Autor escreve Livro. As expressões "vários livros" e "um ou mais autores" indicam as cardinalidades — 1:N entre editora e livro, N:N entre autor e livro.
Resposta
Entidades: Editora, Livro, Autor. Relacionamentos: publica (Editora–Livro) e escreve (Autor–Livro).
Classifique os atributos de Livro: isbn, titulo, autores, ano_publicacao, idade_do_livro.
Dica
Um deles é identificador, um é multivalorado e um é derivado.
Resolução comentada
isbn: identificador, simples, monovalorado — distingue univocamente cada livro. titulo: descritivo, simples, monovalorado. autores: descritivo, multivalorado — um livro pode ter vários. ano_publicacao: descritivo, simples, armazenado. idade_do_livro: derivado — calculado do ano de publicação em relação ao ano corrente, e por isso não deve ser armazenado.
Resposta
isbn = identificador; titulo e ano_publicacao = descritivos simples; autores = multivalorado; idade_do_livro = derivado.
Determine a cardinalidade máxima: (a) Autor e Livro; (b) Estado e Cidade; (c) Pessoa e CPF.
Dica
Leia dos dois lados antes de responder.
Resolução comentada
(a) N:N — um autor escreve vários livros e um livro pode ter vários autores. (b) 1:N — um estado tem várias cidades, mas cada cidade pertence a um só estado. (c) 1:1 — uma pessoa tem um CPF e um CPF pertence a uma só pessoa.
Resposta
(a) N:N; (b) 1:N; (c) 1:1.
Numa universidade há Alunos e Professores, e ambos têm nome, CPF e endereço. Proponha uma generalização.
Dica
O que é comum sobe; o que distingue fica.
Resolução comentada
Cria-se a entidade genérica Pessoa, com nome, CPF e endereço. Aluno e Professor tornam-se especializações: Aluno acrescenta matrícula e semestre; Professor acrescenta titulação e regime de trabalho. A cobertura é parcial se a universidade puder cadastrar pessoas que não sejam nem aluno nem professor (um visitante, por exemplo), e a disjunção é compartilhada se alguém puder ser aluno e professor ao mesmo tempo — situação real em pós-graduação.
Resposta
Pessoa (nome, CPF, endereço) como genérica; Aluno (matrícula, semestre) e Professor (titulação, regime) como especializações.
Cite três coisas que uma ferramenta de modelagem faz automaticamente e uma que ela não consegue fazer.
Dica
A que não consegue depende de conhecer o negócio.
Resolução comentada
Faz automaticamente: converter o modelo conceitual em lógico aplicando as regras de transformação; gerar o script DDL para o SGBD escolhido; validar a estrutura do modelo, acusando entidade sem identificador ou chave estrangeira órfã; e fazer engenharia reversa de um banco existente. Não consegue: dizer se o modelo representa corretamente o negócio — uma cardinalidade errada ou uma entidade esquecida produzem um modelo perfeitamente válido e completamente errado.
Resposta
Faz: conversão conceitual→lógico, geração de DDL, validação estrutural, engenharia reversa. Não faz: verificar se o modelo corresponde ao negócio.
Dada a relação produto(codigo, descricao, preco, categoria) com 250 linhas, informe o grau e a cardinalidade.
Dica
Um conta colunas, o outro conta linhas.
Resolução comentada
O grau é 4, porque a relação tem quatro atributos: codigo, descricao, preco e categoria. A cardinalidade é 250, porque há 250 tuplas. O grau faz parte do esquema e muda só quando a estrutura é alterada; a cardinalidade faz parte da instância e muda a cada INSERT ou DELETE.
Resposta
Grau 4, cardinalidade 250.
Em livro(isbn, titulo, autor, editora, ano), quais são as chaves candidatas? Qual seria a primária?
Dica
Qual atributo não repete nunca?
Resolução comentada
A única chave candidata é {isbn}, porque o ISBN identifica univocamente uma edição de um livro. Título pode repetir entre livros diferentes, autor publica vários livros, editora publica muitos e ano é compartilhado por milhares. Combinações como {titulo, autor, ano} podem parecer únicas, mas não há garantia — o mesmo autor pode lançar duas edições no mesmo ano. A chave primária é, portanto, isbn.
Resposta
Candidata única: {isbn}, que é também a chave primária.
Converta: Departamento (1,1) — possui — (0,N) Funcionario, com Departamento(codigo, nome) e Funcionario(matricula, nome).
Dica
É 1:N. De que lado vai a chave estrangeira?
Resolução comentada
É um relacionamento 1:N, com o lado N em Funcionario. Pela regra do 1:N, a chave estrangeira vai na relação do lado N. Resultado: departamento(codigo, nome) e funcionario(matricula, nome, codigo_departamento), com codigo_departamento sendo chave estrangeira para departamento(codigo). Como a cardinalidade mínima do lado do funcionário indica que todo funcionário pertence a um departamento, a coluna é NOT NULL.
Resposta
departamento(codigo, nome); funcionario(matricula, nome, codigo_departamento NOT NULL → departamento).
A tabela aluno(matricula, nome, curso, nome_curso) está em 1FN? Justifique.
Dica
1FN pergunta só sobre atomicidade e grupos repetitivos.
Resolução comentada
Sim, está em 1FN: todos os valores são atômicos e não há grupos repetitivos nem colunas numeradas. A tabela tem outros problemas — nome_curso depende de curso, e não da matrícula, o que é uma dependência transitiva e viola a 3FN —, mas a 1FN pergunta apenas sobre atomicidade. É um lembrete importante: estar em 1FN não significa estar bem modelado, significa apenas ter passado pela primeira das três verificações.
Resposta
Sim, está em 1FN (valores atômicos, sem grupos repetitivos), embora viole a 3FN por dependência transitiva.
Uma relação em 1FN com chave primária simples pode violar a 2FN? Justifique.
Dica
O que é preciso existir para haver dependência parcial?
Resolução comentada
Não pode. A 2FN proíbe dependência parcial, que é a dependência de parte da chave. Com chave simples, não existe "parte da chave" — ou o atributo depende da chave inteira, ou não depende dela. Portanto toda relação em 1FN com chave primária simples está automaticamente em 2FN. Isso torna a verificação da 2FN necessária apenas em relações com chave composta, o que é uma economia útil ao normalizar um esquema grande.
Resposta
Não. Sem chave composta não há "parte da chave", logo não há dependência parcial possível.
Escreva o CREATE TABLE de categoria(id, nome), com id como chave primária e nome obrigatório e único.
Dica
Três restrições ao todo.
Resolução comentada
CREATE TABLE categoria (
id SERIAL PRIMARY KEY,
nome VARCHAR(60) NOT NULL UNIQUE
);PRIMARY KEY já implica NOT NULL e UNIQUE em id, então não é preciso declará-los. Em nome, NOT NULL e UNIQUE são independentes e ambos necessários: sem NOT NULL, seria possível gravar categoria sem nome; sem UNIQUE, duas categorias com o mesmo nome.
Resposta
CREATE TABLE categoria (id SERIAL PRIMARY KEY, nome VARCHAR(60) NOT NULL UNIQUE);
Escreva o INSERT para cadastrar a categoria de id 7 e nome 'Periféricos'.
Dica
Nomeie as colunas.
Resolução comentada
INSERT INTO categoria (id, nome) VALUES (7, 'Periféricos');O texto vai entre aspas simples, que é o delimitador de literal do SQL padrão; aspas duplas identificam objetos, não valores. Se a coluna id fosse SERIAL, o correto seria omiti-la e deixar o banco gerar: INSERT INTO categoria (nome) VALUES ('Periféricos').
Resposta
INSERT INTO categoria (id, nome) VALUES (7, 'Periféricos');
Escreva a consulta que traz descrição e preço dos produtos com preço maior que 200, do mais caro para o mais barato.
Dica
Projeção, seleção e ordenação.
Resolução comentada
SELECT descricao, preco
FROM produto
WHERE preco > 200
ORDER BY preco DESC;A lista após o SELECT é a projeção, o WHERE é a seleção e DESC inverte a ordem padrão, que é crescente.
Resposta
SELECT descricao, preco FROM produto WHERE preco > 200 ORDER BY preco DESC;
Por que SELECT * FROM cliente WHERE email = NULL não devolve os clientes sem e-mail?
Dica
O que resulta de comparar algo com desconhecido?
Resolução comentada
Porque NULL representa valor desconhecido, e comparar qualquer coisa com desconhecido produz desconhecido — nem verdadeiro nem falso. O WHERE só aceita a linha quando a condição é verdadeira, então linhas com e-mail nulo são descartadas junto com todas as outras, e a consulta devolve zero linhas sempre. O correto é WHERE email IS NULL, operador criado exatamente para testar a ausência de valor sem recorrer a comparação.
Resposta
Porque comparação com NULL resulta em desconhecido, que o WHERE descarta. Use IS NULL.
Escreva a consulta que lista a descrição do produto e o nome da sua categoria.
Dica
Uma junção interna pela chave estrangeira.
Resolução comentada
SELECT p.descricao, c.nome AS categoria
FROM produto p
JOIN categoria c ON c.id = p.categoria_id;A junção casa a chave estrangeira categoria_id com a chave primária de categoria. Como se usou JOIN sem qualificador, a junção é interna: produtos sem categoria não apareceriam.
Resposta
SELECT p.descricao, c.nome FROM produto p JOIN categoria c ON c.id = p.categoria_id;
Escreva a consulta que devolve a quantidade de produtos e o preço médio por categoria.
Dica
Uma linha por categoria.
Resolução comentada
SELECT categoria_id,
COUNT(*) AS quantidade,
AVG(preco) AS preco_medio
FROM produto
GROUP BY categoria_id;categoria_id aparece no SELECT fora de função de agregação, então precisa constar do GROUP BY — sem isso o SGBD recusa a consulta, porque não haveria valor único a exibir para o grupo.
Resposta
SELECT categoria_id, COUNT(*), AVG(preco) FROM produto GROUP BY categoria_id;
Crie uma visão chamada vw_produto_caro com código, descrição e preço dos produtos acima de 1.000.
Dica
CREATE VIEW ... AS SELECT.
Resolução comentada
CREATE VIEW vw_produto_caro AS
SELECT codigo, descricao, preco
FROM produto
WHERE preco > 1000;A visão não guarda linha nenhuma: guarda esta consulta. Um produto que sofra reajuste e ultrapasse 1.000 passa a aparecer nela automaticamente, sem que nada precise ser recalculado.
Resposta
CREATE VIEW vw_produto_caro AS SELECT codigo, descricao, preco FROM produto WHERE preco > 1000;
Escreva em álgebra relacional: descrição e preço dos produtos com preço acima de 500.
Dica
Uma seleção dentro de uma projeção.
Resolução comentada
π descricao, preco ( σ preco > 500 (produto) ). Lê-se de dentro para fora: primeiro a seleção escolhe as tuplas com preço acima de 500, depois a projeção escolhe os dois atributos. Escrever na ordem inversa — projetar antes e selecionar depois — seria inválido aqui, porque após projetar apenas descricao e preco o atributo preco ainda existiria, mas em geral projetar antes pode remover justamente o atributo de que a seleção precisa.
Resposta
π descricao, preco ( σ preco > 500 (produto) )
Intermediário (20)
Uma escola tem um arquivo de alunos na secretaria e outro na biblioteca, cada um com nome e telefone do aluno. Descreva o que acontece quando um aluno troca de telefone e explique qual dos quatro problemas isso ilustra.
Dica
Pergunte-se: quem avisa o outro arquivo?
Resolução comentada
O aluno comunica a mudança ao setor com que tem contato — digamos, a secretaria. O arquivo da secretaria passa a ter o telefone novo; o da biblioteca continua com o antigo, porque não existe mecanismo que propague a alteração. A biblioteca liga para cobrar um livro atrasado e não encontra o aluno. O problema é a inconsistência, causada pela redundância descontrolada: o telefone está guardado duas vezes e nada obriga as duas cópias a andarem juntas.
Resposta
A biblioteca fica com o telefone desatualizado. É inconsistência, causada por redundância descontrolada.
O administrador cria um índice sobre a coluna nome da tabela cliente para acelerar as buscas. Nenhuma consulta do sistema precisou ser reescrita. Que tipo de independência de dados esse fato demonstra? Justifique.
Dica
Índice é decisão de armazenamento.
Resolução comentada
Demonstra independência física de dados. O índice é uma estrutura de armazenamento: altera como o SGBD encontra as linhas no disco, sem alterar quais linhas existem nem quais colunas a tabela tem. Como o esquema lógico permaneceu idêntico, as consultas — escritas contra o esquema lógico — continuam válidas. Quem decide usar ou não o índice é o otimizador do SGBD, em tempo de execução; o programador nem precisa saber que ele existe.
Resposta
Independência física: mudou o armazenamento, não o esquema lógico contra o qual as consultas foram escritas.
Em que nível ocorre cada mudança? (a) criar um índice; (b) acrescentar a coluna cpf; (c) criar uma visão que oculta o salário.
Dica
Pergunte se a mudança altera o que existe ou só como se chega até ele.
Resolução comentada
(a) Nível interno: o índice é um caminho de acesso, não altera o que existe logicamente. (b) Nível conceitual: acrescentar coluna altera a estrutura lógica do banco. (c) Nível externo: a visão é um recorte para um grupo de usuários; a tabela por baixo continua com o salário.
Resposta
(a) interno; (b) conceitual; (c) externo.
Por que o modelo conceitual não deve conter chaves estrangeiras nem tipos como VARCHAR(80)?
Dica
A que pergunta o modelo conceitual responde?
Resolução comentada
Porque nenhum dos dois pertence à pergunta que o modelo conceitual responde. Chave estrangeira é o mecanismo com que o modelo relacional implementa um relacionamento; num modelo conceitual o relacionamento já está representado diretamente, e antecipar a chave é misturar duas etapas. VARCHAR(80) é decisão de projeto físico, que depende do SGBD escolhido — e o modelo conceitual precisa sobreviver à troca de SGBD. Além disso, há uma razão prática: o modelo conceitual é o documento que se revisa com quem entende do negócio, e essa pessoa não tem como validar um tipo de dado, mas tem plena condição de dizer se um cliente pode ou não ter dois endereços.
Resposta
Porque ambos são decisões de implementação. O conceitual descreve o domínio e precisa sobreviver à troca de SGBD e ser validável por quem entende do negócio, não de banco.
Explique o que é uma entidade fraca e dê um exemplo diferente do apresentado na aula, indicando o identificador parcial.
Dica
Procure algo cujo número recomeça do 1 dentro de cada 'pai'.
Resolução comentada
Entidade fraca é a que não tem identificador próprio e depende da existência de outra entidade — a proprietária — para ser identificada. Sua chave é a combinação de um identificador parcial com a chave da proprietária. Exemplo: Dependente de um funcionário num plano de saúde. O identificador parcial é o nome do dependente, que só é único dentro de um mesmo funcionário — existem muitos dependentes chamados "João" na empresa, mas apenas um entre os dependentes do funcionário 4021. A chave completa é, portanto, (matricula_funcionario, nome_dependente). Se o funcionário for removido, seus dependentes deixam de ter sentido e são removidos junto: a dependência é existencial, não apenas de identificação.
Resposta
É a entidade sem identificador próprio, identificada por um identificador parcial mais a chave da proprietária. Ex.: Dependente, com identificador parcial nome, dentro de Funcionário.
Explique a diferença prática entre participação total e parcial, dizendo o que muda no banco de dados gerado.
Dica
Pense na coluna da chave estrangeira.
Resolução comentada
Participação total significa cardinalidade mínima 1: toda ocorrência da entidade obrigatoriamente participa do relacionamento. Participação parcial significa mínima 0: a ocorrência pode existir isolada. No banco, a diferença aparece na chave estrangeira: participação total vira NOT NULL, participação parcial permite NULL. Se um pedido tem participação total no relacionamento com cliente, a coluna cliente_id é NOT NULL e o SGBD passa a recusar qualquer tentativa de gravar pedido sem dono — a regra de negócio deixa de depender da aplicação lembrar de validá-la.
Resposta
Total = mínima 1 = chave estrangeira NOT NULL; parcial = mínima 0 = chave estrangeira aceita NULL.
Classifique quanto a cobertura e disjunção: (a) Veículo em Carro e Moto; (b) Funcionário em Gerente e Técnico, sabendo que há funcionários que não são nem um nem outro e que ninguém acumula os dois papéis.
Dica
São duas perguntas independentes.
Resolução comentada
(a) Cobertura total e disjunção exclusiva: todo veículo é carro ou moto, e nenhum é os dois. (b) Cobertura parcial, porque existem funcionários fora das duas categorias, e disjunção exclusiva, porque ninguém acumula os papéis. O enunciado de (b) informa exatamente as duas dimensões — é assim que elas devem ser levantadas com o cliente: perguntando "existe algum que não seja nenhum dos dois?" e "pode ser os dois ao mesmo tempo?".
Resposta
(a) total e exclusiva; (b) parcial e exclusiva.
Em que ponto do fluxo o modelo deixa de ser independente de SGBD? Explique a consequência prática.
Dica
Siga o diagrama até a última seta descendente.
Resolução comentada
Na geração do DDL. Até o modelo lógico, tudo é vocabulário do modelo relacional — tabelas, colunas, chaves — que vale para qualquer SGBD relacional. É na geração do script que se escolhe o produto, e aí entram tipos específicos (SERIAL do PostgreSQL contra AUTO_INCREMENT do MySQL), sintaxe de restrição e detalhes de armazenamento. A consequência prática é boa: como a dependência aparece só no último passo, migrar de SGBD não exige remodelar nada — basta gerar o DDL de novo escolhendo outro destino. É a independência de tecnologia sendo preservada até o momento em que deixar de preservá-la se torna inevitável.
Resposta
Na geração do DDL. Como é o último passo, trocar de SGBD não exige remodelar: basta regerar o script para o novo destino.
Explique por que a relação não ter ordem entre as tuplas é uma propriedade, e não uma limitação.
Dica
Quem decide a ordem, e quando?
Resolução comentada
Não impor ordem no armazenamento libera o SGBD para escolher, em cada consulta, a estratégia de acesso mais rápida — ler pela ordem física, por um índice, ou em paralelo por várias partições. Se a relação tivesse ordem intrínseca, toda leitura teria de respeitá-la e essas otimizações seriam impossíveis. A ordem passa a ser uma decisão de consulta, expressa por ORDER BY, e cada consulta pede a sua: o mesmo dado sai ordenado por nome num relatório e por data em outro, sem que nada seja reorganizado no disco. É separar o que o dado é do modo como ele é apresentado — e é limitação apenas para quem escreve consulta contando com uma ordem que nunca foi prometida.
Resposta
Porque libera o SGBD a escolher a melhor estratégia de acesso e transfere a ordenação para a consulta, onde cada uma pede a ordem que precisa.
Explique a diferença entre chave candidata e superchave, com um exemplo em que uma superchave não é candidata.
Dica
A palavra que diferencia é mínima.
Resolução comentada
Superchave é qualquer conjunto de atributos que identifique univocamente uma tupla, independentemente de conter atributos desnecessários. Chave candidata é uma superchave mínima: retirar qualquer atributo dela faz perder a unicidade. Em aluno(matricula, cpf, nome, semestre), o conjunto {matricula, nome} é superchave, porque conhecidos matrícula e nome identifica-se exatamente uma tupla. Não é candidata, porém, porque {matricula} sozinha já identifica — nome é supérfluo. Toda chave candidata é superchave; a recíproca é falsa.
Resposta
Superchave identifica; candidata identifica e é mínima. {matricula, nome} é superchave mas não candidata, pois {matricula} basta.
Converta um N:N entre Medico e Paciente com atributos data e diagnostico do relacionamento. Qual a chave primária da tabela gerada?
Dica
Cuidado: o par (medico, paciente) basta como chave?
Resolução comentada
O N:N gera uma relação própria: consulta(crm, cpf_paciente, data, diagnostico), com crm referenciando medico e cpf_paciente referenciando paciente. A chave primária, porém, não pode ser apenas (crm, cpf_paciente): isso impediria que o mesmo médico atendesse o mesmo paciente mais de uma vez, o que é irreal. A data precisa entrar na chave, resultando em (crm, cpf_paciente, data). Se houver mais de uma consulta no mesmo dia, nem isso basta, e o caminho é reconhecer que Consulta é uma entidade com identidade própria e dar-lhe uma chave artificial (id), mantendo as duas chaves estrangeiras como colunas comuns. Esse é o sinal, visto na aula 5, de que o relacionamento quer ser entidade.
Resposta
consulta(crm, cpf_paciente, data, diagnostico) com PK (crm, cpf_paciente, data) — e, se houver mais de uma no mesmo dia, promover Consulta a entidade com id próprio.
Em item_pedido(num_pedido, cod_produto, desc_produto, quantidade), com chave (num_pedido, cod_produto), liste as dependências funcionais.
Dica
Pergunte, para cada atributo, de que ele depende de fato.
Resolução comentada
As dependências são: (num_pedido, cod_produto) → quantidade, porque a quantidade depende da combinação — é a quantidade daquele produto naquele pedido; e cod_produto → desc_produto, porque a descrição depende apenas do produto, independentemente do pedido. A segunda é uma dependência parcial: desc_produto depende de parte da chave, não dela inteira. É exatamente essa dependência parcial que viola a 2FN e que motiva separar produto numa tabela própria.
Resposta
(num_pedido, cod_produto) → quantidade (total) e cod_produto → desc_produto (parcial, viola a 2FN).
Normalize até a 3FN: funcionario(matricula, nome, cod_depto, nome_depto, cod_cargo, nome_cargo, salario_base), sabendo que salario_base depende do cargo.
Dica
Procure o que depende de coluna que não é chave.
Resolução comentada
As dependências são: matricula → nome, cod_depto, cod_cargo; cod_depto → nome_depto; cod_cargo → nome_cargo, salario_base. A chave é simples (matricula), então a 2FN já está satisfeita. As duas últimas dependências são transitivas e violam a 3FN. Decompondo:
funcionario(matricula, nome, cod_depto, cod_cargo)
departamento(cod_depto, nome_depto)
cargo(cod_cargo, nome_cargo, salario_base)O ganho é imediato: reajustar o salário-base de um cargo passa a ser uma linha alterada em cargo, e não uma alteração em todos os funcionários daquele cargo — com o risco de deixar um para trás.
Resposta
funcionario(matricula, nome, cod_depto, cod_cargo); departamento(cod_depto, nome_depto); cargo(cod_cargo, nome_cargo, salario_base).
Por que usar NUMERIC(10,2) e não FLOAT para valores monetários?
Dica
Some 0,10 + 0,20 em ponto flutuante.
Resolução comentada
Porque FLOAT é ponto flutuante binário e não representa exatamente frações decimais. O valor 0,1 não tem representação finita em base 2, do mesmo modo que 1/3 não tem em base 10; o que se armazena é uma aproximação. Somando 0,10 + 0,20 obtém-se 0,30000000000000004, e o erro se acumula a cada operação. Em dinheiro isso é inaceitável: totais não fecham, comparações de igualdade falham e o balanço fica com centavos de diferença que ninguém consegue explicar. NUMERIC (ou DECIMAL) é aritmética decimal exata: armazena os dígitos e a posição da vírgula, e 0,10 + 0,20 dá exatamente 0,30. A regra é usar NUMERIC para qualquer valor que precise ser exato — dinheiro, quantidades contábeis — e reservar FLOAT para grandezas científicas, em que a aproximação já faz parte da medida.
Resposta
FLOAT é binário e aproxima frações decimais, acumulando erro (0,10+0,20 = 0,30000000000000004). NUMERIC é decimal exato — obrigatório para dinheiro.
Escreva o UPDATE que dá 5% de desconto em todos os produtos da categoria 2 com preço acima de 500.
Dica
São duas condições no WHERE.
Resolução comentada
UPDATE produto
SET preco = preco * 0.95
WHERE categoria_id = 2
AND preco > 500;O cálculo usa o valor corrente da própria coluna, então não é preciso consultar antes. As duas condições ligadas por AND restringem a linhas que satisfaçam ambas. Convém rodar antes o SELECT com o mesmo WHERE para conferir quantas linhas serão afetadas.
Resposta
UPDATE produto SET preco = preco * 0.95 WHERE categoria_id = 2 AND preco > 500;
Escreva a consulta que lista os produtos das categorias 2, 5 ou 8 com preço entre 50 e 300, em ordem alfabética de descrição.
Dica
IN e BETWEEN deixam isso curto.
Resolução comentada
SELECT descricao, preco, categoria_id
FROM produto
WHERE categoria_id IN (2, 5, 8)
AND preco BETWEEN 50 AND 300
ORDER BY descricao;IN substitui três condições ligadas por OR e BETWEEN substitui duas comparações, incluindo os extremos 50 e 300. ORDER BY sem qualificador é ascendente, que é a ordem alfabética pedida.
Resposta
SELECT descricao, preco, categoria_id FROM produto WHERE categoria_id IN (2,5,8) AND preco BETWEEN 50 AND 300 ORDER BY descricao;
Escreva a consulta que lista os produtos que nunca foram vendidos.
Dica
LEFT JOIN mais IS NULL.
Resolução comentada
SELECT p.codigo, p.descricao
FROM produto p
LEFT JOIN item_pedido i ON i.produto_id = p.id
WHERE i.produto_id IS NULL;O LEFT JOIN preserva todos os produtos; os que nunca apareceram em item de pedido ficam com as colunas de item nulas, e o WHERE filtra exatamente esses. É o padrão de busca de órfãos, e funciona com qualquer par de tabelas ligadas por chave estrangeira.
Resposta
SELECT p.codigo, p.descricao FROM produto p LEFT JOIN item_pedido i ON i.produto_id = p.id WHERE i.produto_id IS NULL;
Explique a diferença entre WHERE e HAVING e dê um exemplo em que trocar um pelo outro muda o resultado.
Dica
Pense na ordem de execução.
Resolução comentada
WHERE filtra tuplas individuais antes do agrupamento; HAVING filtra grupos depois da agregação. A consulta que conta pedidos por cliente somente de 2026 ilustra a diferença. Com WHERE p.data >= '2026-01-01', apenas pedidos de 2026 entram nos grupos, e COUNT devolve quantos pedidos cada cliente fez em 2026 — clientes sem pedido em 2026 nem aparecem. Se a mesma condição fosse posta em HAVING, ela seria avaliada sobre grupos já formados com todos os pedidos de todos os anos, e p.data nem estaria disponível como valor único do grupo, resultando em erro ou em um filtro sobre um valor arbitrário. Além da diferença de resultado, há a de desempenho: o WHERE reduz o volume antes de agrupar, e o HAVING agrupa tudo para descartar depois.
Resposta
WHERE filtra linhas antes de agrupar; HAVING filtra grupos após agregar. Filtrar data no HAVING agruparia todos os anos antes de descartar — resultado diferente e mais lento.
Um usuário precisa consultar nome e setor dos funcionários, mas não pode ver salários. Escreva os comandos completos.
Dica
Não basta criar a visão.
Resolução comentada
CREATE VIEW vw_funcionario_publico AS
SELECT id, nome, setor FROM funcionario;
REVOKE ALL ON funcionario TO consulta; -- ou: FROM consulta
GRANT SELECT ON vw_funcionario_publico TO consulta;O passo que costuma ser esquecido é o REVOKE. Criar a visão não restringe nada por si só: se o usuário mantiver privilégio de SELECT sobre a tabela funcionario, basta consultá-la diretamente para ver os salários, e a visão vira apenas uma conveniência. A proteção existe justamente na combinação — negar a tabela e conceder a visão.
Resposta
CREATE VIEW com as três colunas; REVOKE ALL ON funcionario do usuário; GRANT SELECT na visão. Sem o REVOKE, não há proteção alguma.
Por que a projeção da álgebra relacional elimina duplicatas e o SELECT do SQL não?
Dica
Relação é conjunto; tabela é o quê?
Resolução comentada
Porque operam sobre estruturas diferentes. Na álgebra, o resultado de qualquer operação é uma relação, e relação é conjunto no sentido matemático — não admite elemento repetido. Projetar apenas a coluna cidade de uma tabela de clientes devolve, portanto, cada cidade uma única vez. Em SQL, a tabela e o resultado de consulta são multiconjuntos, que admitem repetição, e o SELECT preserva todas as ocorrências. A razão da escolha do SQL é de desempenho: eliminar duplicatas exige ordenar o resultado ou construir uma tabela de dispersão, custo que seria cobrado de toda consulta, inclusive das que não se importam com repetição. Por isso a eliminação é opcional e explícita, com DISTINCT — e o mesmo raciocínio explica UNION eliminar duplicatas enquanto UNION ALL, mais rápido, as preserva.
Resposta
Porque a álgebra opera sobre conjuntos e o SQL sobre multiconjuntos. Eliminar duplicatas custa ordenação ou dispersão, e o SQL tornou esse custo opcional via DISTINCT.
Avançado (20)
Explique por que um arquivo de log de aplicação não sofre dos problemas descritos na aula, mesmo sendo um arquivo puro sem SGBD nenhum.
Dica
Pense no que se faz com um log depois de escrito.
Resolução comentada
Os quatro problemas nascem da atualização de dados duplicados. Um log é append-only: cada linha é escrita uma vez e nunca alterada nem apagada. Sem atualização, não há como duas cópias divergirem — a inconsistência não tem como surgir. A redundância existe (o log repete dados que estão no banco), mas é redundância controlada e intencional, com finalidade de auditoria. A concorrência é resolvida pelo próprio sistema operacional, que garante atomicidade de escritas pequenas em modo append. E a dependência programa-dados é irrelevante porque o log não é lido por programas que dependem de seu layout, e sim por humanos e ferramentas tolerantes a formato.
Resposta
Porque o log é append-only: sem atualização de dado já gravado, não há divergência entre cópias — e é justamente a atualização que gera os quatro problemas.
Uma equipe acrescenta a coluna data_nascimento à tabela cliente. O relatório de vendas, que faz SELECT * FROM cliente e grava as colunas em posições fixas de um arquivo, passa a gerar saída errada. A independência lógica falhou? Explique.
Dica
Pergunte de quem é a dependência: do SGBD ou do programa?
Resolução comentada
A independência lógica não falhou — ela foi anulada pelo programa. A promessa da independência lógica é que uma aplicação continue funcionando quando o esquema muda em partes que ela não usa. Mas SELECT * não nomeia as colunas que usa: ele pede todas e assume uma ordem posicional que o esquema não garante. O relatório, portanto, depende da estrutura física do resultado, e não do conjunto de colunas de que precisa. Com SELECT nome, cpf, cidade FROM cliente, o acréscimo de data_nascimento seria invisível para ele. A conclusão prática é que independência de dados é uma capacidade oferecida pelo SGBD, não uma garantia automática: o programa precisa escrever consultas que a aproveitem.
Resposta
Não. A independência lógica existe, mas SELECT * a descarta ao depender da ordem posicional das colunas em vez de nomeá-las.
Explique por que o catálogo do SGBD ser, ele próprio, um conjunto de tabelas consultáveis por SQL é uma decisão de projeto útil — e não apenas uma curiosidade.
Dica
Como uma ferramenta de modelagem descobre quais tabelas existem no banco?
Resolução comentada
Se o catálogo é feito das mesmas estruturas que o resto do banco, então toda ferramenta que já sabe falar SQL sabe interrogá-lo, sem precisar de uma interface proprietária. É assim que ferramentas de modelagem fazem engenharia reversa de um banco existente, que geradores de código descobrem colunas e tipos, e que scripts de auditoria verificam se toda tabela tem chave primária. A alternativa — um formato binário fechado — obrigaria cada ferramenta a implementar um leitor específico por SGBD. Além disso, a uniformidade reduz o próprio SGBD: o mesmo processador de consultas serve para dados do usuário e para metadados, em vez de existirem dois mecanismos.
Resposta
Porque torna os metadados acessíveis pela mesma linguagem dos dados: qualquer ferramenta que fale SQL consegue inspecionar o banco, sem interface proprietária.
Um analista modelou "Endereço" como atributo de Cliente. Outro modelou como entidade. Em que situação cada decisão está certa?
Dica
Pergunte se o endereço tem existência e identidade próprias no negócio.
Resolução comentada
Endereço como atributo está certo quando o negócio trata o endereço como uma propriedade simples do cliente, sem existência própria: cada cliente tem um endereço, ninguém precisa consultar endereços independentemente de clientes, e não há dado a guardar sobre o endereço em si. É o caso de um cadastro simples de correspondência. Endereço como entidade está certo quando ele adquire identidade própria — quando um cliente pode ter vários (cobrança, entrega, fiscal), quando dois clientes podem compartilhar o mesmo endereço, quando é preciso guardar dados sobre o endereço (coordenadas, zona de entrega, restrição de acesso) ou quando outras entidades além de cliente também se ligam a endereços. O critério não é estético: é a existência independente. Se a resposta a "faz sentido perguntar algo sobre este endereço sem falar de cliente nenhum?" for sim, é entidade.
Resposta
Atributo quando o endereço é propriedade simples e única do cliente; entidade quando tem existência independente — vários por cliente, compartilhado, ou com dados próprios.
Um sistema guarda o campo total do pedido, que é a soma dos itens. É um atributo derivado. Em que caso guardá-lo é a decisão certa?
Dica
O que acontece com o total de um pedido quando o preço do produto muda no ano seguinte?
Resolução comentada
Guardar é a decisão certa em dois casos. O primeiro é histórico: se o total for sempre recalculado a partir dos preços atuais, um pedido fechado em 2024 passa a exibir o valor de 2026 quando os preços subirem — o sistema reescreve o passado. Guardando o total (e os preços unitários praticados), o pedido registra o que de fato foi cobrado, que é um dado legal e contábil, não um cálculo. O segundo é de desempenho: um relatório que soma o faturamento de milhões de pedidos recalculando itens toda vez faz um trabalho enorme para chegar a um número que nunca mais muda. Nos dois casos a redundância deixa de ser descontrolada e passa a ser controlada — desde que a regra seja explícita: o total é congelado no fechamento do pedido e não é recalculado depois. O erro seria guardar o total e continuar permitindo edição de itens sem recalcular, aí sim criando um dado que mente em silêncio.
Resposta
Quando o valor é histórico (o que foi efetivamente cobrado, imune a mudanças futuras de preço) ou quando o custo de recalcular em relatórios é proibitivo — sempre com a regra de congelamento explícita.
Uma escola quer registrar a nota do aluno em cada disciplina. Onde a nota deve ficar? Justifique.
Dica
De quem é a nota: do aluno, da disciplina, ou de outra coisa?
Resolução comentada
A nota não pertence ao aluno nem à disciplina. Colocada em Aluno, haveria uma nota só, e o aluno cursa várias disciplinas. Colocada em Disciplina, haveria uma nota só para a turma inteira. A nota pertence ao encontro entre os dois — é atributo do relacionamento matricula-se, que é N:N. No modelo E-R ela se desenha ligada ao losango do relacionamento. Na tradução para o relacional, o relacionamento N:N vira uma tabela associativa (matricula), cuja chave primária é o par (aluno_id, disciplina_id), e a nota é uma coluna comum dessa tabela. A regra geral que fica: todo atributo que só faz sentido quando as duas pontas estão presentes é atributo do relacionamento, não das entidades.
Resposta
No relacionamento matricula-se, como atributo de relacionamento — que vira coluna da tabela associativa (aluno_id, disciplina_id, nota).
Explique quando usar agregação em vez de simplesmente transformar o relacionamento numa entidade.
Dica
Compare o que cada solução preserva do modelo original.
Resolução comentada
As duas soluções resolvem o mesmo problema e produzem, na prática, tabelas muito parecidas. A agregação é preferível quando se quer preservar no modelo a informação de que aquilo é um relacionamento entre duas entidades, e não um conceito autônomo do domínio — o vínculo entre equipamento e defeito continua sendo um vínculo, e a agregação o mantém legível como tal, com sua cardinalidade explícita. Transformar em entidade é preferível quando o conceito ganha nome próprio no vocabulário do negócio, atributos próprios e identidade independente: quando as pessoas passam a falar em "a ocorrência", "o chamado", "o atendimento", o conceito já se emancipou e insistir na agregação torna o diagrama mais difícil de ler do que precisaria. O critério prático é linguístico: se o negócio tem um substantivo para aquilo, é entidade; se só consegue descrevê-lo como "a ligação entre X e Y", é agregação.
Resposta
Agregação quando o vínculo continua sendo um relacionamento e se quer preservar isso no modelo; entidade quando o conceito ganha nome próprio, atributos e identidade no vocabulário do negócio.
Por que a engenharia reversa reconstrói o modelo lógico, mas não o conceitual?
Dica
O que existe no conceitual que não tem representação no banco?
Resolução comentada
Porque a conversão do conceitual para o lógico perde informação, e o que se perde não pode ser recuperado do banco. Uma generalização, por exemplo, vira tabelas ligadas por chave — mas uma tabela ligada a outra por chave compartilhada é indistinguível, no catálogo, de um relacionamento 1:1 comum; nada no banco diz "isto era uma hierarquia". Um relacionamento N:N vira tabela associativa, e no catálogo essa tabela é apenas mais uma tabela com duas chaves estrangeiras — pode ser um N:N traduzido ou uma entidade legítima do domínio. Um atributo multivalorado vira tabela, e no banco fica idêntico a uma entidade fraca. A engenharia reversa consegue reconstruir com fidelidade o que está declarado no catálogo — tabelas, colunas, tipos, chaves — porque isso é exatamente o modelo lógico. O conceitual exigiria adivinhar a intenção que produziu aquela estrutura, e várias intenções diferentes produzem a mesma estrutura. Na prática, ferramentas oferecem uma reconstrução conceitual aproximada, que serve de ponto de partida e precisa ser corrigida à mão por quem entende do domínio.
Resposta
Porque a conversão conceitual→lógico perde informação: generalização, N:N e atributo multivalorado produzem no banco estruturas indistinguíveis de outras coisas. Várias intenções geram o mesmo DDL, e o catálogo não guarda a intenção.
Uma tabela sem chave primária admite duas linhas idênticas. Isso contradiz a definição de relação? Como o SGBD lida com isso?
Dica
Relação é conjunto; tabela é o que o produto implementa.
Resolução comentada
Contradiz, sim, e é uma das aproximações em que tabela deixa de ser relação. Por ser conjunto, uma relação não admite elemento repetido — duas tuplas idênticas são a mesma tupla. A tabela SQL, porém, é formalmente um multiconjunto (bag): admite duplicatas, e o padrão SQL assumiu isso deliberadamente, porque eliminar duplicatas exige ordenar ou construir tabela de dispersão a cada operação, e cobrar esse custo de toda consulta seria inaceitável. Daí a linguagem oferecer o controle explícito: SELECT devolve duplicatas por padrão e SELECT DISTINCT as remove quando se quer o comportamento de conjunto; UNION elimina duplicatas e UNION ALL as preserva, sendo o segundo mais rápido justamente por não precisar verificar. A consequência prática é que duas linhas idênticas são indistinguíveis e, portanto, impossíveis de atualizar ou excluir separadamente — não há como escrever um WHERE que atinja uma e não a outra. É exatamente por isso que declarar chave primária não é formalidade: é o que devolve à tabela a propriedade que faz dela uma relação.
Resposta
Contradiz: a tabela SQL é multiconjunto, não conjunto, por decisão de desempenho. O efeito prático é que linhas idênticas não podem ser atualizadas nem excluídas separadamente — motivo pelo qual a chave primária é indispensável.
Um sistema usa ON DELETE CASCADE entre cliente e pedido. Explique o problema e proponha a alternativa.
Dica
O que acontece com o faturamento de 2024 quando alguém apaga um cadastro?
Resolução comentada
O problema é a perda irreversível de dado histórico e financeiro. Excluir um cliente apaga automaticamente todos os seus pedidos, e com eles — se o cascade continuar propagando — os itens desses pedidos. O faturamento de exercícios passados muda retroativamente, relatórios já emitidos deixam de ser reproduzíveis e obrigações fiscais de guarda de documentos são violadas. Pior: a operação parece bem-sucedida, ninguém recebe erro e a perda só é notada quando alguém compara um relatório novo com um antigo. A alternativa correta tem duas partes. A primeira é trocar por ON DELETE RESTRICT, de modo que o banco recuse excluir cliente com pedidos — o erro aparece na hora, para quem tentou, e não meses depois. A segunda é reconhecer que o negócio raramente quer mesmo excluir um cliente: quer pará-lo de aparecer nas telas. Isso é exclusão lógica, uma coluna ativo ou excluido_em que a aplicação filtra, preservando o dado e todo o histórico. As duas juntas resolvem: o CASCADE some, o RESTRICT protege contra o acidente, e a exclusão lógica atende à necessidade real que motivava a exclusão física.
Resposta
CASCADE apaga o histórico de vendas junto com o cadastro, retroativamente e sem erro. Alternativa: ON DELETE RESTRICT para proteger, mais exclusão lógica (coluna ativo) para atender à necessidade real de "sumir da tela".
Converta a entidade fraca Dependente (identificador parcial: nome) da proprietária Funcionario(matricula). Escreva o CREATE TABLE.
Dica
A chave da proprietária entra na chave da fraca.
Resolução comentada
A chave primária da entidade fraca combina a chave da proprietária com o identificador parcial: (matricula, nome). A chave estrangeira para funcionario deve ter ON DELETE CASCADE, porque a dependência é existencial — dependente sem funcionário não significa nada.
CREATE TABLE dependente (
matricula INTEGER NOT NULL,
nome VARCHAR(80) NOT NULL,
data_nascimento DATE,
parentesco VARCHAR(30),
PRIMARY KEY (matricula, nome),
FOREIGN KEY (matricula) REFERENCES funcionario(matricula)
ON DELETE CASCADE
);Repare que matricula desempenha dois papéis ao mesmo tempo: é parte da chave primária e é chave estrangeira. É exatamente isso que caracteriza a tradução de uma entidade fraca.
Resposta
PK composta (matricula, nome), com matricula sendo também FK para funcionario e ON DELETE CASCADE pela dependência existencial.
Explique por que telefone1, telefone2, telefone3 viola a 1FN, se cada célula contém um único valor atômico.
Dica
O que a 1FN proíbe além de valor não atômico?
Resolução comentada
A 1FN proíbe duas coisas: valores não atômicos e grupos repetitivos. As três colunas são atômicas individualmente, mas formam um grupo repetitivo — o mesmo atributo conceitual, telefone, replicado em posições numeradas. Os sintomas mostram que é o mesmo defeito de guardar tudo numa célula. Primeiro, há um limite arbitrário: o cliente com quatro telefones não cabe, e acrescentar telefone4 é alteração de esquema para um fato que deveria ser um simples INSERT. Segundo, há desperdício: a maioria das linhas deixa colunas nulas. Terceiro, e mais grave, a consulta fica antinatural — buscar quem tem determinado número exige WHERE telefone1 = ? OR telefone2 = ? OR telefone3 = ?, que precisa de três índices e ainda assim é difícil de otimizar. Quarto, a posição passa a ter significado que ninguém definiu: telefone1 é o principal? E se o cliente apagar o primeiro, o segundo sobe? A raiz de tudo é a mesma: a multiplicidade foi codificada na estrutura da tabela, em vez de virar linhas, que é o único lugar onde o modelo relacional sabe representar quantidade variável.
Resposta
Porque a 1FN proíbe também grupos repetitivos, e as três colunas são o mesmo atributo replicado. O resultado é limite artificial, colunas nulas, consulta com OR entre colunas e significado indefinido para a posição.
Um sistema de vendas guarda preco_unitario na tabela item_pedido, embora o preço dependa do produto. Isso viola a 2FN? Justifique.
Dica
O preço do item é o mesmo que o preço do produto?
Resolução comentada
Não viola, e a razão é que são dois atributos diferentes com nomes parecidos. O preço na tabela de produto é o preço atual de venda; o preço no item de pedido é o preço praticado naquela venda, naquele momento. Este último depende genuinamente do par (num_pedido, cod_produto): o mesmo produto vendido em dois pedidos diferentes pode ter preços diferentes, se houve reajuste ou desconto entre eles. Portanto a dependência é total, não parcial, e a 2FN está satisfeita. Este caso é importante porque mostra o limite da verificação puramente mecânica: olhando só os nomes das colunas, preco_unitario parece depender de cod_produto, e um normalizador desatento o extrairia — destruindo o histórico de preços e fazendo notas fiscais antigas mudarem de valor quando a tabela de produtos fosse atualizada. A dependência funcional é uma afirmação sobre o significado dos dados no domínio, não sobre os nomes das colunas, e só quem conhece o domínio consegue determiná-la. O sinal de que a modelagem está correta aqui é justamente o oposto do que a intuição sugere: a repetição do preço entre pedidos não é redundância, é registro histórico.
Resposta
Não viola: o preço praticado na venda depende do par (pedido, produto), não só do produto. É dependência total, e a repetição entre pedidos é registro histórico, não redundância.
Escreva a sequência de comandos para acrescentar a coluna cpf, obrigatória e única, à tabela cliente que já tem 5.000 linhas.
Dica
Obrigatória e única exigem cuidados diferentes.
Resolução comentada
Não é possível acrescentar de uma vez, e o UNIQUE traz uma dificuldade que o NOT NULL não tem: não existe valor de preenchimento genérico, porque preencher todas as linhas com o mesmo texto violaria a unicidade. A sequência é:
-- 1. acrescenta permitindo nulo (UNIQUE aceita vários nulos)
ALTER TABLE cliente ADD COLUMN cpf CHAR(11);
-- 2. já declara a unicidade: nulos não conflitam entre si
ALTER TABLE cliente ADD CONSTRAINT uq_cliente_cpf UNIQUE (cpf);
-- 3. preenche as 5.000 linhas com os CPFs reais
-- (carga a partir de outra fonte; não há valor genérico possível)
-- 4. só depois de tudo preenchido
ALTER TABLE cliente ALTER COLUMN cpf SET NOT NULL;O ponto que costuma ser esquecido é o passo 3: ele não é um comando, é um projeto. Enquanto ele não termina, o passo 4 falha, e o esquema fica num estado intermediário em que a aplicação precisa tolerar cpf nulo. Por isso essa alteração se planeja com a área de negócio antes de tocar no banco.
Resposta
ADD COLUMN cpf CHAR(11) nulo; ADD CONSTRAINT UNIQUE (nulos não conflitam); carregar os CPFs reais de uma fonte externa; e só então SET NOT NULL.
Explique a diferença entre DELETE FROM pedido; e TRUNCATE TABLE pedido; quanto a efeito, desempenho e reversibilidade.
Dica
Um é DML, o outro é DDL.
Resolução comentada
Quanto ao efeito imediato, ambos deixam a tabela vazia, mas por caminhos diferentes. DELETE é DML: percorre as linhas, registra cada remoção no log de transações, dispara gatilhos de exclusão e respeita chaves estrangeiras, recusando a operação se houver linhas dependentes. TRUNCATE é DDL: descarta as páginas de dados de uma vez, sem percorrer linha a linha, sem disparar gatilhos e, em vários SGBDs, sem verificar dependências a menos que se peça CASCADE. Quanto ao desempenho, a diferença é grande em tabelas volumosas — DELETE de dez milhões de linhas gera dez milhões de entradas de log e pode levar minutos, enquanto TRUNCATE é praticamente instantâneo porque não registra linha nenhuma. Quanto à reversibilidade, DELETE é transacional em qualquer SGBD e um ROLLBACK o desfaz; TRUNCATE, por ser DDL, provoca commit implícito na maioria dos produtos e é irreversível — PostgreSQL é a exceção notável, onde TRUNCATE é transacional. Há ainda um detalhe frequentemente esquecido: TRUNCATE costuma reiniciar sequências de autoincremento, e DELETE não. Na prática, use DELETE quando houver WHERE, quando gatilhos precisarem disparar ou quando a operação precisar ser desfeita; use TRUNCATE para esvaziar tabelas de carga e de teste, onde o volume importa e a reversibilidade não.
Resposta
DELETE é DML: linha a linha, com log, gatilhos, respeito a FK e ROLLBACK possível. TRUNCATE é DDL: descarta tudo de uma vez, sem gatilhos, muito mais rápido, normalmente irreversível e reinicia sequências.
Um relatório de "clientes que não são de Porto Alegre" usa WHERE cidade <> 'Porto Alegre' e o total não bate com o cadastro. Explique e corrija.
Dica
Quantos clientes têm cidade em branco?
Resolução comentada
Os clientes com cidade nula desapareceram do relatório. A comparação cidade <> 'Porto Alegre' avalia, nessas linhas, desconhecido <> 'Porto Alegre', cujo resultado é desconhecido — e o WHERE descarta o que não é verdadeiro. O resultado é que o cliente sem cidade cadastrada não aparece nem entre os de Porto Alegre nem entre os que não são, e a soma dos dois relatórios fica menor que o total do cadastro. A correção depende do que o negócio quer dizer. Se cidade desconhecida deve contar como "não é de Porto Alegre", escreve-se WHERE cidade <> 'Porto Alegre' OR cidade IS NULL. Se deve ser tratada à parte, o relatório precisa de uma terceira categoria, explícita. O que não se pode é deixar como está, porque a omissão é silenciosa: ninguém recebe erro, e o problema só aparece quando alguém confere os totais. A lição geral é que toda condição de desigualdade sobre coluna que admite nulo precisa de uma decisão consciente sobre os nulos — e é bom motivo para declarar NOT NULL sempre que o domínio permitir.
Resposta
Linhas com cidade NULL somem, porque a desigualdade avalia desconhecido e o WHERE a descarta. Corrija com OR cidade IS NULL, ou trate os nulos como categoria própria.
Um LEFT JOIN entre cliente e pedido, com WHERE p.situacao = 'pago', deixou de trazer os clientes sem pedido. Explique e corrija.
Dica
Em que momento o WHERE é avaliado?
Resolução comentada
O WHERE é avaliado depois da junção, sobre o resultado dela. Os clientes sem pedido chegam a esse resultado, mas com todas as colunas de pedido nulas, inclusive situacao. A condição p.situacao = 'pago' avalia então NULL = 'pago', que é desconhecido, e o WHERE descarta a linha. O efeito prático é que o LEFT JOIN foi anulado: o resultado é idêntico ao de um INNER JOIN, sem que nenhum erro seja emitido. A correção é mover a condição para o ON:
SELECT c.nome, p.id
FROM cliente c
LEFT JOIN pedido p
ON p.cliente_id = c.id
AND p.situacao = 'pago';No ON, a condição faz parte do critério de casamento: clientes sem pedido pago simplesmente não encontram par e permanecem na saída com colunas nulas. A regra geral que vale memorizar: numa junção externa, condição sobre a tabela preservada vai no WHERE; condição sobre a tabela opcional vai no ON. Trocar isso de lugar muda o resultado sem mudar a aparência da consulta.
Resposta
O WHERE age após a junção, e as linhas sem par têm situacao NULL, que a comparação descarta — anulando o LEFT JOIN. Mova a condição para o ON.
Uma tabela cliente tem 100 linhas, 30 com email nulo. Qual o valor de COUNT(*), COUNT(email) e COUNT(DISTINCT email), sabendo que entre os 70 preenchidos há 5 repetidos?
Dica
Cada forma conta uma coisa diferente.
Resolução comentada
COUNT(*) devolve 100: conta tuplas, sem olhar o conteúdo de coluna nenhuma, então os nulos entram. COUNT(email) devolve 70: conta apenas os valores não nulos da coluna, descartando os 30 nulos. COUNT(DISTINCT email) devolve 65: parte dos 70 não nulos e elimina as 5 repetições, contando cada valor uma única vez. A distinção importa muito em relatórios: perguntar "quantos clientes temos" pede COUNT(*), "quantos informaram e-mail" pede COUNT(email) e "quantos e-mails diferentes temos para envio" pede COUNT(DISTINCT email). Usar um pelo outro produz números plausíveis e errados.
Resposta
COUNT(*) = 100; COUNT(email) = 70; COUNT(DISTINCT email) = 65.
Por que uma visão com GROUP BY não pode receber UPDATE? Explique com um exemplo concreto.
Dica
Quantas linhas de origem cada linha da visão representa?
Resolução comentada
Porque não existe mapeamento único de volta para as tuplas de origem. Tome vw_faturamento(cliente_id, faturado), definida como SUM(total) agrupado por cliente. Se o cliente 1 tem cinco pedidos somando 4.000 e alguém executa UPDATE ... SET faturado = 5000 WHERE cliente_id = 1, o SGBD precisaria decidir como distribuir os 1.000 de diferença entre os cinco pedidos: tudo no primeiro? no último? proporcionalmente? criando um sexto pedido? Todas as opções são defensáveis e nenhuma está na consulta — a informação necessária para desfazer a agregação simplesmente não existe. É por isso que o padrão SQL restringe a atualizabilidade a visões em que cada linha da visão corresponde a exatamente uma linha de uma única tabela base: só nesse caso a alteração tem destino inequívoco. Quando é preciso oferecer escrita através de uma visão complexa, o caminho é declarar explicitamente a regra de mapeamento por meio de um gatilho INSTEAD OF, que intercepta a operação e executa, em seu lugar, o comando que o programador determinou. Aí a ambiguidade deixa de existir porque alguém a resolveu à mão.
Resposta
Porque cada linha da visão resume várias da origem e não há como saber como distribuir a alteração entre elas. Só é resolvível declarando a regra num gatilho INSTEAD OF.
Explique por que aplicar a seleção antes da junção é quase sempre melhor, e cite um caso em que não faz diferença.
Dica
O custo da junção depende de quê?
Resolução comentada
O custo de uma junção cresce com o tamanho das relações de entrada — no pior caso, uma varredura aninhada processa o produto das cardinalidades. Aplicar a seleção antes reduz uma das entradas, e a redução se propaga: com um milhão de pedidos dos quais cinquenta mil são de 2026, filtrar primeiro faz a junção trabalhar com vinte vezes menos tuplas, e o ganho aparece também em memória, em leitura de disco e no volume intermediário que precisa ser mantido. Filtrar depois obriga a construir o resultado completo da junção para então descartar quase tudo — trabalho feito para ser jogado fora. Há situações em que não faz diferença. A primeira, e a mais comum na prática, é que o otimizador já aplica essa transformação sozinho: escrever a consulta "na ordem certa" não muda nada, porque o plano é o mesmo. A segunda é quando a seleção é pouco seletiva — se o filtro mantém 95% das tuplas, antecipá-lo economiza pouco e ainda custa uma passagem a mais. A terceira é quando a condição não pode ser antecipada por definição: um predicado que envolve colunas das duas tabelas, como p.data > c.data_cadastro, só é avaliável depois que as tuplas foram casadas. E há o caso já visto na aula 17, em que antecipar não é apenas inútil mas errado: numa junção externa, mover a condição da tabela opcional para antes da junção muda o resultado, transformando LEFT em INNER.
Resposta
Porque o custo da junção cresce com o tamanho das entradas, e filtrar antes encolhe o que será processado. Não faz diferença quando o otimizador já faz isso, quando o filtro é pouco seletivo, ou quando o predicado envolve colunas das duas tabelas e só é avaliável depois do casamento.
Desafio (20)
Um sistema de vendas guarda, em cada pedido, o nome e o endereço do cliente copiados do cadastro. Um colega diz que isso é redundância descontrolada e deve ser eliminado. Argumente a favor de manter essa cópia e diga em que condição ele teria razão.
Dica
O que deve constar numa nota fiscal emitida em 2024 se o cliente mudou de endereço em 2025?
Resolução comentada
A cópia no pedido não é redundância descontrolada: é um dado histórico. O endereço no pedido responde a "para onde esta compra foi entregue", que é uma pergunta diferente de "onde o cliente mora hoje" — respondida pelo cadastro. Se o pedido apenas apontasse para o cadastro, atualizar o endereço do cliente reescreveria o passado, e a nota fiscal de dois anos atrás passaria a mostrar um endereço que não existia na época. Isso é redundância controlada, decidida de propósito, e o nome técnico do padrão é snapshot de dados transacionais. O colega teria razão se o campo copiado fosse usado como se fosse o dado atual — por exemplo, se a tela de cadastro do cliente lesse o endereço a partir do último pedido, ou se um relatório de mala direta usasse os endereços dos pedidos em vez do cadastro. Aí passariam a existir duas fontes disputando a mesma pergunta, que é exatamente a definição do problema.
Resposta
É redundância controlada: o pedido guarda o endereço histórico da entrega, não o endereço atual do cliente. O colega só teria razão se essa cópia fosse usada para responder "onde o cliente mora hoje".
Cite duas funções do SGBD que seriam extremamente caras de reimplementar dentro da aplicação e explique por que o custo é alto.
Dica
Pense no que acontece quando duas coisas ocorrem ao mesmo tempo, e no que acontece quando falta energia.
Resolução comentada
A primeira é o controle de concorrência. Reimplementá-lo exige tratar bloqueios, detectar e resolver impasses (deadlocks) e garantir níveis de isolamento entre transações — problemas que envolvem toda a combinação de operações simultâneas possíveis, e cujos erros aparecem só sob carga, de forma não determinística e quase impossível de reproduzir em teste. A segunda é a recuperação após falha. O SGBD mantém um log de transações que permite refazer o que estava confirmado e desfazer o que estava pela metade quando a energia caiu; garantir isso na aplicação significaria implementar escrita à frente do log, pontos de verificação e um protocolo de recuperação que funcione mesmo se a falha ocorrer durante a própria recuperação. Nos dois casos, o custo alto não está em escrever o caminho normal, e sim em cobrir corretamente todos os caminhos de exceção — e a consequência de errar é perda ou corrupção silenciosa de dados.
Resposta
Controle de concorrência e recuperação após falha. Ambas são caras porque o difícil não é o caso normal, e sim cobrir todos os casos de exceção — cujos erros são não determinísticos e corrompem dados em silêncio.
Um sistema tem uma visão que junta três tabelas e é consultada milhares de vezes por minuto, sempre com desempenho ruim. O DBA propõe materializar a visão. Explique o que isso significa em termos dos três níveis e qual novo problema a decisão introduz.
Dica
Materializar é passar a guardar o resultado. O que passa a existir em dois lugares?
Resolução comentada
Uma visão comum é apenas uma consulta guardada: nada é armazenado, e a junção é refeita a cada acesso. Materializá-la significa passar a armazenar fisicamente o resultado. Em termos dos três níveis, a definição no nível externo permanece idêntica — as aplicações continuam consultando o mesmo nome, com as mesmas colunas — e a mudança acontece no nível interno, que agora guarda uma cópia pré-computada. É, portanto, um ganho obtido sem alterar nada acima, o que é exatamente a promessa da independência física. O novo problema é que o resultado passa a existir em dois lugares: nas tabelas de origem e na cópia materializada. Isso é redundância, e reintroduz o risco de inconsistência — se as tabelas base mudarem e a cópia não for atualizada, a visão devolve dado velho. A decisão a tomar passa a ser a política de atualização: sincronizar a cada alteração (correto, porém caro na escrita) ou periodicamente (barato, mas admitindo uma janela de dados desatualizados). Ou seja, troca-se tempo de consulta por consistência e custo de escrita.
Resposta
A definição externa não muda; o nível interno passa a guardar o resultado pré-computado. O problema novo é a redundância: a cópia pode divergir das tabelas base, e é preciso decidir a política de atualização.
Discuta esta afirmação: "Como no final tudo vira tabela, modelar em E-R é perda de tempo — melhor já desenhar as tabelas."
Dica
O que se perde ao pular o desenho e ir direto para a obra?
Resolução comentada
A afirmação confunde o destino com o caminho, e três consequências mostram por quê. Primeira: o modelo E-R é o artefato que se valida com o especialista do domínio, que não lê DDL. Pulando-o, o erro de entendimento do negócio só aparece quando o sistema já está escrito — e é o erro mais caro que existe, porque nenhuma correção de código o resolve. Segunda: o E-R representa relacionamentos N:N diretamente, enquanto o modelo relacional exige tabela associativa. Desenhando tabelas de saída, essa tabela associativa é inventada antes de o relacionamento ter sido compreendido, e é comum ela nascer sem os atributos que o relacionamento tinha. Terceira: decisões que no E-R são explícitas — se algo é entidade ou atributo, se um relacionamento é obrigatório — ficam implícitas no DDL, escondidas em NOT NULL e em nomes de coluna, e deixam de ser discutíveis. Dito isso, a afirmação tem um fundo legítimo: em domínios pequenos e já conhecidos, um E-R cerimonioso e cheio de notação é burocracia. A resposta madura não é abandonar a modelagem conceitual, e sim ajustar seu rigor ao tamanho do problema — um rascunho de dez minutos num quadro já colhe quase todo o benefício.
Resposta
É falsa como regra: o E-R é o que se valida com o especialista do domínio, representa N:N diretamente e torna explícitas decisões que o DDL esconde. Mas o rigor da notação deve ser proporcional ao tamanho do problema.
Modele: um médico atende um paciente em uma data, e nesse atendimento pode prescrever vários medicamentos. Discuta se o relacionamento é ternário e o que a alternativa mudaria.
Dica
Pergunte se "atendimento" tem atributos e existência próprios.
Resolução comentada
A tentação é modelar um relacionamento ternário entre Médico, Paciente e Medicamento. Ele é tecnicamente possível, mas esconde um problema: a data, o diagnóstico e as observações não pertencem a nenhuma das três entidades — são propriedades do encontro. E um relacionamento ternário só admite um conjunto de atributos por combinação das três pontas, o que impede, por exemplo, que o mesmo médico atenda o mesmo paciente duas vezes prescrevendo o mesmo medicamento em datas diferentes. A alternativa melhor é promover o encontro a entidade: Atendimento, com identificador próprio, ligada a Médico (1:N) e a Paciente (1:N), e ligada a Medicamento por um relacionamento N:N que carrega dose e posologia. Isso decompõe o ternário em binários, permite repetição ao longo do tempo, dá lugar natural aos atributos do encontro e ainda torna o modelo capaz de representar um atendimento sem nenhuma prescrição — situação perfeitamente real que o ternário não conseguiria registrar, já que um relacionamento ternário exige as três pontas presentes. A regra prática que fica: quando um relacionamento tem atributos próprios e pode se repetir no tempo, ele quer ser uma entidade.
Resposta
Não deve ser ternário. Atendimento deve virar entidade, ligada a Médico e Paciente por relacionamentos binários e a Medicamento por N:N — só assim cabem os atributos do encontro, a repetição no tempo e o atendimento sem prescrição.
Um analista modelou Pessoa e Passaporte como 1:1 com participação total nos dois lados. Critique a decisão e diga quando ela seria correta.
Dica
Toda pessoa tem passaporte?
Resolução comentada
A crítica principal é factual: participação total do lado de Pessoa afirma que toda pessoa cadastrada tem passaporte, o que é falso na esmagadora maioria dos domínios. O correto seria (0,1) do lado da pessoa e (1,1) do lado do passaporte — todo passaporte pertence a alguém, mas nem toda pessoa tem passaporte. Modelado como está, ou o cadastro de pessoa fica impossível sem antes emitir um passaporte, ou a regra é ignorada na implementação e o modelo passa a mentir sobre o sistema. Há ainda a questão estrutural: quando um 1:1 tem participação total dos dois lados, as duas entidades sempre aparecem juntas e a recomendação usual é fundi-las numa só tabela, já que separá-las só acrescenta uma junção sem ganho. As exceções legítimas para manter separado são duas: quando os dados têm requisitos de segurança diferentes (dados sensíveis numa tabela com permissão restrita) e quando têm frequências de acesso muito diferentes (um bloco grande e raramente lido separado do núcleo consultado o tempo todo). Neste caso concreto, porém, nada disso se aplica — o problema real é que a cardinalidade mínima do lado da pessoa está simplesmente errada.
Resposta
A participação total do lado de Pessoa está errada: deveria ser (0,1), pois nem toda pessoa tem passaporte. E 1:1 total dos dois lados normalmente pede fusão numa tabela só, salvo separação por segurança ou por frequência de acesso.
Uma hierarquia de Produto tem 12 especializações, cada uma com 3 a 8 atributos próprios. Discuta as três estratégias de tradução para tabelas e recomende uma.
Dica
Pense em quantas colunas nulas e quantas junções cada opção produz.
Resolução comentada
A primeira estratégia é a tabela única com coluna discriminadora: uma tabela Produto com o tipo e todas as colunas de todas as especializações. Com 12 especializações e até 8 atributos cada, chega-se a algo em torno de 60 colunas, das quais cada linha preenche poucas — o resto é NULL. A consulta fica simples e sem junção, mas a integridade se perde: nada impede gravar um livro com prazo de validade, porque a coluna existe para todos, e restrições NOT NULL tornam-se impossíveis nos atributos específicos. A segunda é uma tabela por especialização, sem tabela para a genérica: cada tipo com suas colunas próprias mais as comuns repetidas. Ganha-se integridade e não há coluna nula, mas perde-se a genérica — listar todos os produtos exige UNION de 12 tabelas, e nenhuma chave estrangeira consegue apontar para "um produto qualquer". A terceira é uma tabela para a genérica e uma para cada especializada, ligadas por chave primária compartilhada. Não há coluna nula, a integridade específica é declarável, existe uma tabela Produto para as chaves estrangeiras apontarem, e listar tudo é ler uma tabela só. O custo é uma junção sempre que se quer o registro completo de um tipo. Com 12 especializações e atributos numerosos, recomendo a terceira: os problemas das outras duas crescem com o número de especializações, enquanto o custo da terceira é constante — uma junção — e é o mais fácil de aceitar. A primeira só se justificaria com poucas especializações e pouquíssimos atributos próprios.
Resposta
Tabela única gera ~60 colunas quase sempre nulas e impede restrições; tabela por especializada impede referenciar "um produto qualquer" e exige UNION. Com 12 especializações, recomendo genérica + uma por especializada com chave compartilhada: custo fixo de uma junção.
Uma equipe gerou o banco a partir do modelo há um ano. Desde então, todas as alterações foram feitas direto no SGBD. Descreva os problemas e proponha um processo.
Dica
Qual dos dois artefatos é a verdade hoje?
Resolução comentada
O primeiro problema é que o modelo virou documentação falsa, que é pior do que não ter documentação: quem consulta o diagrama toma decisões com base numa estrutura que não existe mais, e não tem como saber disso. O segundo é que a decisão de projeto se perdeu — as alterações feitas direto no banco não têm registro de por que foram feitas, e um ano depois ninguém sabe se aquela coluna nova é essencial ou resquício de um experimento. O terceiro é que regerar o banco a partir do modelo tornou-se impossível sem destruir dados, o que na prática significa que ambientes novos (teste, homologação) não podem mais ser criados a partir do artefato oficial. Quanto ao processo, a primeira coisa é decidir qual artefato é a fonte da verdade, e para um banco em produção há um ano a resposta honesta é o banco. Portanto: fazer engenharia reversa para reconstruir o modelo lógico a partir do estado atual, revisá-lo à mão para reintroduzir o que a reversa não recupera, e versioná-lo junto com o código. Daí em diante, adotar migrations — arquivos de alteração versionados, aplicados em ordem, cada um com sua justificativa na mensagem de commit — de modo que a estrutura do banco passe a ter histórico e a ser reproduzível em qualquer ambiente. O modelo gráfico deixa de ser a fonte da verdade e passa a ser documentação gerada, atualizada por engenharia reversa a cada release, o que elimina a possibilidade de ele divergir de novo.
Resposta
O modelo virou documentação falsa, as decisões se perderam e não se cria mais ambiente novo a partir dele. Processo: reversa para reconstruir o modelo, adotar migrations versionadas como fonte da verdade, e regenerar o diagrama a cada release.
SGBDs modernos oferecem colunas do tipo JSON e ARRAY, que guardam vários valores numa célula. Isso invalida o modelo relacional? Quando usar?
Dica
O que se ganha e o que se perde ao guardar estrutura dentro de uma célula?
Resolução comentada
Formalmente, uma coluna JSON ou ARRAY viola a atomicidade e, portanto, a primeira forma normal — o valor deixa de ser indivisível para o modelo. Na prática, isso não invalida o modelo relacional; mostra que os produtos foram além dele em pontos específicos, e cada um desses pontos tem um custo que é preciso conhecer. O que se perde é considerável: o SGBD não valida a estrutura interna do documento, então nada impede que uma linha guarde um campo com um nome e a linha seguinte com outro; não há chave estrangeira apontando para dentro do JSON, então a integridade referencial não alcança o conteúdo; consultar por um valor interno exige sintaxe específica do produto, o que reintroduz dependência de tecnologia; e a atualização parcial normalmente reescreve o documento inteiro. O que se ganha é a capacidade de armazenar estrutura genuinamente variável sem modelá-la. O critério de uso decorre disso. Use JSON quando a estrutura for realmente heterogênea e desconhecida em tempo de projeto — atributos que variam por fabricante num catálogo, corpo de webhook recebido de terceiros, respostas de formulário dinâmico — e quando o conteúdo for lido como um bloco, sem necessidade de consulta ou integridade sobre suas partes. Não use como atalho para não criar uma tabela: se você consulta o conteúdo, filtra por ele, ordena por ele ou precisa que ele referencie outra tabela, o dado é relacional e está no lugar errado. A regra prática mais útil é a pergunta: preciso de índice, chave estrangeira ou restrição sobre isso? Se sim, é tabela.
Resposta
Viola a 1FN, mas não invalida o modelo — é extensão com custo: sem validação de estrutura, sem integridade referencial interna e com sintaxe proprietária. Use para estrutura genuinamente variável lida em bloco; se precisa de índice, FK ou restrição sobre o conteúdo, é tabela.
Um colega afirma que validar integridade na aplicação é suficiente e que chaves estrangeiras "só deixam o banco lento". Responda.
Dica
Quem mais escreve nesse banco, além da aplicação?
Resolução comentada
O argumento falha no pressuposto de que a aplicação é o único caminho até o dado, e ela nunca é. Escrevem no banco também os scripts de importação e carga inicial, o DBA em manutenção emergencial, ferramentas de BI e ETL, jobs agendados, o sistema legado que ainda não foi desligado e a próxima aplicação que alguém escreverá sem ler o código desta. Cada um desses caminhos teria de reimplementar as mesmas validações, e basta um esquecer para o dado inconsistente entrar — e uma vez dentro, ele fica, porque nada o remove. Há também a concorrência: validar na aplicação significa consultar se o cliente existe e depois inserir o pedido, e entre as duas operações outra transação pode excluir o cliente. Só uma restrição verificada pelo SGBD dentro da transação fecha essa janela; código de aplicação, por mais correto que seja, não consegue. Quanto ao desempenho, o custo existe e é conhecido: a verificação de chave estrangeira exige uma busca na tabela referenciada, barata quando há índice na chave primária — que sempre há — e é por isso que o problema real costuma ser a falta de índice na coluna da chave estrangeira, não a restrição em si. Além disso, a comparação honesta não é entre validar no banco e não validar: é entre validar no banco e validar na aplicação, e a segunda faz a mesma consulta, só que sem a garantia transacional e com uma ida e volta de rede a mais. O ganho de tirar a restrição é, portanto, menor do que parece, e o preço é abrir mão da única garantia que vale para todos os caminhos. A conclusão prática: valide nos dois lugares — na aplicação para dar mensagem de erro decente ao usuário, no banco porque é lá que a garantia é real.
Resposta
A aplicação nunca é o único caminho até o dado (importações, DBA, ETL, jobs, sistemas futuros), e só a restrição no SGBD fecha a janela de concorrência entre verificar e inserir. O custo é uma busca por índice que já existe. Valide nos dois: na aplicação pela mensagem, no banco pela garantia.
Um relacionamento ternário liga Fornecedor, Produto e Projeto, registrando a quantidade fornecida. Converta e explique por que ele não pode ser decomposto em três binários.
Dica
Tente decompor e veja qual informação some.
Resolução comentada
A conversão é direta: um relacionamento ternário sempre vira uma relação própria com as chaves das três entidades. Resulta fornecimento(cnpj_fornecedor, codigo_produto, id_projeto, quantidade), com chave primária composta pelas três colunas e três chaves estrangeiras. Quanto à decomposição, a tentativa produziria três tabelas binárias: fornecedor_produto (quem fornece o quê), produto_projeto (o que vai para qual projeto) e fornecedor_projeto (quem atende qual projeto). O problema é que essas três tabelas juntas não conseguem reconstruir o fato original. Suponha que o fornecedor A forneça parafusos e porcas, que o projeto X use parafusos e porcas, e que A atenda X e Y. As três binárias ficam satisfeitas, mas não há como distinguir se A forneceu parafusos para X e porcas para Y, ou parafusos para Y e porcas para X, ou tudo para os dois. A informação que se perde é justamente a associação simultânea das três pontas, que é a única coisa que o ternário afirma. Há ainda um segundo problema: a quantidade é atributo da combinação dos três, e nas binárias não haveria onde colocá-la sem repeti-la ou perdê-la. A regra prática que fica: um ternário só é decomponível quando a associação das três pontas for consequência das associações duas a duas — o que é raro, e precisa ser verificado caso a caso, não presumido.
Resposta
fornecimento(cnpj_fornecedor, codigo_produto, id_projeto, quantidade) com PK tripla. Não decompõe porque as três binárias não distinguem qual produto foi de qual fornecedor para qual projeto, e não há onde alojar a quantidade.
Um data warehouse guarda tabelas deliberadamente desnormalizadas. Como conciliar isso com tudo o que se disse sobre anomalias?
Dica
As anomalias dependem de uma operação específica. Qual?
Resolução comentada
As três anomalias são anomalias de escrita: a de atualização acontece ao alterar, a de exclusão ao apagar, a de inserção ao inserir. Nenhuma delas se manifesta em leitura. Isso explica a aparente contradição, porque os dois ambientes têm padrões de uso opostos. Um banco transacional (OLTP) recebe escritas o tempo todo, vindas de muitas transações concorrentes, e é exatamente ali que as anomalias custam caro — por isso se normaliza. Um data warehouse (OLAP) é carregado em lote, por um processo de ETL controlado, e depois é só lido, por consultas que agregam milhões de linhas. Ali, a anomalia de atualização praticamente não existe, porque ninguém atualiza linha a linha; e o custo que domina é o das junções, que a normalização multiplica. Desnormalizar troca um problema que não se tem por um ganho que se tem. Há três condições que tornam a troca legítima, e vale enunciá-las porque é a ausência delas que transforma desnormalização em bagunça: a carga precisa ser controlada por um processo único e reproduzível, de modo que a consistência seja garantida na origem e não na tabela; o dado precisa ser histórico e imutável, para que não haja atualização a propagar; e é preciso existir a fonte normalizada da qual o warehouse é derivado, para que ele possa ser reconstruído se estiver errado. Um banco transacional desnormalizado "para evitar junção" não cumpre nenhuma das três, e por isso a comparação com o warehouse não o justifica.
Resposta
As três anomalias são de escrita, e o warehouse quase só lê: é carregado em lote por ETL e depois consultado. A troca é legítima porque há carga controlada, dado histórico imutável e uma fonte normalizada da qual ele deriva — condições que um OLTP desnormalizado não cumpre.
Aplicar cegamente a 3FN ao CEP separaria cidade e estado numa tabela própria. Em que situação manter os dados na tabela de cliente é a decisão certa?
Dica
Quem garante que a tabela de CEP está completa e correta?
Resolução comentada
A situação em que manter é correto tem a ver com a origem e a completude do dado. A dependência cep → cidade, estado só vale se o sistema tiver uma base de CEPs completa e mantida atualizada. Se ela não existe — e manter uma base de CEPs nacional exige atualização periódica junto aos Correios —, então extrair a tabela cria uma chave estrangeira que não consegue ser satisfeita: o cliente informa um CEP novo, que não está na base, e ou o cadastro é bloqueado por um dado que não é responsabilidade dele, ou a chave estrangeira precisa aceitar nulo, e aí a normalização não entregou a integridade que a justificava. Há um segundo argumento, de natureza histórica: o endereço registrado num cadastro pode precisar preservar o que foi informado na época, e faixas de CEP mudam. Nesse caso cidade e estado no cadastro não são derivados do CEP atual, e sim registro do que se declarou — o mesmo raciocínio do preço praticado na venda. Um terceiro argumento é operacional: se o sistema é local e atende uma cidade só, a tabela de CEP resolve um problema que não existe. A decisão madura costuma ser híbrida e vale a pena enunciá-la: manter cidade e estado na tabela de cliente como o que foi declarado, e usar a base de CEPs, quando houver, como serviço de preenchimento e validação na entrada, e não como chave estrangeira obrigatória. Assim se ganha a conveniência sem transformar a completude de uma base externa em pré-requisito para cadastrar cliente.
Resposta
Quando não há base de CEPs completa e mantida (a FK ficaria insatisfazível), quando o endereço precisa registrar o que foi declarado na época, ou quando o alcance é local. O caminho usual é manter os campos e usar a base de CEP como validação na entrada, não como FK.
Discuta: até que ponto regras de negócio devem ser declaradas como CHECK no banco, em vez de ficarem na aplicação?
Dica
Compare uma regra estável com uma que muda a cada trimestre.
Resolução comentada
O critério decisivo é a estabilidade da regra, e não sua natureza. Regras que decorrem do significado do dado — quantidade positiva, percentual entre 0 e 100, data de fim posterior à de início, situação dentro de um conjunto fechado — praticamente não mudam ao longo da vida do sistema, e declará-las como CHECK dá três ganhos: valem para todos os caminhos de escrita, inclusive importações e correções manuais; são verificadas dentro da transação, sem janela de concorrência; e documentam o domínio no próprio esquema, onde quem chega depois vai olhar. Regras voláteis são o caso oposto. Um CHECK que fixa o desconto máximo em 30% precisa de ALTER TABLE toda vez que a diretoria mudar a política, e ALTER TABLE em produção é evento planejado, não configuração — a regra estaria codificada no lugar mais caro de alterar. Pior: regras assim costumam ter exceções (desconto maior mediante aprovação), e um CHECK não sabe de aprovações. Essas pertencem à aplicação, ou a uma tabela de parâmetros. Há ainda um terceiro grupo que não cabe em CHECK por limitação técnica: regras que dependem de outras tabelas ou do estado anterior — "o total do pedido não pode exceder o limite de crédito do cliente" envolve consulta a outra tabela, algo que CHECK não faz de forma confiável em nenhum SGBD. Essas exigem gatilho ou lógica de aplicação dentro de transação. A síntese prática: declare no banco o que é invariante do dado, deixe na aplicação o que é política de negócio, e reconheça que regras entre tabelas são um terceiro caso, que nenhum dos dois resolve sozinho.
Resposta
Declare como CHECK o que é invariante do dado (quantidade positiva, percentual 0–100, domínio fechado): vale para todo caminho de escrita e documenta o esquema. Deixe na aplicação as políticas voláteis e as que admitem exceção. Regras que dependem de outras tabelas não cabem em CHECK e exigem gatilho ou transação.
Um sistema debita estoque com um SELECT para conferir a quantidade e depois um UPDATE para subtrair. Sob acesso simultâneo, o estoque fica negativo. Explique e corrija.
Dica
O que acontece entre o SELECT e o UPDATE?
Resolução comentada
O problema é uma condição de corrida clássica. Com uma unidade em estoque e duas transações simultâneas, ambas executam o SELECT e leem 1; ambas concluem que há estoque suficiente; ambas executam o UPDATE subtraindo 1; o estoque termina em -1. Nenhuma das duas fez nada errado isoladamente — o defeito está na janela entre a leitura e a escrita, durante a qual a informação lida deixou de ser verdadeira sem que a transação soubesse. Envolver os dois comandos numa transação não resolve sozinho: em nível de isolamento READ COMMITTED, que é o padrão da maioria dos SGBDs, a transação B ainda enxerga o valor confirmado antes de A escrever. Há três correções válidas. A primeira, e a mais robusta, é declarar a regra no esquema: CHECK (quantidade >= 0). Assim o segundo UPDATE falha com erro de restrição, e o estoque negativo torna-se impossível por construção, para qualquer caminho de escrita. A segunda é eliminar a janela fazendo a verificação dentro do próprio UPDATE: UPDATE estoque SET quantidade = quantidade - 1 WHERE produto_id = 10 AND quantidade >= 1 — a condição é avaliada no momento da escrita, com a linha bloqueada, e o comando afeta zero linhas quando não há estoque, o que a aplicação detecta pela contagem de linhas afetadas. A terceira é bloquear a linha na leitura com SELECT ... FOR UPDATE, forçando a segunda transação a esperar a primeira terminar; funciona, mas serializa o acesso e custa concorrência. A recomendação prática é combinar a primeira com a segunda: o CHECK como garantia final e o UPDATE condicional como caminho normal, verificando sempre quantas linhas foram afetadas antes de confirmar a venda.
Resposta
Condição de corrida: as duas transações leem 1 antes de qualquer escrita e ambas subtraem. Corrija com CHECK (quantidade >= 0) no esquema e UPDATE ... WHERE quantidade >= 1, conferindo as linhas afetadas — ou SELECT ... FOR UPDATE, ao custo de serializar.
Explique por que LIKE '%teclado%' costuma ser lento e o que fazer quando a busca por trecho é requisito.
Dica
Como um índice ordenado encontra uma palavra que pode estar no meio?
Resolução comentada
Um índice B-tree armazena os valores ordenados, e essa ordenação só ajuda quando se conhece o início do texto procurado. Com LIKE 'Teclado%', o SGBD desce a árvore até o primeiro valor que começa com "Teclado" e percorre a partir dali — trabalho proporcional ao número de acertos. Com LIKE '%teclado%', o trecho pode estar em qualquer posição, o prefixo é desconhecido e não há por onde começar a descida; resta ler todas as linhas e testar uma a uma, o que é varredura completa e cresce linearmente com o tamanho da tabela. Quando a busca por trecho é requisito, há três caminhos. O primeiro, e o mais indicado quando se busca palavras, é usar busca textual de verdade: índice de texto completo, que tokeniza o conteúdo em palavras e indexa cada uma, permitindo encontrar "teclado" onde quer que esteja, com tratamento de radicais e acentos. O segundo, para busca por trecho arbitrário e não por palavra, é um índice de trigramas, que indexa todas as sequências de três caracteres e transforma a busca por substring em busca indexada. O terceiro, quando o volume é pequeno ou a busca é rara, é aceitar a varredura — otimizar o que não dói é desperdício. O que não funciona é criar um índice B-tree comum na coluna e esperar que ele ajude: ele será simplesmente ignorado pelo otimizador, que sabe que não serve.
Resposta
Porque o curinga inicial impede usar a ordenação do índice B-tree, forçando varredura completa. Soluções: índice de texto completo para busca por palavra, índice de trigramas para trecho arbitrário, ou aceitar a varredura se o volume for pequeno.
Uma consulta que soma o total vendido por cliente passou a devolver valores inflados depois que alguém acrescentou uma junção com a tabela de telefones. Explique.
Dica
Quantas linhas passa a ter cada pedido se o cliente tem três telefones?
Resolução comentada
O problema é a multiplicação de linhas provocada por junções com relacionamentos de cardinalidade N em ramos independentes. Antes, cada item de pedido gerava uma linha, e a soma dos valores estava correta. Ao acrescentar a junção com telefone, cada linha existente passou a ser combinada com cada telefone daquele cliente: um cliente com três telefones faz cada item aparecer três vezes, e a soma triplica. O ponto importante é que não há erro de sintaxe nem de condição de junção — a junção com telefone está correta, casando cliente_id com cliente_id. O que aconteceu é que a consulta passou a percorrer dois caminhos N a partir do mesmo cliente (itens de um lado, telefones do outro), e a junção produz o produto cartesiano entre eles dentro de cada cliente. É uma armadilha especialmente perigosa porque o valor errado é plausível: ninguém desconfia de um faturamento maior. Há três saídas. A mais simples é não juntar: se os telefones não são usados na agregação, tirá-los da consulta e buscá-los separadamente. Se forem necessários na mesma saída, a segunda opção é agregar antes de juntar, calculando o total por cliente numa subconsulta e só então juntar com telefone — assim o total já está pronto e a multiplicação não o atinge. A terceira, quando se quer apenas um telefone, é reduzir o ramo a no máximo uma linha, escolhendo o telefone principal com uma subconsulta correlacionada. O diagnóstico geral: sempre que uma agregação envolver mais de um caminho N a partir da mesma entidade, o resultado está inflado até prova em contrário.
Resposta
Dois ramos N a partir do mesmo cliente (itens e telefones) geram produto cartesiano entre si: cada item se repete uma vez por telefone e a soma multiplica. Corrija removendo a junção desnecessária ou agregando numa subconsulta antes de juntar.
Um relatório de "faturamento por cliente" usa JOIN e mostra apenas clientes que compraram. A diretoria quer todos, com zero para quem não comprou. Escreva a consulta e explique os dois cuidados necessários.
Dica
São dois problemas diferentes: quem aparece e o que aparece na coluna.
Resolução comentada
A consulta é:
SELECT c.nome,
COUNT(p.id) AS pedidos,
COALESCE(SUM(p.total),0) AS faturado
FROM cliente c
LEFT JOIN pedido p ON p.cliente_id = c.id
GROUP BY c.id, c.nome
ORDER BY faturado DESC;O primeiro cuidado é a junção externa. Com INNER JOIN, o cliente sem pedido não encontra par e desaparece antes mesmo do agrupamento; o LEFT JOIN o preserva, com as colunas de pedido nulas. O segundo cuidado é o tratamento do nulo resultante. Para esse cliente, o grupo contém uma única linha, com p.id e p.total nulos: COUNT(p.id) devolve 0 corretamente, porque conta valores não nulos — e é por isso que se usa COUNT(p.id) e não COUNT(*), que devolveria 1 e diria que o cliente fez um pedido. Já SUM(p.total) devolve NULL, não zero, porque não há valor algum a somar; COALESCE converte isso no zero que a diretoria pediu. Vale notar que os dois cuidados são independentes e ambos silenciosos: esquecer o LEFT JOIN omite clientes sem aviso, e usar COUNT(*) inventa um pedido que não existe. Um terceiro cuidado, se houver mais junções, é o da aula anterior: nenhum outro ramo N pode entrar na consulta, sob pena de multiplicar as somas.
Resposta
LEFT JOIN para preservar quem não comprou; COUNT(p.id) — nunca COUNT(*), que devolveria 1 — e COALESCE(SUM(p.total), 0), porque SUM de conjunto vazio é NULL.
Discuta o uso de visões como camada de segurança comparado ao controle de acesso feito na aplicação.
Dica
Quem mais fala com o banco além da aplicação?
Resolução comentada
O argumento a favor da visão é o mesmo da integridade declarada no banco, discutido na aula 10: a aplicação não é o único caminho até o dado. Ferramentas de BI, scripts de extração, o cliente SQL do analista e o sistema seguinte que ninguém previu falam com o banco diretamente, e um controle implementado apenas na aplicação não os alcança. Com visão mais GRANT/REVOKE, a restrição vale para toda conexão, qualquer que seja a ferramenta, porque é o SGBD que a aplica. Há um segundo ganho: a regra fica declarada num lugar inspecionável — é possível auditar quem tem acesso a quê consultando o catálogo, o que não se consegue lendo código de aplicação espalhado. As limitações também são reais. Visões controlam bem o acesso por coluna e por linha estática, mas ficam desconfortáveis quando a regra depende do usuário conectado de forma dinâmica — "cada vendedor vê apenas seus clientes" exige funções de sessão dentro da visão, e a solução moderna para isso é segurança em nível de linha, não visão. Além disso, uma camada espessa de visões sobre visões dificulta a otimização e a depuração, e o número de objetos a manter cresce rápido. Também não substituem controle de acesso funcional: quem pode aprovar um pedido, quem pode cancelar uma venda, isso é regra de aplicação e não se expressa como privilégio de tabela. A conclusão prática é que não são alternativas concorrentes, e sim camadas complementares: o banco garante o que é acesso a dado, a aplicação garante o que é permissão de ação, e a primeira é a que continua valendo quando alguém abre um cliente SQL.
Resposta
Visão + GRANT vale para toda conexão, inclusive BI, scripts e acesso manual, e é auditável no catálogo — a aplicação só protege o próprio caminho. Mas não cobre regra dinâmica por usuário (caso de row-level security) nem permissão de ação. São camadas complementares.
Uma consulta que rodava em 200 ms passou a levar 40 segundos, sem que a consulta ou os índices mudassem. Liste hipóteses e como investigar cada uma.
Dica
O que muda num banco sem ninguém alterar código?
Resolução comentada
A primeira hipótese, e a mais provável, é a mudança de plano por estatísticas desatualizadas. O volume dos dados cresceu ou sua distribuição mudou, e o otimizador passou a decidir com informação velha — trocando, por exemplo, um Index Scan por um Seq Scan, ou invertendo a ordem de junção. Investiga-se com EXPLAIN ANALYZE, comparando as linhas estimadas com as reais: divergência grande confirma a hipótese, e a correção é atualizar as estatísticas com ANALYZE. A segunda é o crescimento puro do volume: a consulta sempre foi ineficiente, mas com poucos dados isso não aparecia. Compara-se a cardinalidade atual das tabelas com a de meses atrás e observa-se se o plano contém alguma operação cujo custo cresce mais que linearmente. A terceira é a degradação física: em bancos com controle de versão de linhas, atualizações e exclusões deixam versões mortas que continuam sendo lidas; o sintoma é uma tabela ocupando muito mais espaço do que o volume de dados justifica, e a correção é a manutenção de rotina (VACUUM ou equivalente) e a reconstrução de índices fragmentados. A quarta é contenção: a consulta não está lenta, está esperando — bloqueada por outra transação de longa duração. Investiga-se olhando as sessões ativas e os bloqueios no momento em que a lentidão ocorre, e o sinal característico é a lentidão ser intermitente em vez de constante. A quinta é ambiental: menos memória disponível para cache, disco mais lento, concorrência maior no servidor. Compara-se com métricas de sistema no período. O método para separar as hipóteses é começar sempre pelo EXPLAIN ANALYZE, porque ele distingue de imediato entre "o plano mudou" e "o plano é o mesmo e ficou lento" — e essa bifurcação elimina metade das hipóteses de uma vez.
Resposta
Hipóteses: estatísticas desatualizadas mudando o plano (a mais provável), crescimento de volume expondo ineficiência antiga, degradação física por versões mortas e índices fragmentados, contenção por bloqueio, e mudança ambiental. Comece por EXPLAIN ANALYZE: ele separa "o plano mudou" de "o mesmo plano ficou lento".
Resumo Geral
O que cada aula deixou como essencial, reunido.
- redundância
- inconsistência
- dependência programa-dados
- concorrência
- SGBD
- esquema
- instância
- independência de dados
- DDL
- DML
- ANSI/SPARC
- nível interno
- nível conceitual
- nível externo
- DBA
- catálogo
- modelo E-R
- minimundo
- entidade
- atributo
- relacionamento
- projeto conceitual
- atributo composto
- multivalorado
- derivado
- identificador
- entidade fraca
- autorrelacionamento
- cardinalidade
- 1:N
- N:N
- participação total
- atributo de relacionamento
- generalização
- especialização
- cobertura total
- disjunção exclusiva
- agregação
- herança
- brModelo
- modelo lógico
- engenharia reversa
- geração de DDL
- sincronização
- migrations
- relação
- tupla
- domínio
- grau
- atomicidade
- multiconjunto
- chave candidata
- chave primária
- chave estrangeira
- integridade referencial
- CASCADE
- RESTRICT
- regras de transformação
- tabela associativa
- chave composta
- dependência funcional
- 1FN
- grupo repetitivo
- anomalia de atualização
- desnormalização
- 2FN
- 3FN
- dependência parcial
- dependência transitiva
- decomposição
- CREATE TABLE
- ALTER TABLE
- CHECK
- NUMERIC
- CONSTRAINT
- INSERT
- UPDATE
- DELETE
- transação
- ACID
- ROLLBACK
- SELECT
- projeção
- seleção
- NULL
- IS NULL
- DISTINCT
- ORDER BY
- INNER JOIN
- LEFT JOIN
- ON
- produto cartesiano
- autojunção
- alias
- GROUP BY
- HAVING
- COUNT
- SUM
- AVG
- COALESCE
- CREATE VIEW
- visão atualizável
- DCL
- GRANT
- REVOKE
- papel
- álgebra relacional
- junção
- equivalência
- otimizador
- EXPLAIN
- Antes do SGBD, cada programa definia e mantinha seus próprios arquivos de dados.
- Os quatro problemas dessa abordagem: redundância descontrolada, inconsistência, dependência programa-dados e dificuldade de acesso concorrente.
- Redundância controlada é decisão de projeto; descontrolada é a que ninguém sabe que existe.
- Banco de dados é a coleção de dados; SGBD é o software que a gerencia.
- Esquema é a estrutura (muda raramente); instância é o conteúdo (muda sempre).
- Independência física isola mudanças de armazenamento; independência lógica isola mudanças de estrutura.
- As vantagens do SGBD: controle de redundância, de acesso, de concorrência e recuperação após falha.
- A arquitetura ANSI/SPARC separa nível interno, conceitual e externo.
- Os mapeamentos entre níveis são o que torna a independência de dados possível.
- O catálogo (dicionário de dados) descreve o próprio banco e é consultável como qualquer tabela.
- Papéis: administrador de dados, DBA, projetista, programador e usuário final.
- Modelagem conceitual descreve o minimundo sem compromisso com tecnologia.
- O modelo E-R (Chen, 1976) tem três elementos: entidade, atributo e relacionamento.
- O conceitual precede o lógico (tabelas) e o físico (armazenamento).
- O modelo conceitual é o artefato validável por quem entende do negócio.
- Atributos: simples/composto, monovalorado/multivalorado, armazenado/derivado, identificador/descritivo.
- O identificador distingue univocamente cada ocorrência; pode ser composto.
- Grau do relacionamento: binário, ternário, n-ário; autorrelacionamento exige papéis nomeados.
- Entidade fraca não tem identificador próprio: usa identificador parcial + chave da proprietária.
- Cardinalidade máxima gera 1:1, 1:N e N:N e determina como o relacionamento vira tabela.
- Cardinalidade mínima (0 ou 1) determina se a chave estrangeira aceita NULL.
- Participação total = obrigatória; parcial = opcional.
- Atributo que só existe quando as duas pontas existem é atributo do relacionamento.
- Generalização reúne o comum; especialização define os subconjuntos.
- Cobertura (total/parcial) e disjunção (exclusiva/compartilhada) são dimensões independentes.
- Agregação permite que um relacionamento participe de outro relacionamento.
- Há três estratégias de tradução de hierarquia para tabelas, cada uma com um custo diferente.
- A ferramenta converte conceitual → lógico → DDL, e o SGBD só é escolhido no último passo.
- Ela valida a estrutura do modelo, não a correspondência com o negócio.
- Engenharia reversa reconstrói o lógico, nunca o conceitual — a conversão perde informação.
- Sem sincronização, o modelo vira documentação falsa.
- Relação é conjunto de tuplas: sem duplicatas, sem ordem de linhas, sem ordem de colunas.
- Grau = número de atributos; cardinalidade = número de tuplas.
- Todo valor é atômico — daí não existir atributo multivalorado nem composto.
- A tabela SQL é multiconjunto, e por isso a chave primária é o que a aproxima de uma relação.
- Superchave identifica; candidata é superchave mínima; primária é a candidata escolhida.
- Integridade de entidade: chave primária não admite nulo.
- Integridade referencial: chave estrangeira aponta para tupla existente, ou é nula.
- As ações referenciais (CASCADE, RESTRICT, SET NULL) são decisão de negócio.
- Entidade vira relação; identificador vira chave primária.
- 1:N gera chave estrangeira no lado N; N:N gera tabela associativa obrigatoriamente.
- Atributo multivalorado e entidade fraca viram relações próprias.
- No 1:1, a chave estrangeira vai no lado de participação total.
- X → Y significa que X determina Y; a dependência pode ser parcial ou transitiva.
- As três anomalias (atualização, exclusão, inserção) são todas de escrita.
- 1FN exige valores atômicos e proíbe grupos repetitivos.
- Colunas numeradas são grupo repetitivo, mesmo sendo atômicas uma a uma.
- 2FN: sem dependência parcial — só verificável em chave composta.
- 3FN: sem dependência transitiva entre atributos não-chave.
- Decompor é extrair o determinante com o que ele determina, deixando-o como chave estrangeira.
- "Da chave, da chave inteira e de nada além da chave."
- CREATE TABLE declara colunas, tipos e restrições; ALTER TABLE altera; DROP TABLE remove.
- Nomear restrições com CONSTRAINT faz a mensagem de erro do SGBD ser legível.
- NUMERIC para dinheiro; FLOAT nunca.
- Acrescentar coluna NOT NULL a tabela com dados exige três passos.
- INSERT, UPDATE e DELETE alteram dados; sem WHERE, os dois últimos atingem a tabela inteira.
- Sempre nomeie as colunas no INSERT.
- Transação é a unidade atômica: COMMIT confirma tudo, ROLLBACK desfaz tudo.
- ACID: Atomicidade, Consistência, Isolamento, Durabilidade.
- SELECT projeta colunas, WHERE seleciona linhas, ORDER BY ordena.
- NULL é desconhecido: use IS NULL, nunca = NULL.
- Desigualdade sobre coluna que admite nulo descarta os nulos silenciosamente.
- LIKE com curinga no início impede o uso de índice.
- INNER JOIN traz só o que casa; LEFT JOIN preserva a tabela da esquerda.
- LEFT JOIN + IS NULL é o padrão para encontrar registros órfãos.
- Numa junção externa, condição sobre a tabela opcional vai no ON, não no WHERE.
- Junção sem condição gera produto cartesiano.
- COUNT, SUM, AVG, MIN e MAX resumem conjuntos; GROUP BY define os grupos.
- Ordem lógica: FROM → WHERE → GROUP BY → agregação → HAVING → SELECT → ORDER BY.
- WHERE filtra linhas; HAVING filtra grupos.
- Agregações ignoram NULL; COUNT(*) é a exceção, e SUM de vazio é NULL.
- Visão é consulta nomeada; guarda definição, não dados. É o nível externo do ANSI/SPARC.
- Serve para simplificar, proteger e preservar independência lógica.
- Só é atualizável quando cada linha mapeia para uma linha de uma única tabela base.
- GRANT concede e REVOKE retira privilégios; papéis evitam administrar usuário a usuário.
- Operadores fundamentais: σ, π, ×, ∪, − e ρ; ⋈ e ∩ são derivados.
- π elimina duplicatas porque relação é conjunto; SELECT não, porque tabela é multiconjunto.
- Antecipar seleções e projeções é a principal equivalência de otimização.
- O otimizador escolhe o plano com base em estatísticas; EXPLAIN mostra o que ele decidiu.
Bibliografia
Como consta no Plano de Ensino oficial da disciplina.
Básica
- ELMASRI, R.; NAVATHE, S. B. Sistemas de Bancos de Dados 4. ed. São Paulo: Pearson Addison, 2005.
- KORTH, Henry F.; SILBERSCHATZ, Abraham Sistema de Banco de Dados São Paulo: Makron Books, 2006.
- HEUSER, C. A. Projeto de Banco de Dados 6. ed. Porto Alegre: Bookman, 2009.
Complementar
- DATE, C. J. Introdução a Sistemas de Bancos de Dados Rio de Janeiro: Campus, 2000.
- GROFF, J. R.; WEINBERG, P. N. SQL: The Complete Reference 2. ed. New York: McGraw-Hill, 2002.
- OLIVEIRA, C. H. C. SQL: Curso Prático São Paulo: Novatec, 2002.
- ULLMAN, J. D.; WIDOM, J. A First Course in Data Base Systems São Paulo: Prentice Hall, 1997.
- WATSON, R. T. Data Management: Banco de Dados e Organizações 3. ed. Rio de Janeiro: LTC, 2004.
Anotações
Salvas automaticamente e independentes por disciplina.
Carregando anotações…