Do Diagrama ER ao esquema lógico: mapeamento para tabelas, colunas e constraints

Capítulo 12

Tempo estimado de leitura: 8 minutos

+ Exercício

O que significa “sair do ER” e chegar no esquema lógico

O diagrama ER (conceitual) descreve significado: entidades, atributos e relacionamentos. O esquema lógico descreve implementação relacional: tabelas, colunas, chaves e constraints. O mapeamento é um conjunto de regras de transformação para preservar o sentido do modelo e, ao mesmo tempo, produzir uma estrutura executável em um SGBD relacional.

Ao transformar, você toma decisões que o ER não detalha totalmente, como: nomes físicos, tipos de dados, tamanhos, constraints (NOT NULL, UNIQUE, CHECK), ações de integridade (ON DELETE/UPDATE) e estratégias para casos como 1:1, multivalorados e especialização/generalização.

Regras de transformação (ER → Relacional)

1) Entidade → Tabela

Para cada entidade forte, crie uma tabela. A chave primária do ER vira PRIMARY KEY na tabela. Atributos simples viram colunas.

  • Nome físico: prefira singular e sem espaços (ex.: cliente, pedido).
  • Chave primária: mantenha a mesma semântica do ER. Se for substituta (ex.: id), padronize (ex.: cliente_id).

2) Atributo → Coluna

Cada atributo simples vira uma coluna com tipo, obrigatoriedade e, quando aplicável, unicidade e validações. Atributos compostos normalmente são “achatados” em colunas (ex.: enderecologradouro, numero, cidade).

  • Obrigatório (participação total no atributo): NOT NULL.
  • Valor único (candidato a chave): UNIQUE.
  • Domínio restrito: CHECK (ex.: status em conjunto fechado).

3) Relacionamento 1:N → Chave estrangeira no lado N

Em um relacionamento 1:N, a tabela do lado N recebe a chave estrangeira apontando para a tabela do lado 1.

Continue em nosso aplicativo e ...
  • Ouça o áudio com a tela desligada
  • Ganhe Certificado após a conclusão
  • + de 5000 cursos para você explorar!
ou continue lendo abaixo...
Download App

Baixar o aplicativo

Passo a passo:

  • Identifique o lado 1 (pai) e o lado N (filho).
  • Copie a PK do pai para o filho como coluna FK.
  • Defina FOREIGN KEY e escolha ações (ON DELETE/ON UPDATE).
  • Se a participação do lado N for obrigatória, marque a FK como NOT NULL.

Exemplo: Cliente (1) — (N) Pedido → pedido.cliente_id referencia cliente.cliente_id.

4) Relacionamento N:N → Tabela associativa

Relacionamentos N:N não são representáveis diretamente com uma única FK. Crie uma tabela associativa (também chamada de tabela de junção) contendo as FKs para as duas entidades.

Passo a passo:

  • Crie uma tabela com duas colunas FK (uma para cada lado).
  • Defina a PK como composta ((fk_a, fk_b)) ou use uma PK substituta e imponha UNIQUE(fk_a, fk_b).
  • Se o relacionamento tiver atributos próprios (ex.: quantidade, preço negociado), eles ficam na tabela associativa.

Exemplo: Pedido (N) — (N) Produto → item_pedido com pedido_id, produto_id, quantidade, preco_unitario.

5) Relacionamento 1:1 → Estratégias alternativas

Relacionamentos 1:1 exigem escolha de estratégia. As mais comuns:

  • FK em uma das tabelas + UNIQUE: coloque a FK em uma das tabelas e aplique UNIQUE na FK para garantir 1:1. Útil quando uma entidade depende da outra ou quando há opcionalidade em apenas um lado.
  • Tabela única: mescle as duas entidades em uma tabela só (bom quando o 1:1 é total-total e não há muitos campos opcionais).
  • Tabela separada com PK=FK: a tabela dependente usa a mesma chave do “dono” como PK e FK ao mesmo tempo. Excelente para “extensões” (ex.: dados raros/sensíveis).

Como decidir:

  • Se uma parte é opcional e a outra é obrigatória, prefira FK no lado opcional.
  • Se há muitos campos opcionais e raramente usados, prefira tabela separada (PK=FK).
  • Se a separação existe só por organização e sempre existe, tabela única pode simplificar.

6) Atributos multivalorados → Tabelas separadas

Um atributo multivalorado (ex.: cliente tem vários telefones) vira uma tabela própria. Essa tabela contém a FK para a entidade “dona” e a coluna do valor (e possivelmente tipo/ordem).

Passo a passo:

  • Crie tabela cliente_telefone.
  • Inclua cliente_id como FK.
  • Inclua telefone (e opcionalmente tipo).
  • Defina PK composta (cliente_id, telefone) ou PK substituta + UNIQUE.

7) Especialização/Generalização (quando aplicável)

Quando há uma superentidade e subentidades (ex.: Pessoa → Pessoa Física / Pessoa Jurídica), existem três estratégias clássicas no relacional:

  • Tabela por hierarquia (single table): uma tabela com todas as colunas e um discriminador (ex.: tipo_pessoa). Simples, mas pode gerar muitos NULLs.
  • Tabela por tipo (class table inheritance): uma tabela para a superentidade e uma para cada subentidade, onde a PK da subentidade também é FK para a superentidade (PK=FK). Preserva bem o modelo.
  • Tabela por classe concreta: uma tabela para cada subentidade contendo também os atributos comuns. Evita joins, mas duplica colunas comuns.

Regras práticas:

  • Se a especialização é grande e com muitos atributos específicos, use “tabela por tipo”.
  • Se é pequena e o custo de joins é indesejado, “single table” pode ser aceitável com CHECK bem definido.

Escolha de tipos de dados: critérios práticos

Identificadores (PK/FK)

  • Inteiros (INT/BIGINT): comuns para chaves substitutas. Use BIGINT se o volume pode ser muito alto.
  • UUID (UUID ou CHAR(36)): útil para geração distribuída; maior custo de armazenamento/índice.

Textos

  • VARCHAR(n) para textos curtos com limite (nome, email).
  • TEXT para descrições longas (quando o SGBD suportar).
  • Defina tamanho com base em regra de negócio e dados reais; evite “255 por padrão” sem motivo.

Números e dinheiro

  • DECIMAL(p,s) para valores monetários (ex.: DECIMAL(10,2)).
  • INT para contagens/quantidades inteiras.

Datas e horários

  • DATE para datas sem horário.
  • TIMESTAMP/DATETIME para eventos com horário.

Booleanos e enums

  • BOOLEAN quando o SGBD suportar.
  • Para status com conjunto fechado, prefira VARCHAR + CHECK (portável) ou tipo enum nativo (dependente do SGBD).

Constraints: como transformar regras em garantias no banco

NOT NULL

Use quando o atributo é obrigatório. Em relacionamentos 1:N, a FK no lado N deve ser NOT NULL se o registro não pode existir sem o pai.

UNIQUE

Use para garantir unicidade de atributos candidatos (ex.: email) e para garantir 1:1 quando uma FK representa o vínculo exclusivo.

CHECK

Use para restringir domínio e validar regras simples no nível de linha.

  • Faixas numéricas: CHECK (quantidade > 0)
  • Conjunto de valores: CHECK (status IN ('ABERTO','PAGO','CANCELADO'))
  • Regras condicionais simples: CHECK (data_fim IS NULL OR data_fim >= data_inicio)

FOREIGN KEY e ações referenciais

Além de declarar a FK, escolha o comportamento em exclusões/atualizações:

  • ON DELETE RESTRICT/NO ACTION: impede apagar pai com filhos.
  • ON DELETE CASCADE: apaga filhos junto (bom para entidades dependentes).
  • ON DELETE SET NULL: desassocia (exige FK nullable).

Evite CASCADE quando a exclusão em cadeia pode apagar dados de negócio importantes sem intenção.

Exemplo completo (esquema lógico + dicionário de dados)

Mini-ER (descrição conceitual resumida)

  • Entidades: Cliente, Pedido, Produto.
  • Relacionamentos: Cliente 1:N Pedido; Pedido N:N Produto (com atributos quantidade e preço_unitário).
  • Atributo multivalorado: Cliente possui Telefones (vários).
  • Regra de domínio: Pedido.status ∈ {ABERTO, PAGO, CANCELADO}.

Script lógico (estrutura SQL)

-- Exemplo em SQL padrão (ajuste tipos/auto incremento conforme seu SGBD)
CREATE TABLE cliente (  cliente_id      BIGINT PRIMARY KEY,  nome            VARCHAR(120) NOT NULL,  email           VARCHAR(254) NOT NULL,  documento       VARCHAR(20)  NOT NULL,  data_cadastro   TIMESTAMP    NOT NULL,  CONSTRAINT uq_cliente_email UNIQUE (email),  CONSTRAINT uq_cliente_documento UNIQUE (documento));CREATE TABLE pedido (  pedido_id       BIGINT PRIMARY KEY,  cliente_id      BIGINT NOT NULL,  data_pedido     TIMESTAMP NOT NULL,  status          VARCHAR(10) NOT NULL,  valor_total     DECIMAL(12,2) NOT NULL,  CONSTRAINT fk_pedido_cliente FOREIGN KEY (cliente_id) REFERENCES cliente(cliente_id) ON DELETE RESTRICT ON UPDATE RESTRICT,  CONSTRAINT ck_pedido_status CHECK (status IN ('ABERTO','PAGO','CANCELADO')),  CONSTRAINT ck_pedido_valor_total CHECK (valor_total >= 0));CREATE TABLE produto (  produto_id      BIGINT PRIMARY KEY,  nome            VARCHAR(150) NOT NULL,  preco_atual     DECIMAL(12,2) NOT NULL,  ativo           BOOLEAN NOT NULL,  CONSTRAINT ck_produto_preco CHECK (preco_atual >= 0));CREATE TABLE item_pedido (  pedido_id       BIGINT NOT NULL,  produto_id      BIGINT NOT NULL,  quantidade      INT NOT NULL,  preco_unitario  DECIMAL(12,2) NOT NULL,  CONSTRAINT pk_item_pedido PRIMARY KEY (pedido_id, produto_id),  CONSTRAINT fk_item_pedido_pedido FOREIGN KEY (pedido_id) REFERENCES pedido(pedido_id) ON DELETE CASCADE ON UPDATE RESTRICT,  CONSTRAINT fk_item_pedido_produto FOREIGN KEY (produto_id) REFERENCES produto(produto_id) ON DELETE RESTRICT ON UPDATE RESTRICT,  CONSTRAINT ck_item_quantidade CHECK (quantidade > 0),  CONSTRAINT ck_item_preco_unitario CHECK (preco_unitario >= 0));CREATE TABLE cliente_telefone (  cliente_id      BIGINT NOT NULL,  telefone        VARCHAR(20) NOT NULL,  tipo            VARCHAR(10) NOT NULL,  CONSTRAINT pk_cliente_telefone PRIMARY KEY (cliente_id, telefone),  CONSTRAINT fk_cliente_telefone_cliente FOREIGN KEY (cliente_id) REFERENCES cliente(cliente_id) ON DELETE CASCADE ON UPDATE RESTRICT,  CONSTRAINT ck_cliente_telefone_tipo CHECK (tipo IN ('CELULAR','FIXO','COMERCIAL')));

Dicionário de dados (tabelas e colunas)

TabelaColunaTipoObrigatórioConstraints/Observações
clientecliente_idBIGINTSimPK
clientenomeVARCHAR(120)SimNome completo/razão social conforme regra do domínio
clienteemailVARCHAR(254)SimUNIQUE (uq_cliente_email)
clientedocumentoVARCHAR(20)SimUNIQUE (uq_cliente_documento); pode representar CPF/CNPJ/ID interno
clientedata_cadastroTIMESTAMPSimMomento de criação do cliente
pedidopedido_idBIGINTSimPK
pedidocliente_idBIGINTSimFK → cliente(cliente_id); ON DELETE RESTRICT
pedidodata_pedidoTIMESTAMPSimData/hora do pedido
pedidostatusVARCHAR(10)SimCHECK: ABERTO/PAGO/CANCELADO
pedidovalor_totalDECIMAL(12,2)SimCHECK: valor_total >= 0 (pode ser calculado pela aplicação)
produtoproduto_idBIGINTSimPK
produtonomeVARCHAR(150)SimNome do produto
produtopreco_atualDECIMAL(12,2)SimCHECK: preco_atual >= 0
produtoativoBOOLEANSimIndica disponibilidade
item_pedidopedido_idBIGINTSimPK (composta) e FK → pedido; ON DELETE CASCADE
item_pedidoproduto_idBIGINTSimPK (composta) e FK → produto
item_pedidoquantidadeINTSimCHECK: quantidade > 0
item_pedidopreco_unitarioDECIMAL(12,2)SimCHECK: preco_unitario >= 0; preço no momento do pedido
cliente_telefonecliente_idBIGINTSimPK (composta) e FK → cliente; ON DELETE CASCADE
cliente_telefonetelefoneVARCHAR(20)SimPK (composta); formato pode ser validado na aplicação ou via CHECK avançado
cliente_telefonetipoVARCHAR(10)SimCHECK: CELULAR/FIXO/COMERCIAL

Agora responda o exercício sobre o conteúdo:

Ao mapear um relacionamento 1:N do modelo ER para o esquema relacional, qual é a decisão correta para representar esse relacionamento nas tabelas e reforçar a obrigatoriedade quando necessário?

Você acertou! Parabéns, agora siga para a próxima página

Você errou! Tente novamente.

Em 1:N, a FK deve ficar no lado N (filho), apontando para a PK do lado 1 (pai). Se o filho não puder existir sem o pai, essa FK deve ser NOT NULL.

Próximo capitúlo

Modelagem de Dados com casos especiais: histórico, temporalidade e auditoria

Arrow Right Icon
Baixe o app para ganhar Certificação grátis e ouvir os cursos em background, mesmo com a tela desligada.
  • Leia este curso no aplicativo para ganhar seu Certificado Digital!
  • Ouça este curso no aplicativo sem precisar ligar a tela do celular;
  • Tenha acesso 100% gratuito a mais de 4000 cursos online, ebooks e áudiobooks;
  • + Centenas de exercícios + Stories Educativos.