Iniciante 2 horas

Diagrama Entidade-Relacionamento (DER)

Aprenda a criar e interpretar Diagramas Entidade-Relacionamento para modelagem eficiente de banco de dados

🎯 Objetivos da Aula

  • Compreender os conceitos fundamentais do DER
  • Identificar entidades, atributos e relacionamentos
  • Aplicar cardinalidade e chaves em diagramas
  • Criar DERs para sistemas reais

📋 Legenda das Atividades

🔨 CÓDIGO PRÁTICO Atividades para implementar e praticar
📖 EXEMPLO Conceitos e exemplos para estudo
📖 EXEMPLO

1. Introdução ao Diagrama Entidade-Relacionamento

O que é um DER? O Diagrama Entidade-Relacionamento é uma ferramenta visual fundamental para modelar a estrutura de um banco de dados. Ele representa as entidades (objetos do mundo real), seus atributos (características) e os relacionamentos entre elas.

Por que usar DER?

  • Visualização clara: Permite ver a estrutura do banco de dados de forma gráfica
  • Comunicação: Facilita a comunicação entre desenvolvedores, analistas e clientes
  • Planejamento: Ajuda a planejar a estrutura antes de implementar o banco
  • Documentação: Serve como documentação do sistema
  • Manutenção: Facilita futuras modificações e melhorias

Componentes Principais

🏢 Entidades

Objetos ou conceitos do mundo real (Cliente, Produto, Pedido)

📝 Atributos

Características das entidades (nome, idade, preço)

🔗 Relacionamentos

Conexões entre entidades (Cliente faz Pedido)

📖 EXEMPLO

2. Conceitos Básicos e Notações

Símbolos e Notações

Símbolos Principais

  • Retângulo: Representa entidades
  • Elipse: Representa atributos
  • Losango: Representa relacionamentos
  • Linha: Conecta elementos

Tipos de Atributos

  • Simples: Valor único (nome)
  • Composto: Múltiplas partes (endereço)
  • Multivalorado: Múltiplos valores (telefones)
  • Derivado: Calculado (idade a partir da data de nascimento)

Exemplo Visual de Notações

CLIENTE
id (PK)
nome
email
1
faz
N
PEDIDO
id (PK)
data
valor
📖 EXEMPLO

3. Entidades

Uma entidade é um objeto ou conceito do mundo real que pode ser identificado de forma única e sobre o qual queremos armazenar informações no banco de dados.

Características das Entidades

  • Identificação única: Cada instância deve ser distinguível
  • Relevância: Deve ser importante para o sistema
  • Atributos: Deve ter características que queremos armazenar
  • Instâncias múltiplas: Deve haver várias ocorrências
📖 EXEMPLO

Exemplos de Entidades em Diferentes Contextos:

E-commerce
  • • Cliente
  • • Produto
  • • Pedido
  • • Categoria
  • • Fornecedor
Escola
  • • Aluno
  • • Professor
  • • Disciplina
  • • Turma
  • • Nota
Biblioteca
  • • Livro
  • • Autor
  • • Usuário
  • • Empréstimo
  • • Editora
🔨 CÓDIGO PRÁTICO

Exercício: Identifique as Entidades

Para um sistema de clínica médica, identifique pelo menos 5 entidades principais e justifique por que cada uma é importante para o sistema.

Dica: Pense em quem usa o sistema, o que é gerenciado, e que informações são importantes para o funcionamento da clínica.

📖 EXEMPLO

4. Atributos

Atributos são as características ou propriedades que descrevem uma entidade. Eles representam os dados que queremos armazenar sobre cada instância da entidade.

Tipos de Atributos

1. Atributo Simples

Não pode ser dividido em partes menores

Exemplos: nome, idade, CPF, email

2. Atributo Composto

Pode ser dividido em sub-atributos

Exemplo: endereço (rua, número, cidade, CEP)

3. Atributo Multivalorado

Pode ter múltiplos valores

Exemplos: telefones, emails, habilidades

4. Atributo Derivado

Calculado a partir de outros atributos

Exemplos: idade (de data_nascimento), total (de itens)
📖 EXEMPLO

Entidade CLIENTE com Diferentes Tipos de Atributos

CLIENTE
id (PK) - Simples
nome - Simples
endereço - Composto
telefones - Multivalorado
idade - Derivado
data_nascimento - Simples
🔨 CÓDIGO PRÁTICO

Exercício: Classifique os Atributos

Para a entidade PRODUTO de um e-commerce, classifique cada atributo abaixo:

  • codigo: _______________
  • nome: _______________
  • preco_com_desconto: _______________ (calculado de preço e desconto)
  • categorias: _______________ (um produto pode ter várias)
  • dimensoes: _______________ (altura, largura, profundidade)
  • cor: _______________
📖 EXEMPLO

5. Relacionamentos

Relacionamentos são as associações ou conexões entre duas ou mais entidades. Eles representam como as entidades interagem entre si no mundo real.

Grau dos Relacionamentos

1. Relacionamento Unário

Uma entidade se relaciona consigo mesma

Exemplo: FUNCIONÁRIO supervisiona FUNCIONÁRIO

2. Relacionamento Binário

Relacionamento entre duas entidades

Exemplo: CLIENTE faz PEDIDO

3. Relacionamento Ternário

Relacionamento entre três entidades

Exemplo: MÉDICO atende PACIENTE em CONSULTA
📖 EXEMPLO

Relacionamentos em um Sistema de E-commerce

CLIENTE
id
nome
email
faz
PEDIDO
id
data
total
contém
PRODUTO
id
nome
preco
🔨 CÓDIGO PRÁTICO

Exercício: Identifique os Relacionamentos

Para um sistema de BIBLIOTECA, identifique os relacionamentos entre as entidades:

  • USUÁRIO _______ LIVRO (ação: emprestar)
  • LIVRO _______ AUTOR (ação: escrever)
  • LIVRO _______ CATEGORIA (ação: pertencer)
  • FUNCIONÁRIO _______ EMPRÉSTIMO (ação: registrar)
  • FUNCIONÁRIO _______ FUNCIONÁRIO (ação: supervisionar)

Dica: Pense em verbos que descrevem as ações entre as entidades.

📖 EXEMPLO

6. Cardinalidade

Cardinalidade define quantas instâncias de uma entidade podem se relacionar com quantas instâncias de outra entidade. É fundamental para entender as regras de negócio.

Tipos de Cardinalidade

1. Um para Um (1:1)

Cada instância de A se relaciona com apenas uma instância de B

Exemplo: PESSOA possui CPF

2. Um para Muitos (1:N)

Uma instância de A se relaciona com várias instâncias de B

Exemplo: CLIENTE faz vários PEDIDOS

3. Muitos para Muitos (N:N)

Várias instâncias de A se relacionam com várias instâncias de B

Exemplo: ALUNO cursa várias DISCIPLINAS
📖 EXEMPLO

Cardinalidades em um Sistema Acadêmico

Relacionamento 1:1
ALUNO
id
nome
1
possui
1
MATRÍCULA
numero
data
Relacionamento 1:N
PROFESSOR
id
nome
1
leciona
N
DISCIPLINA
codigo
nome
Relacionamento N:N
ALUNO
id
nome
N
cursa
N
DISCIPLINA
codigo
nome
🔨 CÓDIGO PRÁTICO

Exercício: Determine a Cardinalidade

Para um sistema de HOSPITAL, determine a cardinalidade dos relacionamentos:

  • MÉDICO atende PACIENTE: _____ : _____
  • PACIENTE possui PRONTUÁRIO: _____ : _____
  • MÉDICO prescreve MEDICAMENTO: _____ : _____
  • HOSPITAL tem DEPARTAMENTO: _____ : _____
  • ENFERMEIRO trabalha em PLANTÃO: _____ : _____

Dica: Pense: "Quantos X podem se relacionar com quantos Y?"

📖 EXEMPLO

7. Chaves

Chaves são atributos ou conjuntos de atributos que identificam unicamente as instâncias de uma entidade ou estabelecem relacionamentos entre entidades.

Tipos de Chaves

1. Chave Primária (PK)

Identifica unicamente cada registro na tabela

Características: Única, não nula, imutável
Exemplo: id, CPF, matrícula

2. Chave Estrangeira (FK)

Referencia a chave primária de outra tabela

Função: Estabelece relacionamentos
Exemplo: cliente_id em PEDIDO

3. Chave Candidata

Atributo que poderia ser chave primária

Exemplo: CPF, email, matrícula
Nota: Uma vira PK, outras ficam únicas

4. Chave Composta

Combinação de dois ou mais atributos

Exemplo: (aluno_id + disciplina_id)
Uso: Relacionamentos N:N
📖 EXEMPLO

Sistema de E-commerce - Chaves em Ação

Tabela CLIENTE
CLIENTE
🔑 id (PK) - Chave Primária
nome
📧 email (Candidata)
📄 cpf (Candidata)
telefone
Relacionamento CLIENTE → PEDIDO
CLIENTE
🔑 id (PK)
nome
1
faz
N
PEDIDO
🔑 id (PK)
🔗 cliente_id (FK)
data
total
Chave Composta - ITEM_PEDIDO
ITEM_PEDIDO
🔑 (pedido_id + produto_id) - PK Composta
🔗 pedido_id (FK)
🔗 produto_id (FK)
quantidade
preco_unitario
🔨 CÓDIGO PRÁTICO

Exercício: Identifique as Chaves

Para um sistema de BIBLIOTECA, identifique os tipos de chaves:

Tabela LIVRO:
  • id: _______________
  • isbn: _______________ (único para cada livro)
  • titulo: _______________
  • autor_id: _______________ (referencia AUTOR)
Tabela EMPRESTIMO:
  • (usuario_id + livro_id + data_emprestimo): _______________
  • usuario_id: _______________ (referencia USUARIO)
  • livro_id: _______________ (referencia LIVRO)

Tipos: Primária (PK), Estrangeira (FK), Candidata, Composta

📖 EXEMPLO

8. Exemplos Práticos

Vamos aplicar os conceitos de DER em exemplos práticos reais, criando diagramas completos e implementando-os com código SQL funcional.

📖 EXEMPLO

Sistema de E-commerce - Relacionamentos Complexos

DER: Loja Virtual Completa

CLIENTE
id (PK)
nome
email
cpf
telefone
data_nascimento
PRODUTO
id (PK)
nome
descricao
preco
estoque
categoria_id (FK)
CATEGORIA
id (PK)
nome
descricao
PEDIDO
id (PK)
data_pedido
status
valor_total
cliente_id (FK)
ITEM_PEDIDO
pedido_id (FK)
produto_id (FK)
quantidade
preco_unitario
subtotal
ENDERECO
id (PK)
rua
numero
cidade
cep
cliente_id (FK)

Relacionamentos:
• Um cliente pode fazer vários pedidos (1:N)
• Um pedido pode ter vários produtos (N:N)
• Um produto pertence a uma categoria (N:1)
• Um cliente pode ter vários endereços (1:N)

🔨 CÓDIGO PRÁTICO

1. SQL - Criação das Tabelas do E-commerce

-- Tabela de categorias
CREATE TABLE categorias (
  id SERIAL PRIMARY KEY,
  nome VARCHAR(100) NOT NULL,
  descricao TEXT,
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- Tabela de clientes
CREATE TABLE clientes (
  id SERIAL PRIMARY KEY,
  nome VARCHAR(100) NOT NULL,
  email VARCHAR(150) UNIQUE NOT NULL,
  cpf VARCHAR(11) UNIQUE NOT NULL,
  telefone VARCHAR(15),
  data_nascimento DATE,
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- Tabela de produtos
CREATE TABLE produtos (
  id SERIAL PRIMARY KEY,
  nome VARCHAR(200) NOT NULL,
  descricao TEXT,
  preco DECIMAL(10,2) NOT NULL,
  estoque INTEGER DEFAULT 0,
  categoria_id INTEGER REFERENCES categorias(id),
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- Tabela de endereços
CREATE TABLE enderecos (
  id SERIAL PRIMARY KEY,
  rua VARCHAR(200) NOT NULL,
  numero VARCHAR(10),
  cidade VARCHAR(100) NOT NULL,
  cep VARCHAR(8) NOT NULL,
  cliente_id INTEGER REFERENCES clientes(id) ON DELETE CASCADE,
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- Tabela de pedidos
CREATE TABLE pedidos (
  id SERIAL PRIMARY KEY,
  data_pedido TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  status VARCHAR(20) DEFAULT 'pendente',
  valor_total DECIMAL(10,2) DEFAULT 0,
  cliente_id INTEGER REFERENCES clientes(id) ON DELETE CASCADE
);

-- Tabela de itens do pedido (relacionamento N:N)
CREATE TABLE itens_pedido (
  pedido_id INTEGER REFERENCES pedidos(id) ON DELETE CASCADE,
  produto_id INTEGER REFERENCES produtos(id) ON DELETE CASCADE,
  quantidade INTEGER NOT NULL,
  preco_unitario DECIMAL(10,2) NOT NULL,
  subtotal DECIMAL(10,2) NOT NULL,
  PRIMARY KEY (pedido_id, produto_id)
);
📖 EXEMPLO

Sistema de Biblioteca - Relacionamentos Temporais

DER: Gestão de Biblioteca

LIVRO
id (PK)
titulo
isbn
ano_publicacao
editora
autor_id (FK)
AUTOR
id (PK)
nome
nacionalidade
data_nascimento
USUARIO
id (PK)
nome
email
telefone
tipo_usuario
EMPRESTIMO
id (PK)
data_emprestimo
data_devolucao_prevista
data_devolucao_real
status
livro_id (FK)
usuario_id (FK)
🔨 CÓDIGO PRÁTICO

2. Queries SQL Avançadas para Biblioteca

-- Buscar livros disponíveis para empréstimo
SELECT l.titulo, a.nome as autor, l.isbn
FROM livros l
JOIN autores a ON l.autor_id = a.id
WHERE l.id NOT IN (
  SELECT livro_id FROM emprestimos 
  WHERE status = 'ativo'
);

-- Listar empréstimos em atraso
SELECT u.nome as usuario, l.titulo, e.data_devolucao_prevista,
       CURRENT_DATE - e.data_devolucao_prevista as dias_atraso
FROM emprestimos e
JOIN usuarios u ON e.usuario_id = u.id
JOIN livros l ON e.livro_id = l.id
WHERE e.status = 'ativo' 
  AND e.data_devolucao_prevista < CURRENT_DATE;

-- Relatório de livros mais emprestados
SELECT l.titulo, a.nome as autor, COUNT(e.id) as total_emprestimos
FROM livros l
JOIN autores a ON l.autor_id = a.id
LEFT JOIN emprestimos e ON l.id = e.livro_id
GROUP BY l.id, l.titulo, a.nome
ORDER BY total_emprestimos DESC
LIMIT 10;
🔨 EXERCÍCIOS PRÁTICOS

Exercícios para Implementar

1. Sistema de Restaurante

Crie um DER para um sistema de restaurante com: Mesas, Garçons, Clientes, Pedidos, Pratos e Ingredientes. Implemente as tabelas SQL.

Dica: Considere relacionamentos N:N entre Pratos e Ingredientes.

2. Sistema de Academia

Desenvolva um DER para academia com: Alunos, Instrutores, Treinos, Exercícios e Equipamentos. Crie queries para relatórios.

Dica: Um treino pode ter vários exercícios, e um exercício pode usar vários equipamentos.

3. Sistema de Clínica Veterinária

Projete um DER para clínica veterinária com: Donos, Animais, Veterinários, Consultas, Tratamentos e Medicamentos.

Dica: Um dono pode ter vários animais, e um animal pode ter várias consultas.

🔨 CÓDIGO PRÁTICO

9. DER na Programação: Node.js, Express e Supabase

🚀 Como o DER se Transforma em Código

Na programação moderna, especialmente com Node.js, Express e Supabase, os conceitos do DER se materializam em estruturas de dados, APIs e bancos de dados. Vamos ver como cada elemento do DER se traduz em código prático.

📦 Entidades → Tabelas Supabase

Cada entidade vira uma tabela no banco

🔗 Relacionamentos → Foreign Keys

Conexões viram chaves estrangeiras

🛣️ APIs → Rotas Express

Operações viram endpoints REST

📖 EXEMPLO

Sistema de Blog - Do DER ao Código

DER: Sistema de Blog

USUÁRIO
id (PK)
nome
email
senha
created_at
1
escreve
N
POST
id (PK)
titulo
conteudo
usuario_id (FK)
created_at
1
recebe
N
COMENTÁRIO
id (PK)
texto
post_id (FK)
usuario_id (FK)
created_at
🔨 CÓDIGO PRÁTICO

1. Criação das Tabelas no Supabase (SQL)

-- Tabela USUÁRIO
CREATE TABLE usuarios (
  id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
  nome VARCHAR(100) NOT NULL,
  email VARCHAR(255) UNIQUE NOT NULL,
  senha VARCHAR(255) NOT NULL,
  created_at TIMESTAMP DEFAULT NOW()
);

-- Tabela POST
CREATE TABLE posts (
  id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
  titulo VARCHAR(200) NOT NULL,
  conteudo TEXT NOT NULL,
  usuario_id UUID REFERENCES usuarios(id) ON DELETE CASCADE,
  created_at TIMESTAMP DEFAULT NOW()
);

-- Tabela COMENTÁRIO
CREATE TABLE comentarios (
  id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
  texto TEXT NOT NULL,
  post_id UUID REFERENCES posts(id) ON DELETE CASCADE,
  usuario_id UUID REFERENCES usuarios(id) ON DELETE CASCADE,
  created_at TIMESTAMP DEFAULT NOW()
);
🔨 CÓDIGO PRÁTICO

2. API Express - Rotas para o Blog

const express = require('express');
const { createClient } = require('@supabase/supabase-js');
const app = express();

// Configuração Supabase
const supabase = createClient(
  process.env.SUPABASE_URL,
  process.env.SUPABASE_ANON_KEY
);

app.use(express.json());

// ROTAS PARA USUÁRIOS
app.post('/api/usuarios', async (req, res) => {
  const { nome, email, senha } = req.body;
  
  const { data, error } = await supabase
    .from('usuarios')
    .insert([{ nome, email, senha }])
    .select();
    
  if (error) return res.status(400).json({ error: error.message });
  res.json(data[0]);
});

// ROTAS PARA POSTS
app.get('/api/posts', async (req, res) => {
  const { data, error } = await supabase
    .from('posts')
    .select(`
      *,
      usuarios(nome),
      comentarios(count)
    `);
    
  if (error) return res.status(400).json({ error: error.message });
  res.json(data);
});

app.post('/api/posts', async (req, res) => {
  const { titulo, conteudo, usuario_id } = req.body;
  
  const { data, error } = await supabase
    .from('posts')
    .insert([{ titulo, conteudo, usuario_id }])
    .select();
    
  if (error) return res.status(400).json({ error: error.message });
  res.json(data[0]);
});

// ROTAS PARA COMENTÁRIOS
app.post('/api/comentarios', async (req, res) => {
  const { texto, post_id, usuario_id } = req.body;
  
  const { data, error } = await supabase
    .from('comentarios')
    .insert([{ texto, post_id, usuario_id }])
    .select();
    
  if (error) return res.status(400).json({ error: error.message });
  res.json(data[0]);
});
📖 EXEMPLO

E-commerce Avançado - Relacionamentos Complexos

DER: Sistema E-commerce Completo

CLIENTE
id (PK)
nome
email
telefone
1
faz
N
PEDIDO
id (PK)
data
total
cliente_id (FK)
PRODUTO
id (PK)
nome
preco
categoria_id (FK)
N
pertence
1
CATEGORIA
id (PK)
nome
descricao
ITEM_PEDIDO
pedido_id (FK)
produto_id (FK)
quantidade
preco_unitario
🔨 CÓDIGO PRÁTICO

Node.js - Lógica de Negócio para E-commerce

// Classe para gerenciar pedidos
class PedidoService {
  constructor(supabaseClient) {
    this.supabase = supabaseClient;
  }
  
  // Criar pedido com múltiplos produtos
  async criarPedido(clienteId, itens) {
    try {
      // 1. Criar o pedido
      const { data: pedido, error: pedidoError } = await this.supabase
        .from('pedidos')
        .insert([{
          cliente_id: clienteId,
          data: new Date().toISOString(),
          total: 0 // Será calculado depois
        }])
        .select()
        .single();
        
      if (pedidoError) throw pedidoError;
      
      // 2. Adicionar itens ao pedido
      let totalPedido = 0;
      const itensParaInserir = [];
      
      for (const item of itens) {
        // Buscar preço atual do produto
        const { data: produto } = await this.supabase
          .from('produtos')
          .select('preco')
          .eq('id', item.produto_id)
          .single();
          
        const subtotal = produto.preco * item.quantidade;
        totalPedido += subtotal;
        
        itensParaInserir.push({
          pedido_id: pedido.id,
          produto_id: item.produto_id,
          quantidade: item.quantidade,
          preco_unitario: produto.preco
        });
      }
      
      // 3. Inserir itens do pedido
      const { error: itensError } = await this.supabase
        .from('itens_pedido')
        .insert(itensParaInserir);
        
      if (itensError) throw itensError;
      
      // 4. Atualizar total do pedido
      const { error: updateError } = await this.supabase
        .from('pedidos')
        .update({ total: totalPedido })
        .eq('id', pedido.id);
        
      if (updateError) throw updateError;
      
      return { ...pedido, total: totalPedido };
      
    } catch (error) {
      throw new Error(`Erro ao criar pedido: ${error.message}`);
    }
  }
  
  // Buscar pedidos com detalhes
  async buscarPedidosCliente(clienteId) {
    const { data, error } = await this.supabase
      .from('pedidos')
      .select(`
        *,
        clientes(nome, email),
        itens_pedido(
          quantidade,
          preco_unitario,
          produtos(nome, categoria_id)
        )
      `)
      .eq('cliente_id', clienteId)
      .order('data', { ascending: false });
      
    if (error) throw error;
    return data;
  }
}
📖 EXEMPLO

Biblioteca Digital - Relacionamentos Múltiplos

DER: Sistema de Biblioteca Digital

LIVRO
id (PK)
titulo
isbn
ano_publicacao
editora_id (FK)
AUTOR
id (PK)
nome
nacionalidade
data_nascimento
USUÁRIO
id (PK)
nome
email
tipo
LIVRO_AUTOR
livro_id (FK)
autor_id (FK)
papel
EMPRÉSTIMO
id (PK)
livro_id (FK)
usuario_id (FK)
data_emprestimo
data_devolucao
🔨 CÓDIGO PRÁTICO

Express API - Consultas Complexas com Joins

// Rota para buscar livros com autores e disponibilidade
app.get('/api/livros', async (req, res) => {
  try {
    const { data: livros, error } = await supabase
      .from('livros')
      .select(`
        *,
        editoras(nome),
        livro_autor(
          papel,
          autores(nome, nacionalidade)
        ),
        emprestimos(
          data_emprestimo,
          data_devolucao,
          usuarios(nome)
        )
      `);
      
    if (error) throw error;
    
    // Processar dados para incluir disponibilidade
    const livrosComDisponibilidade = livros.map(livro => {
      const emprestimosAtivos = livro.emprestimos.filter(
        emp => !emp.data_devolucao
      );
      
      return {
        ...livro,
        disponivel: emprestimosAtivos.length === 0,
        emprestado_para: emprestimosAtivos[0]?.usuarios?.nome || null
      };
    });
    
    res.json(livrosComDisponibilidade);
    
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

// Rota para realizar empréstimo
app.post('/api/emprestimos', async (req, res) => {
  const { livro_id, usuario_id } = req.body;
  
  try {
    // Verificar se livro está disponível
    const { data: emprestimosAtivos } = await supabase
      .from('emprestimos')
      .select('*')
      .eq('livro_id', livro_id)
      .is('data_devolucao', null);
      
    if (emprestimosAtivos.length > 0) {
      return res.status(400).json({ 
        error: 'Livro já está emprestado' 
      });
    }
    
    // Criar empréstimo
    const { data, error } = await supabase
      .from('emprestimos')
      .insert([{
        livro_id,
        usuario_id,
        data_emprestimo: new Date().toISOString()
      }])
      .select(`
        *,
        livros(titulo),
        usuarios(nome)
      `);
      
    if (error) throw error;
    
    res.json(data[0]);
    
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});
🔨 CÓDIGO PRÁTICO

Queries SQL Avançadas - Relacionamentos em Ação

1. Consultas com JOIN - Sistema de Blog

-- Buscar posts com informações do autor e contagem de comentários
SELECT 
  p.id,
  p.titulo,
  p.conteudo,
  u.nome AS autor,
  u.email AS email_autor,
  COUNT(c.id) AS total_comentarios,
  p.created_at
FROM posts p
INNER JOIN usuarios u ON p.usuario_id = u.id
LEFT JOIN comentarios c ON p.id = c.post_id
GROUP BY p.id, p.titulo, p.conteudo, u.nome, u.email, p.created_at
ORDER BY p.created_at DESC;

-- Buscar usuários mais ativos (com mais posts)
SELECT 
  u.nome,
  u.email,
  COUNT(p.id) AS total_posts,
  MAX(p.created_at) AS ultimo_post
FROM usuarios u
LEFT JOIN posts p ON u.id = p.usuario_id
GROUP BY u.id, u.nome, u.email
HAVING COUNT(p.id) > 0
ORDER BY total_posts DESC;

2. Consultas Complexas - E-commerce

-- Relatório de vendas por categoria
SELECT 
  cat.nome AS categoria,
  COUNT(DISTINCT p.id) AS produtos_vendidos,
  SUM(ip.quantidade) AS quantidade_total,
  SUM(ip.quantidade * ip.preco_unitario) AS receita_total,
  AVG(ip.preco_unitario) AS preco_medio
FROM categorias cat
INNER JOIN produtos p ON cat.id = p.categoria_id
INNER JOIN itens_pedido ip ON p.id = ip.produto_id
INNER JOIN pedidos ped ON ip.pedido_id = ped.id
WHERE ped.data >= '2024-01-01'
GROUP BY cat.id, cat.nome
ORDER BY receita_total DESC;

-- Clientes com maior valor de compras
SELECT 
  c.nome,
  c.email,
  COUNT(p.id) AS total_pedidos,
  SUM(p.total) AS valor_total_compras,
  AVG(p.total) AS ticket_medio,
  MAX(p.data) AS ultima_compra
FROM clientes c
INNER JOIN pedidos p ON c.id = p.cliente_id
GROUP BY c.id, c.nome, c.email
HAVING SUM(p.total) > 1000
ORDER BY valor_total_compras DESC;

3. Consultas com Subconsultas - Biblioteca

-- Livros mais populares (mais emprestados)
SELECT 
  l.titulo,
  l.isbn,
  COUNT(e.id) AS total_emprestimos,
  (
    SELECT STRING_AGG(a.nome, ', ')
    FROM autores a
    INNER JOIN livro_autor la ON a.id = la.autor_id
    WHERE la.livro_id = l.id
  ) AS autores
FROM livros l
LEFT JOIN emprestimos e ON l.id = e.livro_id
GROUP BY l.id, l.titulo, l.isbn
ORDER BY total_emprestimos DESC
LIMIT 10;

-- Usuários com empréstimos em atraso
SELECT 
  u.nome,
  u.email,
  l.titulo,
  e.data_emprestimo,
  CURRENT_DATE - e.data_emprestimo::date AS dias_atraso
FROM usuarios u
INNER JOIN emprestimos e ON u.id = e.usuario_id
INNER JOIN livros l ON e.livro_id = l.id
WHERE e.data_devolucao IS NULL
  AND e.data_emprestimo < CURRENT_DATE - INTERVAL '14 days'
ORDER BY dias_atraso DESC;
🔨 CÓDIGO PRÁTICO

Implementação Completa no Supabase

🚀 Passo a Passo: Do DER ao Supabase Funcionando

📋
1. Criar DER
🗄️
2. Criar Tabelas
🔐
3. Configurar RLS
🛣️
4. Criar APIs

1. Configuração Inicial do Supabase

// supabase/config.js
const { createClient } = require('@supabase/supabase-js');

// Configuração do cliente Supabase
const supabaseUrl = process.env.SUPABASE_URL;
const supabaseKey = process.env.SUPABASE_ANON_KEY;

const supabase = createClient(supabaseUrl, supabaseKey);

// Função para verificar conexão
async function testarConexao() {
  try {
    const { data, error } = await supabase
      .from('usuarios')
      .select('count')
      .limit(1);
      
    if (error) {
      console.error('Erro na conexão:', error.message);
      return false;
    }
    
    console.log('✅ Conexão com Supabase estabelecida!');
    return true;
  } catch (err) {
    console.error('❌ Falha na conexão:', err.message);
    return false;
  }
}

module.exports = { supabase, testarConexao };

2. Row Level Security (RLS) - Segurança das Tabelas

-- Habilitar RLS em todas as tabelas
ALTER TABLE usuarios ENABLE ROW LEVEL SECURITY;
ALTER TABLE posts ENABLE ROW LEVEL SECURITY;
ALTER TABLE comentarios ENABLE ROW LEVEL SECURITY;

-- Políticas para a tabela usuarios
CREATE POLICY "Usuários podem ver próprio perfil" ON usuarios
  FOR SELECT USING (auth.uid() = id);

CREATE POLICY "Usuários podem atualizar próprio perfil" ON usuarios
  FOR UPDATE USING (auth.uid() = id);

-- Políticas para a tabela posts
CREATE POLICY "Posts são públicos para leitura" ON posts
  FOR SELECT USING (true);

CREATE POLICY "Usuários podem criar posts" ON posts
  FOR INSERT WITH CHECK (auth.uid() = usuario_id);

CREATE POLICY "Usuários podem editar próprios posts" ON posts
  FOR UPDATE USING (auth.uid() = usuario_id);

-- Políticas para comentários
CREATE POLICY "Comentários são públicos" ON comentarios
  FOR SELECT USING (true);

CREATE POLICY "Usuários autenticados podem comentar" ON comentarios
  FOR INSERT WITH CHECK (auth.uid() = usuario_id);

3. Triggers e Funções - Automação no Banco

-- Função para atualizar timestamp automaticamente
CREATE OR REPLACE FUNCTION update_updated_at_column()
RETURNS TRIGGER AS $$
BEGIN
  NEW.updated_at = NOW();
  RETURN NEW;
END;
$$ language 'plpgsql';

-- Trigger para posts
CREATE TRIGGER update_posts_updated_at
  BEFORE UPDATE ON posts
  FOR EACH ROW
  EXECUTE FUNCTION update_updated_at_column();

-- Função para contar comentários automaticamente
CREATE OR REPLACE FUNCTION atualizar_contador_comentarios()
RETURNS TRIGGER AS $$
BEGIN
  IF TG_OP = 'INSERT' THEN
    UPDATE posts 
    SET total_comentarios = total_comentarios + 1
    WHERE id = NEW.post_id;
    RETURN NEW;
  ELSIF TG_OP = 'DELETE' THEN
    UPDATE posts 
    SET total_comentarios = total_comentarios - 1
    WHERE id = OLD.post_id;
    RETURN OLD;
  END IF;
  RETURN NULL;
END;
$$ LANGUAGE plpgsql;

-- Triggers para comentários
CREATE TRIGGER trigger_comentario_insert
  AFTER INSERT ON comentarios
  FOR EACH ROW
  EXECUTE FUNCTION atualizar_contador_comentarios();

CREATE TRIGGER trigger_comentario_delete
  AFTER DELETE ON comentarios
  FOR EACH ROW
  EXECUTE FUNCTION atualizar_contador_comentarios();

🌍 Analogias do Mundo Real

🏢 DER = Planta de um Prédio

  • Entidades: Salas do prédio
  • Atributos: Características das salas (tamanho, tipo)
  • Relacionamentos: Corredores conectando salas
  • Chaves: Números das salas (identificação única)

🎭 DER = Roteiro de Filme

  • Entidades: Personagens do filme
  • Atributos: Características dos personagens
  • Relacionamentos: Interações entre personagens
  • Cardinalidade: Quantos personagens interagem

🍕 DER = Receita de Pizza

  • Entidades: Ingredientes da pizza
  • Atributos: Quantidade, tipo, origem
  • Relacionamentos: Como ingredientes se combinam
  • Regras: Proporções e compatibilidades

🎮 DER = Jogo de RPG

  • Entidades: Personagens, itens, missões
  • Atributos: Nível, força, raridade
  • Relacionamentos: Quem possui o quê
  • Cardinalidade: Quantos itens por personagem

4. APIs Express Completas - Integrando Tudo

// routes/blog.js - API completa para sistema de blog
const express = require('express');
const { supabase } = require('../config/supabase');
const router = express.Router();

// GET /api/posts - Listar posts com autores e comentários
router.get('/posts', async (req, res) => {
  try {
    const { data: posts, error } = await supabase
      .from('posts')
      .select(`
        id,
        titulo,
        conteudo,
        created_at,
        usuarios:usuario_id (
          nome,
          email
        ),
        comentarios (
          id,
          conteudo,
          created_at,
          usuarios:usuario_id (
            nome
          )
        )
      `)
      .order('created_at', { ascending: false });

    if (error) throw error;
    
    res.json({ success: true, data: posts });
  } catch (error) {
    res.status(500).json({ 
      success: false, 
      error: error.message 
    });
  }
});

// POST /api/posts - Criar novo post
router.post('/posts', async (req, res) => {
  try {
    const { titulo, conteudo, usuario_id } = req.body;
    
    const { data: post, error } = await supabase
      .from('posts')
      .insert({
        titulo,
        conteudo,
        usuario_id,
        total_comentarios: 0
      })
      .select()
      .single();

    if (error) throw error;
    
    res.status(201).json({ success: true, data: post });
  } catch (error) {
    res.status(500).json({ 
      success: false, 
      error: error.message 
    });
  }
});

module.exports = router;

5. Middleware de Validação e Autenticação

// middleware/auth.js
const { supabase } = require('../config/supabase');

// Middleware para verificar autenticação
const verificarAuth = async (req, res, next) => {
  try {
    const token = req.headers.authorization?.replace('Bearer ', '');
    
    if (!token) {
      return res.status(401).json({ 
        success: false, 
        error: 'Token de acesso necessário' 
      });
    }

    const { data: { user }, error } = await supabase.auth.getUser(token);
    
    if (error || !user) {
      return res.status(401).json({ 
        success: false, 
        error: 'Token inválido' 
      });
    }

    req.user = user;
    next();
  } catch (error) {
    res.status(500).json({ 
      success: false, 
      error: error.message 
    });
  }
};

// Middleware para validar dados de entrada
const validarPost = (req, res, next) => {
  const { titulo, conteudo } = req.body;
  
  if (!titulo || titulo.trim().length < 5) {
    return res.status(400).json({
      success: false,
      error: 'Título deve ter pelo menos 5 caracteres'
    });
  }
  
  if (!conteudo || conteudo.trim().length < 10) {
    return res.status(400).json({
      success: false,
      error: 'Conteúdo deve ter pelo menos 10 caracteres'
    });
  }
  
  next();
};

module.exports = { verificarAuth, validarPost };

💡 Resumo: DER → Código Funcionando

📊 Do Diagrama para o Banco
  • Entidades → Tabelas SQL
  • Atributos → Colunas com tipos
  • Relacionamentos → Foreign Keys
  • Cardinalidade → Constraints
🚀 Do Banco para a API
  • Tabelas → Rotas Express
  • Queries → Funções Supabase
  • Validações → Middlewares
  • Segurança → RLS + Auth

🎯 Resultado: Um DER bem planejado se transforma em um sistema completo e funcional!

🔨 CÓDIGO PRÁTICO

10. Exercícios Práticos

🔨 CÓDIGO PRÁTICO

Exercício 1: Sistema de Biblioteca

Crie um DER completo para um sistema de biblioteca com as seguintes entidades: Livro, Autor, Usuário, Empréstimo, Editora

Defina os atributos de cada entidade, identifique as chaves primárias e estabeleça os relacionamentos entre elas.

🔨 CÓDIGO PRÁTICO

Exercício 2: E-commerce

Desenvolva um DER para um sistema de e-commerce considerando: Cliente, Produto, Pedido, Categoria, Fornecedor

Considere relacionamentos como: um cliente pode fazer vários pedidos, um pedido pode conter vários produtos, produtos pertencem a categorias.