🧚 Ei! O Beekeeper Studio é uma GUI de banco de dados rápida, moderna e open source Download
7 de janeiro de 2026 Por Matthew Rathbone

Um relacionamento um-para-muitos liga uma linha de uma tabela pai a qualquer número de linhas de uma tabela filha, como um cliente e seus pedidos. Você o cria com uma coluna de chave estrangeira na tabela filha que aponta para o pai. Os exemplos funcionam em PostgreSQL, MySQL, SQLite e SQL Server.

Como modelar um relacionamento um-para-muitos

Dê à tabela filha, o lado “muitos”, uma coluna que guarda a chave primária do pai e declare-a como chave estrangeira. Cada pedido armazena o id do cliente que o fez, e a tabela de clientes não muda nada.

Diagrama de relacionamento um-para-muitos em banco de dados

CREATE TABLE customers (
    id INTEGER PRIMARY KEY,
    name VARCHAR(100) NOT NULL
);

-- Each order stores the id of the customer who placed it
CREATE TABLE orders (
    id INTEGER PRIMARY KEY,
    customer_id INTEGER NOT NULL,
    order_date DATE NOT NULL,
    total DECIMAL(10,2) NOT NULL,
    FOREIGN KEY (customer_id) REFERENCES customers(id)
);

Faça o join das duas tabelas pela chave estrangeira para ver cada cliente com seus pedidos:

-- Input: customers
-- id | name
-- ---+------
-- 1  | Alice
-- 2  | Bob
-- 3  | Carol

-- Input: orders
-- id | customer_id | order_date | total
-- ---+-------------+------------+-------
-- 1  | 2           | 2026-03-02 | 42.50
-- 2  | 1           | 2026-03-05 | 18.00
-- 3  | 2           | 2026-04-11 | 120.00
-- 4  | 2           | 2026-05-20 | 64.99

SELECT c.name, o.order_date, o.total
FROM customers c
JOIN orders o ON c.id = o.customer_id
ORDER BY c.name, o.order_date;

-- Output:
-- name  | order_date | total
-- ------+------------+-------
-- Alice | 2026-03-05 | 18.00
-- Bob   | 2026-03-02 | 42.50
-- Bob   | 2026-04-11 | 120.00
-- Bob   | 2026-05-20 | 64.99

Bob tem três pedidos e cada pedido tem um cliente.

Regras importantes

  • A chave estrangeira fica na tabela filha. Cada pedido registra seu cliente. Nunca dê a customers uma coluna que liste IDs de pedidos.
  • Um JOIN simples descarta pais sem filhos. Carol não aparece no resultado acima. Use um LEFT JOIN quando todos os pais precisarem aparecer, como nos exemplos mais abaixo.
  • Indexe a chave estrangeira. Joins e buscas por cliente precisam de um índice em orders.customer_id (o MySQL cria um para você junto com a chave estrangeira):
CREATE INDEX idx_orders_customer_id ON orders(customer_id);
  • A chave estrangeira protege as duas tabelas. Ela rejeita um pedido para um cliente que não existe e, por padrão, se recusa a excluir um cliente que ainda tem pedidos. Adicione ON DELETE CASCADE para excluir os pedidos junto com o cliente, ou ON DELETE SET NULL para manter os pedidos sem cliente (a coluna precisa então aceitar NULL):
-- Input: the customers and orders tables above

INSERT INTO orders VALUES (5, 99, '2026-06-01', 10.00);
-- ERROR: insert or update on table "orders" violates foreign key constraint "orders_customer_id_fkey"

DELETE FROM customers WHERE id = 1;
-- ERROR: update or delete on table "customers" violates foreign key constraint "orders_customer_id_fkey" on table "orders"

-- With FOREIGN KEY (customer_id) REFERENCES customers(id) ON DELETE CASCADE
DELETE FROM customers WHERE id = 1;

-- Output (orders after the DELETE):
-- id | customer_id | order_date | total
-- ---+-------------+------------+-------
-- 1  | 2           | 2026-03-02 | 42.50
-- 3  | 2           | 2026-04-11 | 120.00
-- 4  | 2           | 2026-05-20 | 64.99
  • O SQLite ignora chaves estrangeiras até você ativá-las. Execute PRAGMA foreign_keys = ON; em cada conexão, ou o SQLite aceitará pedidos de clientes que não existem.

Sintaxe alternativa

Há outras três formas de declarar a mesma chave:

-- Inline on the column (MySQL 8.0 accepts this but silently ignores it)
customer_id INTEGER NOT NULL REFERENCES customers(id)

-- Named, so errors and later changes can refer to it
CONSTRAINT fk_orders_customer FOREIGN KEY (customer_id) REFERENCES customers(id)

-- Added to an existing table (SQLite can't add a foreign key this way)
ALTER TABLE orders ADD CONSTRAINT fk_orders_customer
    FOREIGN KEY (customer_id) REFERENCES customers(id);

Para ids preenchidos pelo banco, declare a chave do pai como SERIAL PRIMARY KEY no PostgreSQL, INT AUTO_INCREMENT PRIMARY KEY no MySQL, INTEGER PRIMARY KEY no SQLite ou INT IDENTITY PRIMARY KEY no SQL Server. A coluna da chave estrangeira continua sendo um inteiro simples.

Quando usar um relacionamento um-para-muitos?

Use sempre que cada filho pertencer a exatamente um pai: clientes e pedidos, posts e comentários, departamentos e funcionários, categorias e produtos. Se um filho puder ter vários pais, como um aluno que faz vários cursos, você precisa de um relacionamento muitos-para-muitos. Estes são os problemas que um relacionamento um-para-muitos ajuda a resolver.

Mostrar cada pai com seus filhos

Uma nota fiscal com seus itens, um post com seus comentários: faça o join da tabela filha com a pai pela chave estrangeira. As colunas do pai se repetem em cada linha filha.

Contar os filhos de cada pai

Quantos pedidos cada cliente fez, quantos artigos cada autor escreveu: LEFT JOIN, agrupe pelo pai e conte uma coluna da tabela filha. Contar uma coluna filha em vez de * faz com que pais sem filhos apareçam com 0.

Encontrar pais sem filhos

Clientes que nunca fizeram pedidos, departamentos sem projetos, produtos que ninguém avaliou. Um LEFT JOIN preenche as colunas filhas com NULL quando não há correspondência, então mantenha as linhas em que o id do filho é NULL.

Somar os valores dos filhos

Receita por cliente, valor de estoque por categoria, horas registradas por projeto: SUM, AVG, MIN ou MAX sobre as linhas filhas, agrupando pelo pai. Um pai sem filhos recebe NULL, que o COALESCE transforma em 0.

Armazenar uma hierarquia em uma tabela

Funcionários e seus gerentes, pastas e subpastas, categorias e subcategorias: a chave estrangeira aponta para a própria tabela, e um NULL nela marca o topo da árvore. Faça o join da tabela com ela mesma para colocar cada linha ao lado do seu pai.

Exemplos reais de relacionamentos um-para-muitos

Mostrar o pedido mais recente de cada cliente

ROW_NUMBER() numera os pedidos de cada cliente do mais novo para o mais antigo. Mantenha o número 1:

-- Input: customers
-- id | name
-- ---+------
-- 1  | Alice
-- 2  | Bob
-- 3  | Carol

-- Input: orders
-- id | customer_id | order_date | total
-- ---+-------------+------------+-------
-- 1  | 2           | 2026-03-02 | 42.50
-- 2  | 1           | 2026-03-05 | 18.00
-- 3  | 2           | 2026-04-11 | 120.00
-- 4  | 2           | 2026-05-20 | 64.99

SELECT name, order_date, total
FROM (
    SELECT c.name, o.order_date, o.total,
           ROW_NUMBER() OVER (PARTITION BY c.id ORDER BY o.order_date DESC) AS rn
    FROM customers c
    JOIN orders o ON c.id = o.customer_id
) latest
WHERE rn = 1
ORDER BY name;

-- Output:
-- name  | order_date | total
-- ------+------------+------
-- Alice | 2026-03-05 | 18.00
-- Bob   | 2026-05-20 | 64.99

Classificar clientes por número de pedidos

-- Input: the customers and orders tables above

SELECT c.name, COUNT(o.id) AS order_count
FROM customers c
LEFT JOIN orders o ON c.id = o.customer_id
GROUP BY c.id, c.name
ORDER BY order_count DESC;

-- Output:
-- name  | order_count
-- ------+------------
-- Bob   | 3
-- Alice | 1
-- Carol | 0

Encontrar clientes que nunca fizeram pedidos

-- Input: the customers and orders tables above

SELECT c.name
FROM customers c
LEFT JOIN orders o ON c.id = o.customer_id
WHERE o.id IS NULL;

-- Output:
-- name
-- -----
-- Carol

NOT EXISTS faz o mesmo trabalho e fica mais próximo da pergunta:

-- Input: the customers and orders tables above

SELECT c.name
FROM customers c
WHERE NOT EXISTS (
    SELECT 1 FROM orders o WHERE o.customer_id = c.id
);

-- Output:
-- name
-- -----
-- Carol

Encontrar quem mais gasta

SUM sobre nenhuma linha é NULL, então o COALESCE transforma o total de Carol em 0:

-- Input: the customers and orders tables above

SELECT c.name, COALESCE(SUM(o.total), 0) AS total_spent
FROM customers c
LEFT JOIN orders o ON c.id = o.customer_id
GROUP BY c.id, c.name
ORDER BY total_spent DESC;

-- Output:
-- name  | total_spent
-- ------+------------
-- Bob   | 227.49
-- Alice | 18.00
-- Carol | 0

Percorrer um organograma de cima para baixo

O manager_id de cada funcionário aponta para outro funcionário. Uma consulta recursiva começa pela pessoa sem gerente e adiciona um nível de subordinados por vez:

CREATE TABLE employees (
    id INTEGER PRIMARY KEY,
    name VARCHAR(100) NOT NULL,
    manager_id INTEGER,  -- NULL for the person at the top
    FOREIGN KEY (manager_id) REFERENCES employees(id)
);

-- Input: employees
-- id | name  | manager_id
-- ---+-------+-----------
-- 1  | Maya  | NULL
-- 2  | Raj   | 1
-- 3  | Lena  | 1
-- 4  | Tom   | 2
-- 5  | Sofia | 2

-- In SQL Server, write WITH instead of WITH RECURSIVE
WITH RECURSIVE org_chart AS (
    -- Start at the top: the employee with no manager
    SELECT id, name, 0 AS level
    FROM employees
    WHERE manager_id IS NULL

    UNION ALL

    -- Then add each employee's direct reports, one level at a time
    SELECT e.id, e.name, oc.level + 1
    FROM employees e
    JOIN org_chart oc ON e.manager_id = oc.id
)
SELECT name, level
FROM org_chart
ORDER BY level, name;

-- Output:
-- name  | level
-- ------+------
-- Maya  | 0
-- Lena  | 1
-- Raj   | 1
-- Sofia | 2
-- Tom   | 2

Leitura adicional sobre relacionamentos um-para-muitos

Neste blog:

Documentação oficial:

Beekeeper Studio é um cliente de banco de dados gratuito e open source

A melhor ferramenta de consultas e edição de SQL que já usei. Tem tudo o que preciso para gerenciar meu banco de dados. - ⭐⭐⭐⭐⭐ Mit

O Beekeeper Studio é rápido, intuitivo e fácil de usar. Ele suporta vários bancos de dados e funciona muito bem no Windows, Mac e Linux.

A versão do Beekeeper para Linux tem 100% dos recursos, sem atalhos e sem concessões.

O que os usuários dizem sobre o Beekeeper Studio

★★★★★
"O Beekeeper Studio substituiu completamente meu antigo fluxo de trabalho com SQL. É rápido, intuitivo e torna o trabalho com bancos de dados agradável de novo."
— Alex K., Desenvolvedor de banco de dados
★★★★★
"Já testei muitas GUIs de banco de dados, mas o Beekeeper encontra o equilíbrio perfeito entre recursos e simplicidade. Simplesmente funciona."
— Sarah M., Engenheira Full Stack

Pronto para melhorar seu fluxo de trabalho com SQL?

download Baixe grátis