SQL Avançado: Queries para Diagnóstico e Análise
Um guia prático com exemplos interativos de queries complexas para resolver problemas reais de negócio, como análise de churn, relatórios gerenciais e auditoria de dados.
Se o nosso primeiro artigo de SQL foi o "cinto de utilidades" com as ferramentas essenciais, este é o seu "dicionário avançado". Aqui, vamos explorar cenários mais complexos que QAs e BAs enfrentam no dia a dia, especialmente em ambientes com grande volume de dados, como em projetos de telecomunicações ou do setor bancário.
Cada exemplo abaixo foi projetado para resolver um problema de negócio específico, transformando dados brutos em insights valiosos.
Navegue pelos Comandos Avançados:
1. LEFT JOIN: Encontrando Dados "Órfãos"
Enquanto o JOIN tradicional só mostra a combinação perfeita entre duas tabelas, o LEFT JOIN é mais inclusivo. Ele retorna todos os registros da tabela da esquerda, e preenche com NULL (vazio) caso não encontre um par na tabela da direita. É a query perfeita para responder: "Quais clientes nunca fizeram um pedido?".
SELECT c.nome, p.id_pedido FROM clientes c LEFT JOIN pedidos p ON c.id = p.id_cliente;Tabelas Originais:
| clientes (esquerda) | |
|---|---|
| id | nome |
| 1 | Ana |
| 2 | Bruno |
| 3 | Carlos |
| pedidos (direita) | |
|---|---|
| id_pedido | id_cliente |
| 101 | 2 |
| 102 | 1 |
2. HAVING: O Filtro para Grupos
Se o WHERE filtra linhas individuais antes da agregação, o HAVING filtra os resultados de um grupo, depois que a agregação já aconteceu. Ele é sempre usado com GROUP BY. A pergunta muda de "quais clientes são de SP?" (WHERE) para "quais estados têm mais de 1 cliente?" (HAVING).
SELECT estado, COUNT(*) FROM clientes GROUP BY estado HAVING COUNT(*) > 1;Tabela Agrupada (Antes do HAVING):
| estado | COUNT(*) |
|---|---|
| SP | 2 |
| RJ | 2 |
| MG | 1 |
3. CASE: O "Se-Então-Senão" do SQL
A instrução CASE é a sua ferramenta para criar lógicas condicionais dentro de uma query. Ela permite criar uma nova coluna baseada em regras. Por exemplo: "SE o preço for menor que 50, ENTÃO chame de 'Promoção', SENÃO, chame de 'Preço Normal'".
SELECT nome, preco, CASE WHEN preco < 50 THEN 'Promoção' ELSE 'Preço Normal' END AS categoria FROM produtos;| nome | preco |
|---|---|
| Caneta | 19.99 |
| Teclado | 99.90 |
| Mouse | 49.50 |
4. Window Functions: Análise Sequencial
As Funções de Janela são um superpoder do SQL. Elas realizam cálculos em um conjunto de linhas (uma "janela") relacionadas à linha atual. Diferente do GROUP BY que agrega tudo em uma única linha, as Window Functions retornam um valor para cada linha. São perfeitas para criar rankings, calcular médias móveis ou totais acumulados.
SELECT produto, vendas, RANK() OVER (ORDER BY vendas DESC) AS ranking FROM vendas_mensais;| produto | vendas |
|---|---|
| Produto A | 9500 |
| Produto B | 7200 |
| Produto C | 9500 |
| Produto D | 8100 |
5. CTEs: Organizando Queries Complexas
As Expressões de Tabela Comuns (CTEs), iniciadas com a cláusula WITH, são como criar uma tabela temporária e nomeada que existe apenas durante a execução da query. Elas são essenciais para quebrar consultas longas e complexas em blocos lógicos e legíveis, evitando subqueries aninhadas e confusas.
WITH VendasPorRegiao AS (SELECT regiao, SUM(vendas) AS total_vendasFROM filiais GROUP BY regiao)
SELECT * FROM VendasPorRegiao WHERE total_vendas > 50000;Tabela Original (filiais):
| filial_id | regiao | vendas |
|---|---|---|
| 1 | Sudeste | 30000 |
| 2 | Nordeste | 25000 |
| 3 | Sudeste | 40000 |
| 4 | Sul | 45000 |
| 5 | Nordeste | 35000 |
6. Subqueries: Uma Query Dentro de Outra
Uma Subquery (ou subconsulta) é uma instrução SELECT aninhada dentro de outra query. Ela é executada primeiro, e seu resultado é usado como filtro para a consulta principal. É uma alternativa poderosa às CTEs para resolver problemas em etapas, como: "Primeiro, encontre o ID da categoria 'Eletrônicos', depois, me mostre todos os produtos dessa categoria".
SELECT * FROM produtos WHERE id_categoria IN (SELECT id FROM categorias WHERE nome = 'Eletrônicos');
Tabelas Originais:
| produtos | ||
|---|---|---|
| id | nome | id_categoria |
| 10 | Teclado | 1 |
| 11 | Caneta | 2 |
| 12 | Mouse | 1 |
| categorias | |
|---|---|
| id | nome |
| 1 | Eletrônicos |
| 2 | Papelaria |
7. UNION: Combinando Listas
O operador UNION é usado para combinar o resultado de duas ou mais instruções SELECT em uma única lista. É como pegar duas listas de convidados e juntá-las em uma só, removendo automaticamente os nomes duplicados. É perfeito para consolidar dados de tabelas diferentes que possuem a mesma estrutura de colunas.
SELECT nome, email FROM clientes_ativosUNIONSELECT nome, email FROM clientes_novos;Tabelas Originais:
| clientes_ativos | |
|---|---|
| nome | |
| Ana | ana@email.com |
| Bruno | bruno@email.com |
| clientes_novos | |
|---|---|
| nome | |
| Carlos | carlos@email.com |
| Ana | ana@email.com |
8. Self-Join: Relacionando uma Tabela com Ela Mesma
Um SELF JOIN é uma técnica onde você junta uma tabela com ela mesma, tratando-a como se fossem duas tabelas distintas (usando apelidos, como e para empregados e m para gerentes). É a solução perfeita para consultar dados hierárquicos, como encontrar o nome do gerente de cada funcionário.
SELECT e.nome AS funcionario, m.nome AS gerenteFROM funcionarios e JOIN funcionarios m ON e.id_gerente = m.id;Tabela Original (funcionarios):
| id | nome | id_gerente |
|---|---|---|
| 1 | Carlos | NULL |
| 2 | Ana | 1 |
| 3 | Bruno | 1 |
9. Funções de Data: Viajando no Tempo
Manipular datas é uma tarefa diária em análise de dados. Funções como NOW(), DATE(), e operadores de intervalo (INTERVAL) permitem filtrar registros em períodos específicos, como "todos os usuários que se cadastraram nos últimos 30 dias", uma query essencial para analisar o crescimento recente.
SELECT nome, data_cadastro FROM usuariosWHERE data_cadastro >= NOW() - INTERVAL '30 day';Tabela Original (usuarios):
| nome | data_cadastro |
|---|