📊 SQL - Linguagem de Consulta Estruturada
Conceitos, Operações, JOINs, Agregações e Manipulação de Dados
1. O que é SQL?
SQL (Structured Query Language) é a linguagem padrão para gerenciar e manipular dados em bancos de dados relacionais. Ela permite consultar, inserir, atualizar e deletar dados de forma eficiente e segura.
SQL é utilizada para interagir com diversos sistemas de gerenciamento de banco de dados (SGBD), como MySQL, PostgreSQL, SQL Server, Oracle e SQLite. Uma das principais características do SQL é sua sintaxe simples e intuitiva, tornando-a acessível mesmo para iniciantes.
Os dados em um banco de dados SQL são organizados em tabelas, que contêm linhas (registros) e colunas (campos ou atributos). Cada tabela representa uma entidade, como Usuários, Produtos ou Pedidos.
As operações fundamentais em SQL são conhecidas como CRUD:
- CREATE (CREATE): inserir novos dados
- READ (SELECT): consultar dados existentes
- UPDATE (UPDATE): modificar dados existentes
- DELETE (DELETE): remover dados
Além do CRUD básico, SQL oferece recursos avançados como JOINs para combinar dados de múltiplas tabelas, agregações para calcular resumos (como somas e médias), subconsultas e índices para otimizar o desempenho das consultas.
SQL também é responsável pela integridade de dados através de restrições, garantindo que os dados armazenados sejam precisos e consistentes. Isso inclui chaves primárias, chaves estrangeiras e validações de dados.
Em resumo, SQL é a linguagem fundamental para qualquer desenvolvedor que trabalhe com dados, tornando imprescindível seu domínio para desenvolvimento de aplicações robustas e escaláveis.
2. Conceitos Fundamentais
Banco de Dados (Database)
- Definição: Coleção organizada de dados relacionados
- Função: Armazenar informações de forma estruturada
- Exemplo: sistema de vendas, rede social, e-commerce
- Acesso: Via SQL ou ORM (Object-Relational Mapping)
Tabela (Table)
- Definição: Estrutura de dois níveis com colunas e linhas
- Linhas: Registros ou tuplas (dados individuais)
- Colunas: Campos ou atributos (tipos de dados)
- Exemplo: Tabela usuários com colunas: id, nome, email
Chave Primária (Primary Key)
- Função: Identificador único de cada registro
- Características: Não pode ser nula, deve ser única
- Exemplo: id em uma tabela de usuários
- Utilidade: Garante integridade e permite busca rápida
Chave Estrangeira (Foreign Key)
- Função: Conecta registros entre tabelas diferentes
- Características: Referencia a chave primária de outra tabela
- Exemplo: usuario_id em uma tabela de pedidos
- Utilidade: Mantém relacionamentos e integridade referencial
3. Tipos de Dados em SQL
SQL oferece diferentes tipos de dados para armazenar informações específicas. Escolher o tipo correto é fundamental para otimizar o desempenho e garantir a integridade dos dados.
Tipos de Dados Comuns:
-- NUMÉRICOS
INT, INTEGER -- Números inteiros (-2147483648 a 2147483647)
BIGINT -- Números inteiros grandes
SMALLINT -- Números inteiros pequenos
DECIMAL(p, s) -- Números decimais precisos (ex: DECIMAL(10, 2))
FLOAT -- Números decimais com ponto flutuante
-- TEXTUAIS
VARCHAR(n) -- Texto variável até n caracteres
CHAR(n) -- Texto fixo com n caracteres
TEXT -- Texto longo sem limite
-- DATAS E HORÁRIOS
DATE -- Data (YYYY-MM-DD)
TIME -- Hora (HH:MM:SS)
DATETIME -- Data e hora
TIMESTAMP -- Timestamp automático
-- BOOLEANOS
BOOLEAN -- TRUE ou FALSE (ou 1 ou 0)
-- ESPECIAIS
BLOB -- Dados binários (imagens, arquivos)
JSON -- Dados em formato JSON
UUID -- Identificador único global
4. Operações Básicas - CRUD
CREATE TABLE - Criar Tabela:
CREATE TABLE usuarios (
id INT PRIMARY KEY AUTO_INCREMENT,
nome VARCHAR(100) NOT NULL,
email VARCHAR(100) NOT NULL UNIQUE,
senha VARCHAR(255) NOT NULL,
data_nascimento DATE,
ativo BOOLEAN DEFAULT TRUE,
criado_em TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
atualizado_em TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
-- Constraints (Restrições):
-- NOT NULL - Campo obrigatório
-- UNIQUE - Valor único (não pode repetir)
-- PRIMARY KEY - Identificador único da tabela
-- FOREIGN KEY - Referência a outra tabela
-- DEFAULT - Valor padrão
-- CHECK - Validação customizada
INSERT - Inserir Dados:
-- Inserir um registro
INSERT INTO usuarios (nome, email, senha, data_nascimento)
VALUES ('Ana Silva', 'ana@example.com', 'senha123', '1995-05-15');
-- Inserir múltiplos registros
INSERT INTO usuarios (nome, email, senha, data_nascimento) VALUES
('Bruno Costa', 'bruno@example.com', 'senha456', '1990-08-20'),
('Carlos Souza', 'carlos@example.com', 'senha789', '1992-03-10'),
('Diana Lima', 'diana@example.com', 'senha000', '1998-11-25');
-- Inserir com SELECT (copiar dados de outra tabela)
INSERT INTO usuarios_backup
SELECT * FROM usuarios WHERE ativo = TRUE;
SELECT - Consultar Dados:
-- Selecionar todas as colunas
SELECT * FROM usuarios;
-- Selecionar colunas específicas
SELECT id, nome, email FROM usuarios;
-- Com WHERE (filtrar)
SELECT * FROM usuarios WHERE ativo = TRUE;
SELECT * FROM usuarios WHERE nome LIKE 'A%'; -- Começa com A
SELECT * FROM usuarios WHERE id > 5 AND ativo = TRUE;
-- Com ORDER BY (ordenar)
SELECT * FROM usuarios ORDER BY nome ASC; -- Ascendente
SELECT * FROM usuarios ORDER BY id DESC; -- Descendente
-- Com LIMIT (limitar resultados)
SELECT * FROM usuarios LIMIT 10;
SELECT * FROM usuarios LIMIT 10 OFFSET 5; -- Paginação
-- Com DISTINCT (valores únicos)
SELECT DISTINCT cidade FROM usuarios;
-- Combinando cláusulas
SELECT nome, email FROM usuarios
WHERE ativo = TRUE
ORDER BY nome ASC
LIMIT 20;
UPDATE - Atualizar Dados:
-- Atualizar um registro específico
UPDATE usuarios SET nome = 'Ana Silva Santos' WHERE id = 1;
-- Atualizar múltiplos campos
UPDATE usuarios
SET nome = 'Ana Silva Santos', email = 'ana.silva@example.com'
WHERE id = 1;
-- Atualizar vários registros
UPDATE usuarios SET ativo = FALSE WHERE criado_em < '2020-01-01';
-- Usando expressões
UPDATE usuarios SET email = LOWER(email);
UPDATE pedidos SET total = quantidade * preco WHERE status = 'pendente';
DELETE - Deletar Dados:
-- Deletar um registro
DELETE FROM usuarios WHERE id = 5;
-- Deletar múltiplos registros
DELETE FROM usuarios WHERE ativo = FALSE AND criado_em < '2020-01-01';
-- Deletar todos (cuidado!)
DELETE FROM usuarios;
-- Usar DROP TABLE para deletar a tabela inteira
DROP TABLE usuarios;
5. JOINs - Combinando Tabelas
JOINs permitem combinar dados de múltiplas tabelas baseado em relacionamentos. Existem diferentes tipos de JOINs com comportamentos distintos.
Estrutura de Exemplo:
-- Tabela: usuarios
id | nome | email
1 | Ana Silva | ana@example.com
2 | Bruno Costa | bruno@example.com
3 | Carlos Souza | carlos@example.com
-- Tabela: pedidos
id | usuario_id | produto | valor
1 | 1 | Notebook | 3000.00
2 | 2 | Mouse | 150.00
3 | 1 | Teclado | 450.00
4 | 5 | Monitor | 1200.00
INNER JOIN:
-- Retorna apenas registros que existem em AMBAS as tabelas
SELECT usuarios.nome, pedidos.produto, pedidos.valor
FROM usuarios
INNER JOIN pedidos ON usuarios.id = pedidos.usuario_id;
-- Resultado:
-- Ana Silva | Notebook | 3000.00
-- Bruno Costa | Mouse | 150.00
-- Ana Silva | Teclado | 450.00
-- (Usuário 5 não aparece pois não existe em usuarios)
LEFT JOIN:
-- Retorna TODOS os registros da tabela esquerda + correspondências da direita
SELECT usuarios.nome, pedidos.produto, pedidos.valor
FROM usuarios
LEFT JOIN pedidos ON usuarios.id = pedidos.usuario_id;
-- Resultado:
-- Ana Silva | Notebook | 3000.00
-- Ana Silva | Teclado | 450.00
-- Bruno Costa | Mouse | 150.00
-- Carlos Souza | NULL | NULL
-- (Carlos Souza aparece mesmo sem pedidos)
RIGHT JOIN:
-- Retorna TODOS os registros da tabela direita + correspondências da esquerda
SELECT usuarios.nome, pedidos.produto, pedidos.valor
FROM usuarios
RIGHT JOIN pedidos ON usuarios.id = pedidos.usuario_id;
-- Resultado:
-- Ana Silva | Notebook | 3000.00
-- Bruno Costa | Mouse | 150.00
-- Ana Silva | Teclado | 450.00
-- NULL | Monitor | 1200.00
-- (Monitor aparece mesmo que usuario_id 5 não exista em usuarios)
FULL OUTER JOIN:
-- Retorna TODOS os registros de AMBAS as tabelas
SELECT usuarios.nome, pedidos.produto, pedidos.valor
FROM usuarios
FULL OUTER JOIN pedidos ON usuarios.id = pedidos.usuario_id;
-- Resultado: Combina LEFT + RIGHT
-- Ana Silva | Notebook | 3000.00
-- Ana Silva | Teclado | 450.00
-- Bruno Costa | Mouse | 150.00
-- Carlos Souza | NULL | NULL
-- NULL | Monitor | 1200.00
CROSS JOIN:
-- Retorna o produto cartesiano (todas as combinações)
SELECT usuarios.nome, pedidos.produto
FROM usuarios
CROSS JOIN pedidos;
-- Resultado: 3 usuários × 4 pedidos = 12 linhas
-- Ana Silva | Notebook
-- Ana Silva | Mouse
-- Ana Silva | Teclado
-- Ana Silva | Monitor
-- Bruno Costa | Notebook
-- ... (todas as combinações)
6. Funções de Agregação
Funções de agregação calculam resumos a partir de múltiplos registros, como contagem, soma, média, máximo e mínimo.
Funções Principais:
-- COUNT - Contar registros
SELECT COUNT(*) AS total_usuarios FROM usuarios;
SELECT COUNT(id) AS usuarios_com_id FROM usuarios;
SELECT COUNT(DISTINCT cidade) AS cidades_unicas FROM usuarios;
-- SUM - Somar valores
SELECT SUM(valor) AS valor_total FROM pedidos;
SELECT SUM(valor) AS valor_total FROM pedidos WHERE status = 'concluído';
-- AVG - Calcular média
SELECT AVG(valor) AS valor_medio FROM pedidos;
SELECT AVG(idade) AS idade_media FROM usuarios;
-- MAX e MIN - Máximo e mínimo
SELECT MAX(valor) AS maior_pedido FROM pedidos;
SELECT MIN(valor) AS menor_pedido FROM pedidos;
SELECT MAX(criado_em) AS pedido_mais_recente FROM pedidos;
-- GROUP BY - Agrupar e agregar
SELECT usuario_id, COUNT(*) AS total_pedidos, SUM(valor) AS valor_total
FROM pedidos
GROUP BY usuario_id;
-- GROUP BY com HAVING (filtrar grupos)
SELECT usuario_id, COUNT(*) AS total_pedidos
FROM pedidos
GROUP BY usuario_id
HAVING COUNT(*) > 2; -- Apenas usuários com mais de 2 pedidos
-- Combinações úteis
SELECT
usuarios.nome,
COUNT(pedidos.id) AS total_pedidos,
SUM(pedidos.valor) AS valor_gasto,
AVG(pedidos.valor) AS valor_medio
FROM usuarios
LEFT JOIN pedidos ON usuarios.id = pedidos.usuario_id
GROUP BY usuarios.id, usuarios.nome;
7. Exemplo Prático Completo
Vamos criar um sistema de e-commerce com múltiplas tabelas relacionadas.
Schema do Banco de Dados:
-- Tabela: usuarios
CREATE TABLE usuarios (
id INT PRIMARY KEY AUTO_INCREMENT,
nome VARCHAR(100) NOT NULL,
email VARCHAR(100) NOT NULL UNIQUE,
senha VARCHAR(255) NOT NULL,
telefone VARCHAR(20),
cidade VARCHAR(100),
estado VARCHAR(2),
ativo BOOLEAN DEFAULT TRUE,
criado_em TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- Tabela: categorias
CREATE TABLE categorias (
id INT PRIMARY KEY AUTO_INCREMENT,
nome VARCHAR(100) NOT NULL UNIQUE,
descricao TEXT
);
-- Tabela: produtos
CREATE TABLE produtos (
id INT PRIMARY KEY AUTO_INCREMENT,
nome VARCHAR(150) NOT NULL,
descricao TEXT,
preco DECIMAL(10, 2) NOT NULL,
estoque INT DEFAULT 0,
categoria_id INT NOT NULL,
criado_em TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (categoria_id) REFERENCES categorias(id)
);
-- Tabela: pedidos
CREATE TABLE pedidos (
id INT PRIMARY KEY AUTO_INCREMENT,
usuario_id INT NOT NULL,
status ENUM('pendente', 'confirmado', 'enviado', 'entregue', 'cancelado') DEFAULT 'pendente',
valor_total DECIMAL(10, 2) NOT NULL,
data_pedido TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
data_entrega DATE,
FOREIGN KEY (usuario_id) REFERENCES usuarios(id)
);
-- Tabela: itens_pedido
CREATE TABLE itens_pedido (
id INT PRIMARY KEY AUTO_INCREMENT,
pedido_id INT NOT NULL,
produto_id INT NOT NULL,
quantidade INT NOT NULL,
preco_unitario DECIMAL(10, 2) NOT NULL,
subtotal DECIMAL(10, 2) GENERATED ALWAYS AS (quantidade * preco_unitario) STORED,
FOREIGN KEY (pedido_id) REFERENCES pedidos(id),
FOREIGN KEY (produto_id) REFERENCES produtos(id)
);
Inserindo Dados:
-- Inserir categorias
INSERT INTO categorias (nome, descricao) VALUES
('Eletrônicos', 'Produtos eletrônicos em geral'),
('Livros', 'Livros e e-books'),
('Roupas', 'Vestuário e acessórios');
-- Inserir produtos
INSERT INTO produtos (nome, descricao, preco, estoque, categoria_id) VALUES
('Notebook Dell', 'Intel i7, 16GB RAM', 3500.00, 10, 1),
('Mouse Logitech', 'Wireless, 1600 DPI', 150.00, 50, 1),
('Clean Code', 'Livro sobre código limpo', 89.90, 25, 2),
('Camiseta Básica', '100% algodão', 45.00, 100, 3);
-- Inserir usuários
INSERT INTO usuarios (nome, email, senha, telefone, cidade, estado) VALUES
('Ana Silva', 'ana@example.com', 'senha123', '11987654321', 'São Paulo', 'SP'),
('Bruno Costa', 'bruno@example.com', 'senha456', '21987654321', 'Rio de Janeiro', 'RJ'),
('Diana Lima', 'diana@example.com', 'senha789', '85987654321', 'Fortaleza', 'CE');
-- Inserir pedido
INSERT INTO pedidos (usuario_id, status, valor_total) VALUES
(1, 'confirmado', 3650.00);
-- Inserir itens do pedido
INSERT INTO itens_pedido (pedido_id, produto_id, quantidade, preco_unitario) VALUES
(1, 1, 1, 3500.00),
(1, 2, 1, 150.00);
Consultas Complexas:
-- 1️⃣ Listar pedidos com detalhes do usuário
SELECT
p.id,
u.nome AS usuario,
u.email,
p.status,
p.valor_total,
p.data_pedido
FROM pedidos p
JOIN usuarios u ON p.usuario_id = u.id
ORDER BY p.data_pedido DESC;
---
-- 2️⃣ Listar itens de um pedido específico
SELECT
ip.id,
pro.nome AS produto,
ip.quantidade,
ip.preco_unitario,
ip.subtotal
FROM itens_pedido ip
JOIN produtos pro ON ip.produto_id = pro.id
WHERE ip.pedido_id = 1
ORDER BY ip.id;
---
-- 3️⃣ Calcular total gasto por usuário
SELECT
u.nome,
COUNT(p.id) AS total_pedidos,
SUM(p.valor_total) AS valor_total_gasto,
AVG(p.valor_total) AS ticket_medio,
MAX(p.data_pedido) AS ultimo_pedido
FROM usuarios u
LEFT JOIN pedidos p ON u.id = p.usuario_id
GROUP BY u.id, u.nome
ORDER BY valor_total_gasto DESC;
---
-- 4️⃣ Produtos mais vendidos
SELECT
pro.nome,
cat.nome AS categoria,
SUM(ip.quantidade) AS quantidade_vendida,
SUM(ip.subtotal) AS valor_total
FROM itens_pedido ip
JOIN produtos pro ON ip.produto_id = pro.id
JOIN categorias cat ON pro.categoria_id = cat.id
GROUP BY pro.id, pro.nome, cat.nome
ORDER BY quantidade_vendida DESC;
---
-- 5️⃣ Usuários que não fizeram pedidos
SELECT u.* FROM usuarios u
LEFT JOIN pedidos p ON u.id = p.usuario_id
WHERE p.id IS NULL;
---
-- 6️⃣ Relatório de vendas por categoria
SELECT
cat.nome AS categoria,
COUNT(DISTINCT p.id) AS total_pedidos,
COUNT(ip.id) AS total_itens,
SUM(ip.subtotal) AS valor_total
FROM categorias cat
LEFT JOIN produtos pro ON cat.id = pro.categoria_id
LEFT JOIN itens_pedido ip ON pro.id = ip.produto_id
LEFT JOIN pedidos p ON ip.pedido_id = p.id
GROUP BY cat.id, cat.nome
ORDER BY valor_total DESC;
8. Boas Práticas em SQL
✓ Use Índices
- Crie índices em colunas frequentemente consultadas
- Indexe chaves primárias e estrangeiras automaticamente
- Use índices com moderação para não impactar INSERT/UPDATE
✓ Normalização de Dados
- Organize dados em tabelas relacionadas (evite redundância)
- Use de 1ª até 3ª forma normal (normalização adequada)
- Reduza espaço de armazenamento e erros de inconsistência
✓ Segurança - Prepared Statements
- Use prepared statements para evitar SQL Injection
- Nunca concatene strings em SQL
- Valide e sanitize entrada do usuário
✓ Performance e Otimização
- Use EXPLAIN para analisar plano de execução
- Evite SELECT * (especifique colunas necessárias)
- Limite resultados com LIMIT quando apropriado
9. Exercícios Práticos
Exercício 1: Banco de Dados de Biblioteca
Crie um banco de dados com tabelas: autores, livros, empréstimos e usuários. Implemente consultas para listar livros por autor, histórico de empréstimos e usuários com mais atrasos.
Exercício 2: Relatórios Mensais
Desenvolva queries para gerar relatórios de vendas mensais, produtos mais lucrativos, e comparação de desempenho entre períodos.
Exercício 3: Otimização de Queries
Pegue um conjunto de queries ineficientes e otimize-as usando índices, EXPLAIN e reformulação de lógica SQL.
Exercício 4: Integridade de Dados
Implemente constraints, triggers e validações para garantir a integridade referencial e dados consistentes.
Exercício 5: Backup e Restauração
Pratique backup e restauração de bancos de dados, versionamento de schema e migrations.
Conclusão
SQL é a linguagem fundamental para qualquer desenvolvedor que trabalhe com dados. Dominar SQL é essencial para criar aplicações eficientes, seguras e escaláveis.
Pontos-chave para lembrar:
- CRUD são as operações básicas (Create, Read, Update, Delete)
- JOINs combinam dados de múltiplas tabelas de forma poderosa
- Agregações calculam resumos a partir de dados
- Índices e normalização otimizam desempenho
- Segurança (prepared statements) protege contra SQL Injection
Pratique criando seus próprios bancos de dados, começando simples e evoluindo para estruturas mais complexas. SQL é uma habilidade que se aprimora com a prática! 🚀