Prática - Consultas Básicas Abrangentes
Prática: Consultas Básicas Abrangentes
Nas primeiras 5 lições, aprendemos o "ouvir, falar, ler e escrever" do SQL — criar tabelas, consultar, filtrar, ordenar, operações CRUD e tipos de dados. Agora é hora de combinar essas habilidades para resolver problemas reais de negócio. Esta lição não tem sintaxe nova — apenas prática.
Requisitos do Projeto
Você é um analista de dados em uma empresa de e-commerce e precisa extrair as seguintes informações do banco de dados da empresa:
- Gestão de Funcionários: Consultar funcionários em departamentos específicos, ordenar por salário, paginar resultados
- Filtragem de Produtos: Filtrar produtos por faixa de preço e estoque, busca fuzzy por nomes de produtos
- Análise de Pedidos: Consultar pedidos recentes, calcular valores de pedidos, atualizar status de pedidos
- Manutenção de Dados: Atualização em lote de preços, limpeza de dados expirados, inserção de novos dados
O banco de dados contém quatro tabelas: employees, departments, orders, products.
Implementação Completa do Código
Passo 1: Criar Banco de Dados e Tabelas
-- Criar tabela departments
CREATE TABLE departments (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
location TEXT
);
-- Criar tabela employees
CREATE TABLE employees (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
department_id INTEGER,
salary DECIMAL(10,2),
hire_date DATE,
FOREIGN KEY (department_id) REFERENCES departments(id)
);
-- Criar tabela products
CREATE TABLE products (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
category TEXT,
price DECIMAL(10,2),
stock INTEGER DEFAULT 0
);
-- Criar tabela orders
CREATE TABLE orders (
id INTEGER PRIMARY KEY AUTOINCREMENT,
customer_name TEXT NOT NULL,
product_id INTEGER,
quantity INTEGER,
order_date DATE,
FOREIGN KEY (product_id) REFERENCES products(id)
);
Passo 2: Inserir Dados de Teste
-- Inserir dados dos departamentos
INSERT INTO departments (name, location) VALUES
('Engineering', 'New York'),
('Marketing', 'Los Angeles'),
('Finance', 'New York'),
('HR', 'Chicago');
-- Inserir dados dos funcionários
INSERT INTO employees (name, department_id, salary, hire_date) VALUES
('John', 1, 15000.00, '2023-01-15'),
('Jane', 1, 18000.00, '2022-06-01'),
('Bob', 2, 12000.00, '2023-03-20'),
('Alice', 3, 13000.00, '2021-11-10'),
('Charlie', 1, 20000.00, '2020-08-05'),
('Diana', 2, 11000.00, '2024-01-10'),
('Eve', NULL, 9000.00, '2024-05-01'),
('Frank', 3, 14000.00, '2022-09-15');
-- Inserir dados dos produtos
INSERT INTO products (name, category, price, stock) VALUES
('iPhone 15', 'Phone', 5999.00, 100),
('MacBook Pro', 'Computer', 12999.00, 50),
('AirPods Pro', 'Accessory', 1899.00, 200),
('iPad Air', 'Tablet', 4799.00, 80),
('Apple Watch', 'Watch', 2999.00, 150),
('Magic Keyboard', 'Accessory', 999.00, 300),
('Mac Mini', 'Computer', 4499.00, 0),
('iPhone 14', 'Phone', 4999.00, 20);
-- Inserir dados dos pedidos
INSERT INTO orders (customer_name, product_id, quantity, order_date) VALUES
('Tom', 1, 1, '2024-06-01'),
('Lucy', 3, 2, '2024-06-02'),
('Mike', 2, 1, '2024-06-03'),
('Tom', 5, 1, '2024-06-05'),
('Sarah', 4, 3, '2024-06-10'),
('Lucy', 6, 5, '2024-06-15'),
('David', 1, 2, '2024-06-20'),
('Mike', 8, 1, '2024-06-25');
Passo 3: Consultas de Gestão de Funcionários
-- Q1: Consultar todos os funcionários de Engenharia, ordenados por salário do maior para o menor
SELECT name, salary, hire_date
FROM employees
WHERE department_id = 1
ORDER BY salary DESC;
Saída:
name salary hire_date
------- -------- ----------
Charlie 20000.00 2020-08-05
Jane 18000.00 2022-06-01
John 15000.00 2023-01-15
-- Q2: Consultar os 3 funcionários com maior salário (paginação: página 1, 3 por página)
SELECT name, salary, department_id
FROM employees
ORDER BY salary DESC
LIMIT 3 OFFSET 0;
Saída:
name salary department_id
------- -------- -------------
Charlie 20000.00 1
Jane 18000.00 1
John 15000.00 1
-- Q3: Consultar página 2 (3 registros por página)
SELECT name, salary, department_id
FROM employees
ORDER BY salary DESC
LIMIT 3 OFFSET 3;
Saída:
name salary department_id
----- -------- -------------
Frank 14000.00 3
Alice 13000.00 3
Bob 12000.00 2
-- Q4: Consultar funcionários sem departamento atribuído
SELECT name, salary
FROM employees
WHERE department_id IS NULL;
Saída:
name salary
---- --------
Eve 9000.00
Passo 4: Consultas de Filtragem de Produtos
-- Q5: Consultar produtos com preço entre 1000 e 5000, ordenados por preço ascendente
SELECT name, category, price, stock
FROM products
WHERE price BETWEEN 1000 AND 5000
ORDER BY price ASC;
Saída:
name category price stock
-------------- -------- ------- -----
AirPods Pro Accessory 1899.00 200
Apple Watch Watch 2999.00 150
Mac Mini Computer 4499.00 0
iPad Air Tablet 4799.00 80
-- Q6: Busca fuzzy por produtos com "iPhone" no nome
SELECT name, price, stock
FROM products
WHERE name LIKE '%iPhone%';
Saída:
name price stock
---------- ------- -----
iPhone 15 5999.00 100
iPhone 14 4999.00 20
-- Q7: Consultar produtos em estoque (stock > 0) com preço abaixo de 3000
SELECT name, price, stock
FROM products
WHERE stock > 0 AND price < 3000
ORDER BY price DESC;
Saída:
name price stock
-------------- ------- -----
AirPods Pro 1899.00 200
Magic Keyboard 999.00 300
Passo 5: Consultas de Análise de Pedidos
-- Q8: Consultar os 5 pedidos mais recentes, mostrando nome do produto e nome do cliente
SELECT o.customer_name, p.name AS product_name,
o.quantity, o.order_date
FROM orders o
JOIN products p ON o.product_id = p.id
ORDER BY o.order_date DESC
LIMIT 5;
Saída:
customer_name product_name quantity order_date
------------- ------------- -------- ----------
Mike iPhone 14 1 2024-06-25
David iPhone 15 2 2024-06-20
Lucy Magic Keyboard 5 2024-06-15
Sarah iPad Air 3 2024-06-10
Tom Apple Watch 1 2024-06-05
-- Q9: Consultar o valor de cada pedido (preço unitário × quantidade), ordenado por valor descendente
SELECT o.customer_name, p.name AS product_name,
p.price, o.quantity,
(p.price * o.quantity) AS total_amount
FROM orders o
JOIN products p ON o.product_id = p.id
ORDER BY total_amount DESC;
Saída:
customer_name product_name price quantity total_amount
------------- ------------- -------- -------- ------------
Sarah iPad Air 4799.00 3 14397.00
Mike MacBook Pro 12999.00 1 12999.00
David iPhone 15 5999.00 2 11998.00
Tom iPhone 15 5999.00 1 5999.00
Lucy Magic Keyboard 999.00 5 4995.00
Mike iPhone 14 4999.00 1 4999.00
Tom Apple Watch 2999.00 1 2999.00
Lucy AirPods Pro 1899.00 2 3798.00
Passo 6: Operações de Manutenção de Dados
-- Q10: Aumentar o preço de todos os produtos da categoria "Accessory" em 10%
-- Primeiro confirmar o escopo afetado
SELECT name, price FROM products WHERE category = 'Accessory';
Antes do ajuste:
name price
-------------- -------
AirPods Pro 1899.00
Magic Keyboard 999.00
-- Executar a atualização
UPDATE products
SET price = ROUND(price * 1.1, 2)
WHERE category = 'Accessory';
-- Verificar resultados
SELECT name, price FROM products WHERE category = 'Accessory';
Após o ajuste:
name price
-------------- -------
AirPods Pro 2088.90
Magic Keyboard 1098.90
-- Q11: Marcar produtos sem estoque (stock = 0) como descontinuados
-- Aqui deletamos produtos sem estoque como exemplo
-- Primeiro confirmar
SELECT id, name, stock FROM products WHERE stock = 0;
Resultado da confirmação:
id name stock
-- -------- -----
7 Mac Mini 0
-- Deletar produtos sem estoque
DELETE FROM products WHERE stock = 0;
-- Q12: Inserir um novo produto
INSERT INTO products (name, category, price, stock)
VALUES ('AirTag', 'Accessory', 229.00, 500);
-- Visualização final de todos os produtos
SELECT id, name, category, price, stock
FROM products
ORDER BY id;
Lista final de produtos:
id name category price stock
-- -------------- -------- -------- -----
1 iPhone 15 Phone 5999.00 100
2 MacBook Pro Computer 12999.00 50
3 AirPods Pro Accessory 2088.90 200
4 iPad Air Tablet 4799.00 80
5 Apple Watch Watch 2999.00 150
6 Magic Keyboard Accessory 1098.90 300
8 iPhone 14 Phone 4999.00 20
9 AirTag Accessory 229.00 500
Análise do Código
| Consulta | Pontos de Conhecimento Utilizados | Técnicas-Chave |
|---|---|---|
| Q1-Q3 | SELECT + WHERE + ORDER BY + LIMIT/OFFSET | Paginação usa LIMIT n OFFSET (página-1)*n |
| Q4 | WHERE + IS NULL | Deve usar IS NULL para verificar NULL |
| Q5 | BETWEEN + ORDER BY | BETWEEN inclui ambos os valores dos extremos |
| Q6 | LIKE + curinga % |
%palavra-chave% corresponde em qualquer posição |
| Q7 | Múltiplas condições AND + operadores de comparação | Combinando condições para filtragem |
| Q8 | JOIN + ORDER BY + LIMIT | Consultas relacionando múltiplas tabelas |
| Q9 | JOIN + cálculo de expressão + ORDER BY | Cálculos em SELECT |
| Q10 | UPDATE + WHERE + ROUND | SELECT para confirmar antes de atualizar |
| Q11 | DELETE + WHERE | SELECT para confirmar antes de deletar |
| Q12 | INSERT + verificação final | Consulta para verificar após inserção |
❓ Perguntas Frequentes
P: Com paginação LIMIT OFFSET, se dados forem adicionados ou deletados, haverá duplicatas ou registros faltando? R: Sim. Se os dados mudarem durante a paginação, a paginação baseada em OFFSET pode resultar em registros duplicados ou faltando. Uma abordagem mais estável é "paginação por cursor":
WHERE id > último_id_da_página_anterior LIMIT 10, mas isso requer que cada registro tenha uma chave de ordenação consistente.
P:
LIKE '%palavra-chave%'causa falha de índice. E para grandes conjuntos de dados? R: LIKE começando com curinga realmente não pode usar um índice regular. Para grandes conjuntos de dados, considere: ① Usar busca de texto completo (FTS5, suportado pelo SQLite); ② Usar mecanismos de busca como Elasticsearch na camada de aplicação; ③ Se apenas correspondência de prefixo é necessária,LIKE 'palavra-chave%'pode utilizar um índice.
P: E se duas tabelas tiverem o mesmo nome de coluna em uma consulta JOIN? R: Use aliases de tabela ou aliases de coluna para diferenciar. Por exemplo:
SELECT e.name, d.name AS dept_name FROM employees e JOIN departments d ON e.department_id = d.id;. É um bom hábito usar aliases de coluna para uma saída mais clara.
P: Operações UPDATE e DELETE podem ser desfeitas? R: Se dentro de uma transação (entre BEGIN...COMMIT), você pode usar ROLLBACK para desfazer. Se já fez COMMIT, no SQLite é basicamente irrecuperável (a menos que você tenha um backup). MySQL pode recuperar através de replay de binlog. Então sempre faça backup antes de operações em produção.
📖 Resumo
- Combine SELECT + WHERE + ORDER BY + LIMIT para consultas complexas e paginação
- Use IS NULL para valores nulos, LIKE para correspondência fuzzy, BETWEEN para consultas de intervalo
- Consultas multi-tabela usam JOIN; campos calculados podem usar expressões em SELECT
- SELECT para confirmar antes de UPDATE/DELETE — este é o hábito de segurança mais importante
- Paginação usa LIMIT + OFFSET; para grandes conjuntos de dados, considere paginação por cursor
- Busca fuzzy começando com
%prejudica índices; para grandes conjuntos de dados, considere busca de texto completo
📝 Exercícios
Exercício 1 (⭐)
Escreva consultas para completar as seguintes tarefas:
- Consultar todos os funcionários que entraram em 2023, ordenados por data de contratação ascendente
- Consultar o produto mais caro da categoria "Phone"
- Consultar todos os pedidos do cliente "Tom", mostrando nome do produto e quantidade
Exercício 2 (⭐⭐)
Escreva consultas para completar as seguintes tarefas:
- Consultar o número de funcionários em cada departamento (dica: requer GROUP BY — você pode pular para lições posteriores e voltar para desafiar isso)
- Consultar o valor total de todos os pedidos
- Diminuir o preço de produtos da categoria "Computer" em 5%, depois consultar para verificar
Exercício 3 (⭐⭐⭐)
Simule um cenário de "alerta de estoque baixo":
- Consultar todos os produtos com estoque abaixo de 50, ordenados por estoque ascendente
- Inserir esses produtos em uma nova tabela
low_stock_alert - Aumentar o estoque de cada um desses produtos em 100
- Verificar os resultados da atualização



