SQL e normalização
Do zero
Imagine uma planilha de contratos de TI em que cada linha repete o nome e o telefone do fornecedor. A Alfa Tecnologia tem 30 contratos, então o telefone dela aparece 30 vezes. No dia em que o telefone muda, alguém precisa alterar 30 linhas; se esquecer uma, o banco passa a ter dois telefones para a mesma empresa. Esse tipo de problema se chama anomalia.
Normalizar é arrumar essa planilha em várias tabelas menores, cada fato guardado num lugar só: os fornecedores numa tabela, os contratos em outra. As tabelas se ligam por chaves, como um número de matrícula que aponta para a ficha certa.
SQL é a língua para conversar com o banco: criar as tabelas, guardar dados e fazer perguntas como “quais contratos de 2023 são da Alfa?”. Álgebra relacional é a matemática por trás dessas perguntas, com um símbolo para cada operação.
Frase para guardar: cada fato num lugar só, ligado por chaves; SQL pergunta, a álgebra relacional explica a pergunta.
Em 1 minuto
- Chave primária identifica cada linha e não se repete. Chave estrangeira aponta para a chave primária de outra tabela e pode se repetir (vários contratos do mesmo fornecedor).
- 1FN: valores atômicos, sem listas numa célula. 2FN: nenhum atributo depende de só uma parte da chave composta. 3FN: nenhum atributo não chave depende de outro atributo não chave (dependência transitiva).
- Desnormalizar é repetir dados de propósito para ler mais rápido (relatórios), aceitando atualizar em mais lugares.
- SQL: WHERE filtra linhas antes de agrupar; HAVING filtra grupos depois do GROUP BY. Nulo se testa com IS NULL.
- Álgebra relacional: seleção (σ) escolhe linhas; projeção (π) escolhe colunas; junção (⋈) combina tabelas.
Exemplo concreto: a planilha de contratos, normalizada
Tabela original, com tudo junto:
| contrato | objeto | valor | cnpj | fornecedor | telefones |
|---|---|---|---|---|---|
| 2023/015 | Suporte ao datacenter | 480.000,00 | 12.345.678/0001-90 | Alfa Tecnologia | 62 3333-0000, 62 99999-0000 |
| 2024/002 | Licenças de backup | 95.000,00 | 12.345.678/0001-90 | Alfa Tecnologia | 62 3333-0000, 62 99999-0000 |
Os problemas e a correção, forma por forma:
- 1FN: “telefones” guarda uma lista numa célula. Solução: uma tabela de telefones, um por linha.
- 3FN: “fornecedor” depende do CNPJ, que não é a chave da tabela de contratos (dependência transitiva: contrato → CNPJ → nome). Solução: tabela de fornecedores, com o CNPJ como chave primária.
- Resultado: fornecedor (cnpj, nome), telefone (cnpj, numero) e contrato (numero, objeto, valor, cnpj). O nome da Alfa fica num lugar só.
A 2FN só aparece com chave composta. Na tabela item_contrato (num_contrato, cod_item, descricao_item, quantidade), a chave é o par (num_contrato, cod_item). A quantidade depende do par, mas a descrição depende só do item: é uma dependência parcial, que viola a 2FN. A descrição vai para uma tabela item (cod_item, descricao).
Modelo entidade-relacionamento
Antes das tabelas vem o desenho conceitual, o modelo entidade-relacionamento (MER), proposto por Peter Chen em 1976:
- Entidade: algo do mundo real sobre o qual se guardam dados (Fornecedor, Contrato, Servidor). No diagrama, um retângulo.
- Atributo: uma característica da entidade (cnpj, nome). O atributo identificador vira a chave primária.
- Relacionamento: a ligação entre entidades (Fornecedor fornece Contrato). No diagrama, um losango.
- Cardinalidade: quantos de cada lado participam: 1:1, 1:N ou N:N.
| Cardinalidade | Exemplo | Vira, nas tabelas |
|---|---|---|
| 1:N | Um fornecedor tem vários contratos | Chave estrangeira no lado N (cnpj em contrato) |
| N:N | Um servidor atua em vários projetos, e um projeto tem vários servidores | Tabela associativa servidor_projeto (matricula, id_projeto) |
| 1:1 | Um servidor tem um crachá | Chave estrangeira num dos lados, em geral o opcional |
O mesmo exemplo em SQL
CREATE TABLE fornecedor (
cnpj CHAR(14) PRIMARY KEY,
nome VARCHAR(100) NOT NULL
);
CREATE TABLE contrato (
numero VARCHAR(10) PRIMARY KEY,
objeto VARCHAR(200) NOT NULL,
valor DECIMAL(12,2),
cnpj CHAR(14) NOT NULL,
FOREIGN KEY (cnpj) REFERENCES fornecedor (cnpj)
);
INSERT INTO fornecedor (cnpj, nome) VALUES ('12345678000190', 'Alfa Tecnologia');
INSERT INTO contrato (numero, objeto, valor, cnpj)
VALUES ('2023/015', 'Suporte ao datacenter', 480000.00, '12345678000190');
-- Contratos de 2023, com o nome do fornecedor
SELECT c.numero, c.objeto, f.nome
FROM contrato c
JOIN fornecedor f ON f.cnpj = c.cnpj
WHERE c.numero LIKE '%2023%'
ORDER BY c.numero;
-- Fornecedores com mais de dois contratos
SELECT f.nome, COUNT(*) AS contratos, SUM(c.valor) AS total
FROM contrato c
JOIN fornecedor f ON f.cnpj = c.cnpj
GROUP BY f.cnpj, f.nome
HAVING COUNT(*) > 2;
O banco executa o SELECT nesta ordem lógica: FROM (e os JOINs) → WHERE → GROUP BY → HAVING → SELECT → ORDER BY. Por isso o WHERE não enxerga os grupos e não aceita COUNT; o HAVING aceita.
Comparação: os comandos e os tipos de junção
| Grupo | Comandos | Para quê |
|---|---|---|
| DDL (definição) | CREATE, ALTER, DROP, TRUNCATE | Criar e mudar a estrutura |
| DML (manipulação) | SELECT, INSERT, UPDATE, DELETE | Ler e mudar os dados |
| DCL (controle) | GRANT, REVOKE | Dar e tirar permissões |
| DTL ou TCL (transação) | COMMIT, ROLLBACK, SAVEPOINT | Confirmar ou desfazer |
| Junção | Devolve | Exemplo |
|---|---|---|
| INNER JOIN | Só as linhas que casam nas duas tabelas | Contratos que têm fiscal |
| LEFT JOIN | Todas da esquerda; da direita, o que casar (o resto vem nulo) | Todos os contratos, com o fiscal quando houver |
| RIGHT JOIN | O espelho do LEFT | Todos os servidores, com o contrato quando houver |
| FULL JOIN | Todas das duas, casando quando possível | Conciliação de duas listas |
| CROSS JOIN | Todas as combinações (produto cartesiano) | 5 contratos × 3 servidores = 15 linhas |
Álgebra relacional: o símbolo de cada pergunta
| Operação | Símbolo | Em SQL |
|---|---|---|
| Seleção (escolhe linhas) | σ | WHERE |
| Projeção (escolhe colunas) | π | lista de colunas do SELECT |
| Junção | ⋈ | JOIN … ON |
| Produto cartesiano | × | CROSS JOIN |
| União, interseção, diferença | ∪, ∩, − | UNION, INTERSECT, EXCEPT |
| Renomeação | ρ | AS |
Exemplo: “números dos contratos acima de R$ 100 mil” se escreve π numero (σ valor > 100000 (contrato)). Primeiro a seleção filtra as linhas; depois a projeção fica só com a coluna.
Como a FCC cobra
Identificar qual forma normal uma tabela viola, e prever o resultado de uma consulta SQL sobre uma tabela pequena. SQL também já caiu em estudo de caso da FCC: criar tabela com chave primária, inserir registros e consultar com LIKE.
- Forma normal violada a partir das dependências descritas no enunciado.
- WHERE × HAVING, GROUP BY e funções de agregação (COUNT, SUM, AVG).
- Resultado de INNER × LEFT JOIN com valores nulos.
- Classificação dos comandos em DDL, DML, DCL e DTL.
- Correspondência entre operadores da álgebra relacional e SQL.
- Cardinalidade no modelo entidade-relacionamento (N:N vira tabela associativa).
Pegadinhas
- “A projeção (π) filtra as linhas.” Falso: quem filtra linhas é a seleção (σ); a projeção escolhe colunas. O SELECT do SQL corresponde à projeção.
- “WHERE COUNT(*) > 2” funciona. Falso: agregação no filtro vai no HAVING.
- “WHERE email = NULL” encontra os nulos. Falso: use “IS NULL”; comparar com NULL nunca dá verdadeiro.
- “COUNT(email) conta todas as linhas.” Falso: ignora os nulos; COUNT(*) conta todas.
- “A chave estrangeira não pode se repetir.” Falso: vários contratos podem apontar para o mesmo fornecedor.
- “Desnormalizar é erro de projeto.” Falso: é escolha consciente para acelerar leituras, com custo nas atualizações.
Explique com suas palavras
Explique a alguém que só usa planilhas por que vale a pena separar fornecedores e contratos em duas tabelas, e como juntar os dados de novo numa consulta.
- Escreva sem olhar a lição, com frases curtas e um exemplo seu.
- Onde travar ou precisar de jargão, há uma lacuna: releia só essa parte.
- Compare com a explicação-modelo e reescreva o que faltou.
Fica salva neste navegador.
Comparar com uma explicação-modelo
Se o nome e o telefone do fornecedor ficam em cada linha de contrato, a mesma informação se repete muitas vezes. Quando ela muda, é preciso corrigir todas as linhas, e basta esquecer uma para o banco ficar contraditório. Separando, o fornecedor fica numa tabela só, identificado pelo CNPJ (a chave primária). Cada contrato guarda apenas o CNPJ do fornecedor (a chave estrangeira).
Para ver tudo junto, a consulta usa um JOIN: “junte cada contrato com o fornecedor que tem o mesmo CNPJ”. O resultado parece a planilha original, mas o dado continua guardado num lugar só.
Teste-se
Banco de Dados · SQL e normalização · Inédita, estilo FCC
Questão 1 de 3
Considere a tabela ITEM_CONTRATO (num_contrato, cod_item, descricao_item, quantidade), cuja chave primária é composta por num_contrato e cod_item. Sabe-se que descricao_item depende apenas de cod_item e que quantidade depende da chave inteira. Essa tabela
A–E escolhem, Enter confirma. Botão direito ou Shift + letra risca.
Toque longo numa alternativa para riscá-la.
Banco de Dados · SQL e normalização · Inédita, estilo FCC
Questão 2 de 3
Para listar os CNPJs dos fornecedores que têm mais de dois contratos, um analista escreveu: SELECT cnpj, COUNT(*) FROM contrato ________ cnpj ________ COUNT(*) > 2; Preenchem corretamente as lacunas, na ordem:
A–E escolhem, Enter confirma. Botão direito ou Shift + letra risca.
Toque longo numa alternativa para riscá-la.
Banco de Dados · SQL e normalização · Inédita, estilo FCC
Questão 3 de 3
A tabela SERVIDOR tem 5 linhas. Em 2 delas, a coluna email está nula. O comando SELECT COUNT(*), COUNT(email) FROM servidor; retorna, respectivamente,
A–E escolhem, Enter confirma. Botão direito ou Shift + letra risca.
Toque longo numa alternativa para riscá-la.
Mais duas questões deste tópico estão em Questões.
Flashcards gerados
| Frente | Verso |
|---|---|
| 1FN, 2FN e 3FN em uma frase cada | 1FN, valores atômicos; 2FN, nada depende de parte da chave; 3FN, nada depende de atributo que não é chave. Exemplo: Nome do fornecedor dentro da tabela de contratos depende do CNPJ, não do contrato, e viola a 3FN. |
| Chave primária × chave estrangeira | A primária identifica cada linha e não se repete; a estrangeira aponta para a primária de outra tabela e pode se repetir. Exemplo: Vários contratos com o mesmo CNPJ de fornecedor. |
| Ordem lógica de execução do SELECT | FROM e JOIN, WHERE, GROUP BY, HAVING, SELECT e ORDER BY. Exemplo: Por isso o WHERE não aceita COUNT; o HAVING aceita. |
| Como testar valor nulo em SQL? | Com IS NULL ou IS NOT NULL; comparar com “= NULL” nunca dá verdadeiro. |
| Relacionamento N:N no modelo físico | Vira uma tabela associativa com as chaves das duas entidades. Exemplo: Servidor e projeto, N:N, viram a tabela servidor_projeto (matricula, id_projeto). |
Ligação com o estudo de caso
Um caso provável: “modele as tabelas de contratos de TI do TCE-GO e escreva o SQL para criar, inserir e consultar”. Responda com o CREATE TABLE completo (chave primária, chave estrangeira e NOT NULL), dois INSERT e um SELECT com JOIN e WHERE. Escreva à mão no treino: na prova não há como testar a consulta.
Fontes da lição
- Edital nº 01/2026, Anexo II, Banco de Dados (fontes/edital-01-2026.txt).
- E. F. Codd, “A Relational Model of Data for Large Shared Data Banks”, Communications of the ACM, 1970 (modelo relacional e álgebra relacional).
- Ramez Elmasri e Shamkant Navathe, Sistemas de banco de dados (formas normais, SQL e álgebra relacional).
Claude explica
Tire dúvidas sobre este tema no Claude: o site copia o pedido com a lição e abre uma conversa nova, na sua conta. Confira na fonte qualquer fato de lei ou de norma.
Pratique fora do site
Faça questões da FCC sobre este tópico no QConcursos ou no Gran e registre aqui quantas fez: elas contam na meta da semana e no status do tópico.