JAVA Learning

Hub de Conteúdo

📊 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:

sql
-- 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:

sql
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:

sql
-- 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:

sql
-- 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:

sql
-- 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:

sql
-- 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:

sql
-- 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:

sql
-- 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:

sql
-- 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:

sql
-- 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:

sql
-- 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:

sql
-- 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:

sql
-- 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:

sql
-- 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:

sql
-- 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:

sql
-- 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! 🚀