Fala Devs! Beleza?
Hoje vamos desmistificar um daqueles conceitos que todo mundo que trabalha com banco de dados acha que sabe, mas que pode gerar uns bugs bem chatos se não for compreendido a fundo. Sabe aquela query SQL que você escreve bonitinha, começando com SELECT? Então… E se eu te disser que o banco de dados nem começa por ele?
Pois é! A ordem que a gente escreve uma consulta no nosso editor não é, nem de longe, a mesma ordem que o motor do banco de dados (como o nosso querido PostgreSQL) a executa. Entender essa “ordem real” não é só uma curiosidade, é uma necessidade para otimizar consultas, evitar erros e realmente dominar o SQL.
Bora mergulhar nessa lógica e entender de uma vez por todas como o banco de dados pensa?
A Ordem Escrita vs. A Ordem de Execução Real
Quando escrevemos uma query, geralmente seguimos uma estrutura padrão, quase como uma receita de bolo:
SELECT
coluna1,
COUNT(coluna2)
FROM
tabela
WHERE
condicao = TRUE
GROUP BY
coluna1
HAVING
COUNT(coluna2) > 10
ORDER BY
coluna1;
Visualmente, o SELECT vem primeiro, certo? Nossa mente lê de cima para baixo. Mas o SGBD (Sistema de Gerenciamento de Banco de Dados) tem um plano de execução lógico que segue uma sequência completamente diferente. Pense nisso como o compilador do seu código: ele não lê e executa linha por linha na ordem que você escreveu, mas interpreta, otimiza e depois executa.
A ordem real de execução é pensada para ser o mais eficiente possível, filtrando os dados o quanto antes para trabalhar com um conjunto menor de informações nas etapas seguintes.
A Ordem Lógica de Execução do SQL
Então, qual é a mágica por trás do pano? O PostgreSQL e a maioria dos bancos de dados relacionais seguem uma sequência parecida com esta:
FROM e JOINs
WHERE
GROUP BY
HAVING
SELECT
ORDER BY
Vamos detalhar cada passo para ficar bem claro.
1. FROM e JOINs: A Origem dos Dados
A primeira coisa que o banco de dados faz é identificar de onde os dados virão. Ele olha para a cláusula FROM para saber a tabela principal e depois resolve todas as cláusulas JOIN para combinar os dados de múltiplas tabelas. Nesta fase, ele está montando um grande “pacote” de dados brutos, com todas as linhas e colunas das tabelas envolvidas.
2. WHERE: A Primeira Filtragem
Com o pacotão de dados montado, o próximo passo é aplicar o primeiro filtro. A cláusula WHERE entra em ação para descartar todas as linhas que não atendem às condições especificadas. É aqui que a mágica da otimização começa: quanto mais linhas você eliminar aqui, menos trabalho o banco terá nas próximas etapas.
Ponto importante: O WHERE filtra linhas antes de qualquer agrupamento. Por isso, você não pode usar funções de agregação (como COUNT(), SUM(), AVG()) aqui.
3. GROUP BY: Agrupando os Dados
Depois que os dados foram filtrados, a cláusula GROUP BY entra em cena para agrupar as linhas que têm os mesmos valores nas colunas especificadas. O resultado disso é que, em vez de várias linhas individuais, você passa a ter “grupos” de linhas.
4. HAVING: A Segunda Filtragem (Pós-Agrupamento)
Se o WHERE filtra linhas, o HAVING filtra… grupos! Essa é a principal diferença. A cláusula HAVING é aplicada depois que os dados já foram agrupados pelo GROUP BY. É por isso que aqui você pode usar funções de agregação. Por exemplo, “me traga apenas os grupos cujo total de vendas (SUM(vendas)) seja maior que 1000”.
5. SELECT: A Seleção Final das Colunas
Finalmente, chegamos nele! Só depois de toda a filtragem e agrupamento, o SELECT é processado. Ele vai olhar para o conjunto de dados final (já filtrado e agrupado) e selecionar as colunas que você pediu. É aqui também que apelidos (AS) para colunas são finalmente reconhecidos e funções como COUNT() ou SUM() são efetivamente calculadas e exibidas.
6. ORDER BY: A Cereja do Bolo
Com o resultado final praticamente pronto, a última etapa é a ordenação. A cláusula ORDER BY pega o conjunto de dados selecionado e o organiza de forma ascendente (ASC) ou descendente (DESC).
Demonstração Prática com PostgreSQL
Bora ver isso na prática! Imagine que temos duas tabelas em nosso banco de dados PostgreSQL: usuarios e pedidos.
Tabela usuarios:
| id | nome | estado |
|---|---|---|
| 1 | Ana | SP |
| 2 | Bruno | RJ |
| 3 | Carla | SP |
| 4 | Daniel | MG |
Tabela pedidos:
| id | usuario_id | valor | status |
|---|---|---|---|
| 101 | 1 | 150.00 | ‘pago’ |
| 102 | 2 | 80.00 | ‘pago’ |
| 103 | 1 | 200.00 | ‘pago’ |
| 104 | 3 | 300.00 | ‘pago’ |
| 105 | 1 | 50.00 | ‘cancelado’ |
| 106 | 4 | 120.00 | ‘pago’ |
Agora, vamos executar a seguinte query:
SELECT
u.estado,
COUNT(p.id) AS total_pedidos,
SUM(p.valor) AS faturamento_total
FROM
usuarios AS u
JOIN
pedidos AS p ON u.id = p.usuario_id
WHERE
p.status = 'pago'
GROUP BY
u.estado
HAVING
SUM(p.valor) >= 150
ORDER BY
faturamento_total DESC;SQLComo o PostgreSQL Executa essa Query?
FROMeJOIN: Primeiro, ele combinausuarioscompedidosusando a condiçãou.id = p.usuario_id. O resultado é uma tabela temporária gigante com todos os dados combinados.WHERE: Em seguida, ele filtra essa tabela gigante, mantendo apenas as linhas ondep.status = 'pago'. O pedido deid = 105(da Ana) é descartado aqui.GROUP BY: Agora, ele agrupa as linhas restantes pela colunau.estado. Teremos um grupo para ‘SP’, um para ‘RJ’ e um para ‘MG’.HAVING: O próximo passo é filtrar esses grupos. A condição éSUM(p.valor) >= 150.- Grupo ‘RJ’ (Bruno): Soma do valor = 80.00. É descartado.
- Grupo ‘MG’ (Daniel): Soma do valor = 120.00. É descartado.
- Grupo ‘SP’ (Ana e Carla): Soma do valor = 150.00 + 200 + 300.00 = 650.00. É mantido.
SELECT: Agora sim! Com apenas o grupo ‘SP’ restante, oSELECTé executado. Ele pega a colunau.estado(‘SP’), calcula oCOUNT(p.id)(que dá 3) e oSUM(p.valor)(que dá 650.00), aplicando os apelidostotal_pedidosefaturamento_total.ORDER BY: Como só sobrou uma linha, a ordenação não fará muita diferença visual, mas se houvesse mais resultados, eles seriam ordenados pelofaturamento_totalem ordem decrescente.
Resultado Final:
| estado | total_pedidos | faturamento_total |
|---|---|---|
| SP | 3 | 650.00 |
Viu só? Entender essa ordem evita erros comuns, como tentar usar um alias de SELECT na cláusula WHERE. Não funciona porque o WHERE é executado antes do SELECT!
Resumindo a Jornada
Para não esquecer mais, aqui vai um resumo da ordem de execução:
- ✅
FROM/JOIN: Junta as tabelas. - ✅
WHERE: Filtra as linhas brutas. - ✅
GROUP BY: Agrupa as linhas filtradas. - ✅
HAVING: Filtra os grupos criados. - ✅
SELECT: Escolhe as colunas e calcula as funções. - ✅
ORDER BY: Ordena o resultado final.
Entender essa sequência é fundamental para quem trabalha com banco de dados, seja no backend com Node.js ou até mesmo analisando dados para um app em Flutter.
E aí, essa ordem de execução já te causou alguma dor de cabeça? Ou você tem alguma dica extra sobre otimização de queries? Deixa seu comentário aí embaixo!
Até a próxima! 🚀