Noções Básicas de Normalização
7 exemplos para você começar com Normalização - 5 básicos e 2 intermediários.
Pré-requisitos
- PostgreSQL 18.4 com um banco de dados de rascunho para experimentos de DDL.
- Familiaridade com
CREATE TABLE,PRIMARY KEYeFOREIGN KEY.
Exemplos Básicos
1. Anomalia de Atualização em Uma Tabela Ampla
Dados duplicados forçam atualizações de várias linhas quando um fato muda.
CREATE TABLE order_lines_bad (
order_id integer,
customer_email text,
customer_city text,
product_sku text,
product_name text,
quantity integer,
PRIMARY KEY (order_id, product_sku)
);
-- Cliente muda de cidade: deve atualizar todas as linhas históricas
UPDATE order_lines_bad SET customer_city = 'Portland' WHERE customer_email = 'a@example.com';- Armazenar
customer_cityem cada linha cria uma anomalia de atualização. - Esquecer uma linha deixa valores de cidade contraditórios para o mesmo e-mail.
- A normalização divide fatos estáveis em suas próprias tabelas.
Relacionado: 3NF & BCNF - formas normais formais
2. Decompor Cliente de Linhas de Pedido
Mova atributos repetidos do cliente para uma tabela pai.
CREATE TABLE customers (
customer_id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
email text NOT NULL UNIQUE,
city text NOT NULL
);
CREATE TABLE orders (
order_id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
customer_id bigint NOT NULL REFERENCES customers (customer_id)
);
CREATE TABLE order_items (
order_id bigint NOT NULL REFERENCES orders (order_id),
product_id bigint NOT NULL,
quantity integer NOT NULL CHECK (quantity > 0),
PRIMARY KEY (order_id, product_id)
);- A cidade do cliente vive em uma linha - atualize uma vez.
- Pedidos referenciam clientes por FK - anomalia de inserção evitada com linha pai explícita.
- Itens de linha referenciam pedidos - regras de exclusão controladas por
ON DELETE.
Relacionado: Modelagem de Entidade-Relacionamento - posicionamento de FK
3. Anomalia de Inserção Sem Tabela de Produto
Você não pode adicionar um produto do catálogo até que alguém o peça em um design desnormalizado.
CREATE TABLE products (
product_id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
sku text NOT NULL UNIQUE,
name text NOT NULL
);
-- A equipe de catálogo pode INSERIR produtos antes que qualquer pedido exista
INSERT INTO products (sku, name) VALUES ('WIDGET-1', 'Widget');- A dimensão de produto normalizada evita anomalias de inserção.
order_itemsreferenciaproductsquando uma venda acontece.UNIQUEemskuimpõe a identidade do catálogo.
Relacionado: Desnormalização Intencional - quando quebrar regras deliberadamente
4. Anomalia de Exclusão Preserva o Catálogo
Excluir o último pedido de um produto não deve excluir a definição do produto.
CREATE TABLE order_items (
order_id bigint NOT NULL REFERENCES orders (order_id) ON DELETE CASCADE,
product_id bigint NOT NULL REFERENCES products (product_id) ON DELETE RESTRICT,
quantity integer NOT NULL,
PRIMARY KEY (order_id, product_id)
);ON DELETE RESTRICTemproduct_idimpede a remoção de produtos ainda referenciados.ON DELETE CASCADEemorder_idremove itens de linha quando um pedido é cancelado.- Separar entidades elimina anomalias de exclusão em dados de referência compartilhados.
5. Dependência Funcional Orienta o Posicionamento da Coluna
Se sku -> product_name, product_name pertence com sku, não em cada linha de fato.
CREATE TABLE products (
product_id bigint PRIMARY KEY,
sku text NOT NULL UNIQUE,
name text NOT NULL
);
-- name é determinado por sku (e product_id), não por order_id- Uma dependência funcional
A -> Bsignifica que B pertence à mesma tabela que A. - Tabelas de fatos carregam IDs e medidas (
quantity), não duplicatas descritivas. - Documente as dependências em comentários de migração para revisores futuros.
Exemplos Intermediários
6. Dependência Transitiva (Rumo à 3NF)
A cidade determinada pelo código postal não deve ficar no cliente se o CEP determina a cidade.
CREATE TABLE zip_codes (
zip text PRIMARY KEY,
city text NOT NULL,
state text NOT NULL
);
CREATE TABLE customers (
customer_id bigint PRIMARY KEY,
email text NOT NULL UNIQUE,
zip text NOT NULL REFERENCES zip_codes (zip)
);zip -> cityé transitiva quando ambos estavam emcustomerscom apenascustomer_idcomo chave.- A tabela de referência
zip_codescontém atributos de localização uma vez. - O custo de junção é a troca; indexe
zipemcustomers.
Relacionado: 3NF & BCNF - dependências transitivas
7. Detectar Duplicação com uma Consulta
Encontre duplicatas contraditórias antes que cheguem aos relatórios de produção.
SELECT customer_email, COUNT(DISTINCT customer_city) AS city_variants
FROM order_lines_bad
GROUP BY customer_email
HAVING COUNT(DISTINCT customer_city) > 1;- Problemas de normalização aparecem como múltiplos valores distintos para o mesmo determinante.
- Execute após importações de ETL e antes de desnormalizar para desempenho.
- Zero linhas é o objetivo; linhas não zero acionam trabalho de decomposição.
Relacionado: Melhores Práticas de Desnormalização - regras seguras de desnormalização
Versões da Pilha: Esta página foi escrita para PostgreSQL 18.4 (estável 18, manutenção 17), pgvector 0.8+, PgBouncer 1.x, Patroni 3.x e PostGIS 3.5+.