Administração de Banco de Dados (PARTE XIX)
JOINs na prática: Desvendando o verdadeiro poder do modelo relacional
Administração de Banco de Dados (PARTE XIX)
JOINs na prática: Desvendando o verdadeiro poder do modelo relacional
Nas partes anteriores desta série, nós aprendemos a inserir, atualizar e remover dados (DML), além de realizar consultas utilizando o comando SELECT acompanhado de filtros poderosos como o WHERE, LIKE, BETWEEN, IN, entre outros. Também vimos como operações aparentemente simples podem causar impactos gigantescos em ambientes reais e entendemos a importância da integridade referencial durante alterações e exclusões de dados.
Entretanto, até este momento, todas as nossas consultas trabalharam olhando para apenas uma tabela por vez. Conseguimos consultar clientes, listar salários e visualizar registros específicos, mas existe um detalhe importante:
Os bancos de dados relacionais foram criados para relacionar informações entre múltiplas tabelas.
Isso significa que, quando o gerente de vendas da sua empresa pede um relatório, ele não diz: “Me dê a lista de pedidos do cliente id_cliente 4”. Ele é um ser humano, e humanos não falam em IDs numéricos. Ele vai pedir: “Me dê o valor total dos pedidos feitos pelo Pedro Garcia”.
O problema é que o nome “Pedro Garcia” está na tabela clientes, e o "valor do pedido" está na tabela pedidos. Como fazemos para buscar informações em duas tabelas diferentes ao mesmo tempo?
É exatamente para resolver esse problema que o banco de dados “relacional” recebe esse nome. Neste artigo, entraremos no coração da linguagem de consulta (DQL) e aprenderemos a cruzar dados utilizando a mágica dos comandos JOIN.
💡 Em outras palavras: Finalmente começaremos a explorar o verdadeiro poder dos bancos de dados relacionais.
O que são JOINs?
Em bancos de dados relacionais, as informações normalmente ficam distribuídas em várias tabelas diferentes. Os JOINs existem justamente para permitir que esses dados sejam combinados durante uma consulta SQL.
Na prática, os JOINs são operações da linguagem SQL utilizadas para combinar informações de duas ou mais tabelas relacionadas para reunir informações separadas em uma única consulta.
Esses relacionamentos geralmente acontecem através da combinação entre:
- uma PRIMARY KEY;
- e uma FOREIGN KEY.
É essa ligação que permite ao banco entender como os registros se relacionam entre si.
Imagine, por exemplo, um sistema onde:
- a tabela clientes armazena os dados dos clientes;
- e a tabela pedidos armazena os pedidos realizados.
Nesse cenário, a coluna id_cliente presente na tabela pedidos funciona como uma chave estrangeira apontando para a tabela clientes.
Graças a esse relacionamento, conseguimos realizar consultas capazes de responder perguntas como:
- quem realizou cada pedido;
- quais pedidos pertencem a determinado cliente;
- ou até quais clientes nunca fizeram compras.
Na prática, os JOINs permitem que dados armazenados separadamente sejam consultados juntos através dos relacionamentos definidos no banco.
Cenário de testes
Para os exemplos deste artigo, utilizaremos novamente as tabelas clientes e pedidos, como mostrado a seguir.
Estrutura da tabela clientes
CREATE TABLE clientes (
id_cliente INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
nome VARCHAR(150) NOT NULL,
cpf CHAR(11) UNIQUE NOT NULL,
email VARCHAR(60) UNIQUE NOT NULL,
salario DECIMAL(10, 2) NOT NULL,
data_cadastro DATE NOT NULL DEFAULT CURRENT_DATE,
CHECK(salario > 0)
);
Estrutura da tabela pedidos
CREATE TYPE status_pedido AS ENUM (
'Finalizado',
'Em_andamento',
'Cancelado'
);
CREATE TABLE pedidos (
id_pedido INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
data_pedido DATE NOT NULL DEFAULT CURRENT_DATE,
valor_total DECIMAL(10,2) NOT NULL,
status status_pedido NOT NULL,
id_cliente INT NOT NULL,
CHECK(valor_total > 0),
CONSTRAINT fk_pedido_id_cliente
FOREIGN KEY (id_cliente)
REFERENCES clientes(id_cliente)
);
Inserindo registros para os testes, caso você não tenha
INSERT INTO clientes (nome, cpf, email, salario)
VALUES
('Ana Souza', '99988877766', 'ana@email.com', 4200.00),
('Pedro Lima', '44455566677', 'pedro@email.com', 6100.00),
('Fernanda Costa', '33322211100', 'fernanda@email.com', 5300.00),
('Carlos Mendes', '77788899900', 'carlos@email.com', 3900.00);
INSERT INTO pedidos (data_pedido, valor_total, status, id_cliente)
VALUES
('2026-05-21', 150.00, 'Em_andamento', 1),
('2026-05-22', 320.00, 'Finalizado', 2),
('2026-05-23', 570.00, 'Cancelado', 2),
('2026-05-24', 80.00, 'Finalizado', 3);
💡 Observe: O cliente “Carlos Mendes” não possui pedidos cadastrados. Isso será importante para entendermos os diferentes tipos de JOIN.
PK e FK na prática: o casamento perfeito
Antes de mergulharmos nos JOINs, precisamos entender exatamente como as tabelas estão conectadas.
Na nossa estrutura:
- clientes.id_cliente → PRIMARY KEY;
- pedidos.id_cliente → FOREIGN KEY.
Isso significa que:
- cada pedido pertence a um cliente;
- e o banco utiliza esse relacionamento para conectar as informações.
Visualmente o relacionamento é definido da forma como ilustrado a seguir:

Figura 1 — Diagrama entidade — relacionamento gerado através da ferramenta DBeaver.
Lembra quando criamos as tabelas e definimos a PRIMARY KEY (chave primária) para a tabela clientes e a FOREIGN KEY (chave estrangeira) para a tabela pedido? Naquele momento, usamos as chaves para proteger o banco contra exclusões inválidas.
💡 Em outras palavras: A FOREIGN KEY funciona como um “link” entre tabelas relacionais.
Agora, usaremos essas mesmas chaves para conectar os dados. O JOIN usa a PK de uma tabela e a FK da outra como se fossem as duas metades de um zíper.
Vamos conhecer os três tipos de cruzamento de dados mais utilizados no mercado.
💡 NOTA: Para gerar o seu diagrama entidade — relacionamento através da ferramenta DBeaver, siga os seguintes passos: 1) Procure seu banco de dados na área esquerda, onde está a sua conexão com seu SGBD (no nosso caso, é o PostgreSQL, mas também serve para qualquer banco de dados relacional); 2) Uma vez tenha ubicado seu banco de dados, expanda o ícone que aparece ao lado da opção “Esquemas” ou “Schemas” que logo você verá uma outra opção chamada “public”; 3) Uma vez encontrada essa opção chamada “public”, clicar com o botão direito do mouse e selecionar clicando na opção “Visualizar esquema” ou “View schema”; 4) Uma vez feito o descrito anteriormente, deverá aparecer na tela do DBeaver seu diagrama entidade — relacionamento, o qual poderá exportar, caso você queira clicando com o botão direito do mouse em qualquer lugar do diagrama ER e selecionando a opção “Salvar diagrama como” ou “save diagram as”, nesse instante selecione em qual pasta do seu sistema operacional irá armazenar o diagrama gerado e já estará pronto para ser usado ou compartilhado do jeito que você melhor decidir.
INNER JOIN: trazendo apenas registros relacionados
O INNER JOIN é o cruzamento mais estrito e mais utilizado na linguagem SQL, já que ele só retorna os registros “se houver correspondência em ambas as tabelas”. Se um cliente não fez nenhum pedido, ele não aparece. Se um pedido não tiver cliente (o que é impossível graças à nossa FK), ele também não aparece.
A sintaxe básica funciona assim:
SELECT
tabela_1.coluna,
tabela_2.coluna,
tabela_3.coluna
FROM tabela_1
INNER JOIN tabela_2
ON tabela_1.coluna_PK = tabela_2.coluna_FK
INNER JOIN tabela_3
ON tabela_2.coluna_PK = tabela_3.coluna_FK;
Essa estrutura é utilizada para combinar informações de tabelas diferentes em uma única consulta SQL.
O funcionamento ocorre da seguinte forma:
- SELECT: define quais colunas serão exibidas no resultado;
- FROM: define a tabela principal da consulta;
- INNER JOIN: informa qual ou quais tabelas serão conectadas à consulta;
- ON: define a condição de relacionamento entre as tabelas.
A cláusula ON é a parte mais importante do JOIN, porque é ela que define como os registros serão associados.
Observe o seguinte:
ON tabela_1.coluna_PK = tabela_2.coluna_FK
Nesse caso:
- tabela_1.coluna_PK representa a chave primária da tabela principal;
- tabela_2.coluna_FK representa a chave estrangeira da tabela relacionada.
É justamente essa comparação entre as colunas chave que permite ao SGBD combinar os dados corretamente. A partir disso, podemos combinar quantas tabelas forem necessárias dentro da mesma consulta SQL.
Vamos montar um relatório mostrando o nome do cliente, o valor do pedido e o status do pedido. A consulta seria elaborada assim:
SELECT
c.nome,
p.valor_total,
p.status
FROM clientes c
INNER JOIN pedidos p
ON c.id_cliente = p.id_cliente;
Agora vamos dissecar o comando:
- SELECT: Informamos o que queremos ver. Note que agora colocamos o nome da tabela antes da coluna (tabela.coluna) para o banco não se confundir caso as duas tabelas tenham colunas com o mesmo nome.
- FROM clientes: Escolhemos a tabela principal (que ficará do lado esquerdo).
- INNER JOIN pedidos: Dizemos com qual tabela queremos cruzar os dados.
- ON: Esta é a parte mais importante! É aqui que fechamos o zíper. Estamos dizendo ao SGBD: “Junte a linha do cliente com a linha do pedido sempre que o id_cliente da primeira for exatamente igual ao id_cliente da segunda”.
O resultado será uma tabela unificada e perfeita contendo apenas os clientes que compraram algo.
LEFT JOIN: trazendo tudo da tabela da esquerda
O LEFT JOIN é utilizado quando desejamos retornar todos os registros da tabela principal, mesmo que não exista correspondência na tabela relacionada. Na prática, ele funciona de forma semelhante ao INNER JOIN, porém com uma diferença importante:
O LEFT JOIN mantém todos os dados da tabela localizada à esquerda da consulta, preenchendo com NULL os campos da tabela da direita quando não houver relacionamento.
Sua sintaxe básica é a mostrada a seguir:
SELECT
tabela_1.coluna,
tabela_2.coluna,
tabela_3.coluna
FROM tabela_1
LEFT JOIN tabela_2
ON tabela_1.coluna_PK = tabela_2.coluna_FK
LEFT JOIN tabela_3
ON tabela_2.coluna_PK = tabela_3.coluna_FK;
Nesse cenário, todos os registros de tabela_1 serão mantidos, os dados de tabela_2 serão adicionados quando existir relacionamento; os dados de tabela_3 também serão adicionados quando houver correspondência. Caso alguma relação não exista, os registros da tabela principal continuarão aparecendo e os campos das tabelas sem correspondência serão preenchidos com NULL.
Assim como o INNER JOIN, o LEFT JOIN também pode combinar múltiplas tabelas simultaneamente. Isso permite reunir diferentes informações em uma única consulta, mesmo quando alguns registros não possuem relacionamento correspondente.
Vamos montar um relatório seguindo o enunciado a seguir: “Quero uma lista de todos os clientes cadastrados e o valor dos seus pedidos. Se o cliente ainda não comprou nada, mostre ele na lista mesmo assim, para eu mandar um e-mail de desconto”. A consulta seria elaborada assim:
SELECT
c.nome,
p.valor_total
FROM clientes c
LEFT JOIN pedidos p
ON c.id_cliente = p.id_cliente;
Agora vamos dissecar o comando:
- SELECT: Informamos quais informações desejamos visualizar no resultado da consulta. Perceba que utilizamos clientes.nome e pedidos.valor_total. Esse formato (tabela.coluna) ajuda o banco a identificar exatamente de qual tabela cada informação está sendo retirada.
- FROM clientes: Definimos a tabela principal da consulta. Como estamos utilizando LEFT JOIN, todos os registros da tabela clientes serão exibidos no resultado, independentemente de existir relacionamento com a tabela pedidos.
- LEFT JOIN pedidos: Informamos ao banco que queremos combinar os dados da tabela pedidos com os registros da tabela clientes.
- ON clientes.id_cliente = pedidos.id_cliente: Aqui definimos a condição de relacionamento entre as tabelas. Estamos dizendo ao PostgreSQL: “Associe cada cliente aos pedidos que possuem o mesmo id_cliente”.
A grande diferença do LEFT JOIN é que clientes sem pedidos também continuarão aparecendo no resultado da consulta. Nesses casos, as colunas da tabela pedidos serão preenchidas com NULL. Ou seja, o resultado final será uma listagem contendo todos os clientes cadastrados, incluindo aqueles que ainda nunca realizaram compras.
RIGHT JOIN: trazendo tudo da tabela da direita
O RIGHT JOIN funciona de maneira muito semelhante ao LEFT JOIN, porém com uma diferença importante: ele sempre preserva todos os registros da tabela localizada à direita da consulta. Na prática, isso significa que o banco retornará todos os dados da tabela relacionada, mesmo que não exista correspondência na tabela principal. Sua sintaxe básica é a mostrada a seguir:
SELECT
tabela_1.coluna,
tabela_2.coluna,
tabela_3.coluna
FROM tabela_1
RIGHT JOIN tabela_2
ON tabela_1.coluna_PK = tabela_2.coluna_FK
RIGHT JOIN tabela_3
ON tabela_2.coluna_PK = tabela_3.coluna_FK;
Nesse cenário, todos os registros de tabela_2 serão exibidos e os dados de tabela_1 só aparecerão quando existir relacionamento correspondente. Caso algum registro da tabela da direita não possua correspondência, ele continuará aparecendo no resultado e os campos da tabela da esquerda serão preenchidos com NULL.
Esse tipo de JOIN é bastante utilizado quando queremos garantir que todos os registros de uma determinada tabela sejam exibidos, independentemente da existência de relacionamento.
Vamos montar um relatório seguindo o enunciado a seguir: “Quero uma lista contendo todos os pedidos realizados e o nome dos respectivos clientes. Mesmo que exista algum pedido associado incorretamente ou sem cliente cadastrado, ele ainda deve aparecer no relatório para análise”. A consulta seria elaborada da seguinte forma:
SELECT
c.nome,
p.valor_total
FROM clientes c
RIGHT JOIN pedidos p
ON c.id_cliente = p.id_cliente;
Agora vamos dissecar o comando:
- SELECT: Informamos quais informações desejamos visualizar no resultado da consulta. Nesse caso, estamos exibindo o nome do cliente (clientes.nome) e o valor total do pedido (pedidos.valor_total). Utilizar o formato tabela.coluna ajuda o banco a identificar exatamente de onde cada dado está sendo retirado.
- FROM clientes: Definimos a tabela localizada à esquerda da consulta. Entretanto, como estamos utilizando RIGHT JOIN, ela não será a tabela prioritária no resultado final.
- RIGHT JOIN pedidos: Aqui informamos ao banco que queremos combinar os registros da tabela pedidos com os dados da tabela clientes. Como se trata de um RIGHT JOIN, todos os pedidos serão exibidos, mesmo que não exista um cliente correspondente.
- ON clientes.id_cliente = pedidos.id_cliente: Nesta parte definimos a condição de relacionamento entre as tabelas. Estamos dizendo ao SGBD: “Associe cada pedido ao cliente que possui o mesmo id_cliente”.
Como todos os pedidos existentes possuem clientes válidos, o resultado será praticamente igual ao INNER JOIN. Entretanto, em cenários onde existam registros órfãos (ou relacionamentos opcionais), o RIGHT JOIN se torna extremamente útil.
A principal característica do RIGHT JOIN é que todos os registros da tabela localizada à direita da consulta serão preservados no resultado. Isso significa que pedidos sem cliente correspondente também continuarão aparecendo na listagem. Nesses casos, as colunas da tabela clientes serão preenchidas com NULL. Ou seja, o resultado final será uma listagem contendo todos os pedidos cadastrados, incluindo aqueles que não possuem relacionamento válido com clientes.
Na prática de mercado, desenvolvedores quase não usam o RIGHT JOIN. Por quê? Porque o cérebro humano tem mais facilidade de ler da esquerda para a direita.
Cuidados importantes ao utilizar JOINs
Embora os JOINs sejam um dos recursos mais poderosos da linguagem SQL, consultas mal estruturadas podem gerar problemas sérios de desempenho e consistência nos resultados.
Um relacionamento definido incorretamente pode provocar:
- lentidão nas consultas;
- consumo excessivo de memória;
- duplicidade inesperada de registros;
- ou até combinações incorretas de dados entre tabelas.
Na prática, isso normalmente acontece quando as tabelas não possuem relacionamentos bem definidos, as FOREIGN KEYs foram modeladas incorretamente ou a cláusula ON utiliza colunas erradas na comparação.
Por esse motivo, é extremamente importante revisar cuidadosamente as condições utilizadas nos JOINs, garantindo que os relacionamentos representem corretamente a estrutura lógica do banco de dados. Um único JOIN incorreto pode multiplicar registros inesperadamente e produzir resultados completamente diferentes do esperado.
Considerações finais
Os JOINs representam um dos pilares mais importantes da linguagem SQL e dos bancos de dados relacionais. Foi através deles que finalmente começamos a conectar informações distribuídas em múltiplas tabelas, explorando relacionamentos reais entre entidades do sistema.
Graças aos cruzamentos com INNER JOIN, LEFT JOIN e RIGHT JOIN, você agora é capaz de gerar relatórios unificados para áreas de vendas, marketing, recursos humanos e diretoria.
Porém, você pode ter notado que nossos relatórios ainda estão muito “brutos”. Nós listamos os pedidos, mas não sabemos qual é a soma total de dinheiro que a empresa faturou. Não sabemos quantos pedidos foram cancelados, nem qual é a média de salário dos clientes.
Como pedimos para o banco de dados realizar cálculos matemáticos e agrupamentos para nós?
Na próxima parte da nossa série, avançaremos para as Funções de Agregação da linguagem SQL, dominando o uso do SUM, COUNT, AVG, MIN, MAX, GROUP BY e HAVING.
💡 Em outras palavras: Começaremos a transformar dados relacionais em relatórios e informações estratégicas.
Gostou desse resumo prático? Não esqueça de deixar suas palmas 👏, considere compartilhá-lo com outras pessoas que também estejam aprendendo banco de dados, deixe seus comentários se tiver alguma dúvida sobre os conceitos apresentados e continue acompanhando a série.
Desde o mais profundo do meu coração, espero que este conteúdo tenha contribuído para aprofundar seus conhecimentos sobre SQL e bancos de dados relacionais.
Nos vemos na próxima parte!
Para continuar lendo a matéria, clique neste link.
Referências bibliográficas consultadas
ALVES, William Pereira. Banco de Dados — Teoria e Desenvolvimento. São Paulo: Editora Érica, 2009. DATE, C. J. Introdução aos Sistemas de Banco de Dados. 8. ed. Rio de Janeiro: Campus, 2004. ELMASRI, R.; NAVATHE, S. Sistemas de Banco de Dados. São Paulo: Pearson/Addison Wesley, 2011. SILBERSCHATZ, Abraham; KORTH, Henry F.; SUDARSHAN, S. Sistema de Banco de Dados. São Paulo: Elsevier, 2012.
메타데이터
- post_id
- 57768bce5af2
- slug
- administração-de-banco-de-dados-parte-xix-57768bce5af2
- url
- https://medium.com/@kamuz01/administra%C3%A7%C3%A3o-de-banco-de-dados-parte-xix-57768bce5af2
- canonical_url
- https://medium.com/@kamuz01/administra%C3%A7%C3%A3o-de-banco-de-dados-parte-xix-57768bce5af2
- author_url
- https://medium.com/@kamuz01
- status
- ok
- fetched_at
- 2026-06-10 18:44:10