No MySQL, a ordem de execução de uma consulta SELECT não corresponde à ordem sintática. Este artigo detalha o processamento interno, ilusttrado com tabelas virtuais durante a execução.
Ordem Sintática das Palavras-chave SELECT
SELECT DISTINCT <lista_colunas>
FROM <tabela_esquerda>
<tipo_juncao> JOIN <tabela_direita>
ON <condicao_juncao>
WHERE <condicao_filtro>
GROUP BY <lista_agrupamento>
HAVING <condicao_agrupamento>
ORDER BY <condicao_ordenacao>
LIMIT <numero_linhas>
Ordem de Execução das Palavras-chave SELECT
(7) SELECT
(8) DISTINCT <lista_colunas>
(1) FROM <tabela_esquerda>
(3) <tipo_juncao> JOIN <tabela_direita>
(2) ON <condicao_juncao>
(4) WHERE <condicao_filtro>
(5) GROUP BY <lista_agrupamento>
(6) HAVING <condicao_agrupamento>
(9) ORDER BY <condicao_ordenacao>
(10) LIMIT <numero_linhas>
Preparação do Ambiente de Teste
Criar o banco de dados ExemploDB.
CREATE DATABASE ExemploDB;
Criar as tabelas cleintes e pedidos.
CREATE TABLE clientes (
id_cliente VARCHAR(10) NOT NULL,
cidade VARCHAR(20) NOT NULL,
PRIMARY KEY(id_cliente)
) ENGINE=INNODB DEFAULT CHARSET=UTF8;
CREATE TABLE pedidos (
id_pedido INT NOT NULL AUTO_INCREMENT,
id_cliente VARCHAR(10),
PRIMARY KEY(id_pedido)
) ENGINE=INNODB DEFAULT CHARSET=UTF8;
Inserir dados de teste.
INSERT INTO clientes(id_cliente, cidade) VALUES('A100', 'São Paulo');
INSERT INTO clientes(id_cliente, cidade) VALUES('B200', 'Rio de Janeiro');
INSERT INTO clientes(id_cliente, cidade) VALUES('C300', 'São Paulo');
INSERT INTO clientes(id_cliente, cidade) VALUES('D400', 'Belo Horizonte');
INSERT INTO pedidos(id_cliente) VALUES('A100');
INSERT INTO pedidos(id_cliente) VALUES('A100');
INSERT INTO pedidos(id_cliente) VALUES('B200');
INSERT INTO pedidos(id_cliente) VALUES('B200');
INSERT INTO pedidos(id_cliente) VALUES('B200');
INSERT INTO pedidos(id_cliente) VALUES('C300');
INSERT INTO pedidos(id_cliente) VALUES(NULL);
O conteúdo resultante das tabelas será:
mysql> SELECT * FROM clientes;
+------------+-----------------+
| id_cliente | cidade |
+------------+-----------------+
| A100 | São Paulo |
| B200 | Rio de Janeiro |
| C300 | São Paulo |
| D400 | Belo Horizonte |
+------------+-----------------+
4 rows in set (0.00 sec)
mysql> SELECT * FROM pedidos;
+------------+------------+
| id_pedido | id_cliente |
+------------+------------+
| 1 | A100 |
| 2 | A100 |
| 3 | B200 |
| 4 | B200 |
| 5 | B200 |
| 6 | C300 |
| 7 | NULL |
+------------+------------+
7 rows in set (0.00 sec)
Consulta SQL de Teste
-- Encontrar clientes de São Paulo com menos de 2 pedidos.
SELECT c.id_cliente, COUNT(p.id_pedido) AS total_pedidos
FROM clientes AS c
LEFT JOIN pedidos AS p
ON c.id_cliente = p.id_cliente
WHERE c.cidade = 'São Paulo'
GROUP BY c.id_cliente
HAVING COUNT(p.id_pedido) < 2
ORDER BY total_pedidos DESC;
Análise do Processo de Execução
Durante a execução, o MySQL constrói tabelas virtuais intermediárias. Acompanhe cada etapa.
Execução da Cláusula FROM
A etapa inicial calcula o produto catresiano entre as tabelas clientes e pedidos, gerando a tabela virtual VT1. Para este exemplo, VT1 contém 28 linhas (4 × 7). A estrutura inclui todas as colunas de ambas as tabelas.
Filtro com ON
Aplica-se a condição ON (id_cliente coincide), filtrando linhas inconsistentes. O resultado é a tabela virtual VT2 com apenas linhas correspondentes.
Inclusão de Linhas Externas (LEFT JOIN)
Como a junção é LEFT JOIN, adicionam-se linhas da tabela esquerda sem correspondência na direita. A tabela virtual VT3 agora inclui o cliente D400 com valores NULL para pedidos.
Filtro com WHERE
Aplica-se a condição WHERE (cidade = 'São Paulo'), eliminando linhas de outras cidades. A tabela virtual VT4 contém apenas clientes de São Paulo.
Agrupamento com GROUP BY
Agrupa-se por id_cliente, consolidando linhas por cliente. A tabela virtual VT5 mostra uma linha por grupo, com os primeiros valores de cada grupo.
Filtro com HAVING
Aplica-se a condição HAVING (COUNT(p.id_pedido) < 2) sobre os grupos. A tabela virtual VT6 mantém apenas grupos que satisfazem essa condição.
Projeção com SELECT
Selecionam-se as colunas especificadas (id_cliente e COUNT como total_pedidos). A tabela virtual VT7 apresenta os dados no formato desejado.
Ordenação com ORDER BY
Ordena-se por total_pedidos em ordem decrescente. A tabela virtual VT8 reflete essa ordenação.
Execução de LIMIT
Se presente, a cláusula LIMIT seleciona um subconjunto de linhas de VT8. Neste exemplo, sem LIMIT, o resultado final é VT8.