Fundamentos da Sintaxe de Consulta
A estrutura básica parra recuperar dados de múltiplas tabelas envolve a cláusula SELECT, listando as colunas desejadas, a cláusula FROM especificando as tabelas de origem e a cláusula WHERE para definir as condições de filtragem e junção.
Preparação do Ambiente e Dados Iniciais
Para ilustrar os conceitos, criaremos um esquema simples contendo departamentos e funcionários.
-- Criação da tabela de departamentos
CREATE TABLE departamento (
id INT PRIMARY KEY AUTO_INCREMENT,
nome VARCHAR(50) NOT NULL
);
INSERT INTO departamento (nome) VALUES ('Engenharia'), ('Vendas'), ('Financeiro');
-- Criação da tabela de funcionários
CREATE TABLE funcionario (
id INT PRIMARY KEY AUTO_INCREMENT,
nome VARCHAR(50),
sexo CHAR(1),
salario DECIMAL(10, 2),
data_admissao DATE,
id_departamento INT,
FOREIGN KEY (id_departamento) REFERENCES departamento(id)
);
INSERT INTO funcionario (nome, sexo, salario, data_admissao, id_departamento) VALUES
('Ana Silva', 'F', 7500.00, '2015-03-10', 1),
('Carlos Souza', 'M', 4200.00, '2018-07-22', 2),
('Mariana Costa', 'F', 8900.00, '2012-11-05', 2),
('Roberto Lima', 'M', 5100.00, '2020-01-15', 3),
('Juliana Alves', 'F', 4800.00, '2019-06-30', 1);
O Conceito de Produto Cartesiano
Quando duas ou mais tabelas são consultadas sem uma condição de junção adequada, o banco de dados gera um produto cartesiano. Isso significa que cada linha da primeira tabela é combinada com todas as linhas da segunda tabela. Para obter resultados úteis, é essencial aplicar condições que eliminem essas combinações inválidas.
Classificação das Junções (Joins)
1. Inner Join (Junção Interna)
Retorna apenas os registros que têm correspondência em ambas as tabelas.
Junção Implícita: Utiliza a cláusula WHERE para filtrar o produto cartesiano.
-- Exemplo: Buscar dados de funcionários e seus respectivos departamentos
SELECT f.nome AS nome_funcionario, f.salario, d.nome AS nome_departamento
FROM funcionario f, departamento d
WHERE f.id_departamento = d.id;
Junção Explícita: Utiliza a palavra-chave INNER JOIN (ou apenas JOIN) com a cláusula ON. Esta é a abordagem recomendada por ser mais legível e padronizada.
SELECT f.nome, f.salario, d.nome
FROM funcionario f
INNER JOIN departamento d ON f.id_departamento = d.id;
2. Outer Join (Junção Externa)
Retorna todos os registros de uma tabela, mesmo que não haja correspondência na outra.
Left Outer Join: Retorna todos os registros da tabela à esquerda e os correspondantes da direita.
-- Adicionando um funcionário sem departamento para o exemplo
INSERT INTO funcionario (nome, sexo, salario, data_admissao, id_departamento)
VALUES ('Pedro Santos', 'M', 3000.00, '2023-01-10', NULL);
-- Consultando todos os funcionários, exibindo o departamento se existir
SELECT f.nome, d.nome AS departamento
FROM funcionario f
LEFT JOIN departamento d ON f.id_departamento = d.id;
Right Outer Join: Retorna todos os registros da tabela à direita e os correspondentes da esquerda.
SELECT f.nome, d.nome AS departamento
FROM funcionario f
RIGHT JOIN departamento d ON f.id_departamento = d.id;
Subconsultas (Subqueries)
Uma subconsulta é uma consulta aninhada dentro de outra. Elas são úteis para dividir problemas complexos em etapas lógicas.
Subconsulta de Linha e Coluna Únicas: Pode ser usada com operadores de comparação (=, >, <, etc.).
-- Encontrar funcionários que ganham acima da média
SELECT nome, salario
FROM funcionario
WHERE salario > (SELECT AVG(salario) FROM funcionario);
Subconsulta de Múltiplas Linhas e Coluna Única: Utiliza o operador IN.
-- Buscar funcionários dos departamentos de Engenharia e Vendas
SELECT nome, salario
FROM funcionario
WHERE id_departamento IN (SELECT id FROM departamento WHERE nome IN ('Engenharia', 'Vendas'));
Subconsulta de Múltiplas Linhas e Colunas (Tabela Virtual): A subconsulta atua como uma tabela temporária na cláusula FROM.
-- Consultar funcionários admitidos após 2018 e seus departamentos
SELECT d.nome AS departamento, sub.nome AS funcionario, sub.data_admissao
FROM departamento d
INNER JOIN (
SELECT nome, id_departamento, data_admissao
FROM funcionario
WHERE data_admissao > '2018-01-01'
) sub ON d.id = sub.id_departamento;
Exercícios Práticos de Consultas Complexas
Para consolidar o aprendizado, considere o seguinte esquema de banco de dados corporativo:
CREATE TABLE setor (
id INT PRIMARY KEY,
nome_setor VARCHAR(50),
localizacao VARCHAR(50)
);
INSERT INTO setor VALUES
(10, 'Pesquisa', 'Sao Paulo'),
(20, 'Recursos Humanos', 'Rio de Janeiro'),
(30, 'Comercial', 'Belo Horizonte'),
(40, 'Contabilidade', 'Curitiba');
CREATE TABLE cargo (
id INT PRIMARY KEY,
titulo VARCHAR(50),
descricao VARCHAR(100)
);
INSERT INTO cargo VALUES
(1, 'Diretor', 'Gestão estratégica'),
(2, 'Gerente', 'Gestão de equipes'),
(3, 'Analista', 'Execução de tarefas técnicas'),
(4, 'Assistente', 'Suporte operacional');
CREATE TABLE colaborador (
id INT PRIMARY KEY,
nome_colab VARCHAR(50),
id_cargo INT,
id_gerente INT,
data_contratacao DATE,
salario_base DECIMAL(10, 2),
comissao DECIMAL(10, 2),
id_setor INT,
FOREIGN KEY (id_cargo) REFERENCES cargo(id),
FOREIGN KEY (id_setor) REFERENCES setor(id)
);
INSERT INTO colaborador VALUES
(101, 'Lucas Oliveira', 4, 104, '2010-05-15', 4500.00, NULL, 20),
(102, 'Beatriz Ferreira', 3, 106, '2012-08-20', 8000.00, 1500.00, 30),
(103, 'Gabriel Mendes', 3, 106, '2015-02-10', 7500.00, 2000.00, 30),
(104, 'Camila Rocha', 2, 109, '2008-11-01', 12000.00, NULL, 20),
(105, 'Felipe Santos', 4, 106, '2018-04-12', 4000.00, 500.00, 30),
(106, 'Isabela Lima', 2, 109, '2009-07-30', 11500.00, NULL, 30),
(107, 'Thiago Costa', 2, 109, '2011-09-14', 10500.00, NULL, 10),
(108, 'Larissa Almeida', 4, 104, '2020-01-05', 4200.00, NULL, 20),
(109, 'Marcos Vieira', 1, NULL, '2005-03-22', 25000.00, NULL, 10),
(110, 'Amanda Souza', 3, 106, '2016-12-08', 8500.00, 1000.00, 30);
CREATE TABLE nivel_salarial (
grau INT PRIMARY KEY,
salario_min INT,
salario_max INT
);
INSERT INTO nivel_salarial VALUES
(1, 3000, 5000),
(2, 5001, 8000),
(3, 8001, 12000),
(4, 12001, 18000),
(5, 18001, 50000);
1. Detalhes Básicos de Colaboradores e Cargos
SELECT
c.id,
c.nome_colab,
c.salario_base,
ca.titulo,
ca.descricao
FROM colaborador c
INNER JOIN cargo ca ON c.id_cargo = ca.id;
2. Incluindo Informações do Setor
SELECT
c.id,
c.nome_colab,
c.salario_base,
ca.titulo,
s.nome_setor,
s.localizacao
FROM colaborador c
INNER JOIN cargo ca ON c.id_cargo = ca.id
INNER JOIN setor s ON c.id_setor = s.id;
3. Classificação por Nível Salarial
SELECT
c.nome_colab,
c.salario_base,
ns.grau
FROM colaborador c
INNER JOIN nivel_salarial ns
ON c.salario_base BETWEEN ns.salario_min AND ns.salario_max;
4. Visão Completa do Colaborador
SELECT
c.id,
c.nome_colab,
c.salario_base,
ca.titulo,
s.nome_setor,
s.localizacao,
ns.grau AS nivel_salario
FROM colaborador c
INNER JOIN cargo ca ON c.id_cargo = ca.id
INNER JOIN setor s ON c.id_setor = s.id
INNER JOIN nivel_salarial ns
ON c.salario_base BETWEEN ns.salario_min AND ns.salario_max;
5. Contagem de Colaboradores por Setor
SELECT
s.id,
s.nome_setor,
s.localizacao,
COALESCE(sub.total_colaboradores, 0) AS total
FROM setor s
LEFT JOIN (
SELECT id_setor, COUNT(id) AS total_colaboradores
FROM colaborador
GROUP BY id_setor
) sub ON s.id = sub.id_setor;
6. Hierarquia: Colaborador e seu Gerente Direto
Utilizamos um LEFT JOIN para garantir que colaboradores sem gerente (como o diretor) também sejam listados na consulta final.
SELECT
col.nome_colab AS colaborador,
ger.nome_colab AS gerente
FROM colaborador col
LEFT JOIN colaborador ger ON col.id_gerente = ger.id;