Ordem de Execução de Consultas SELECT no MySQL

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.

Tags: MySQL SQL Execution Order Query Processing Database Internals Virtual Tables

Publicado em 6-26 18:15