SQL ANY e ALL

"Este produto é mais caro que todos os da Papelaria?" "Este brinquedo é mais barato que algum livro?" São perguntas que comparam um valor com uma lista inteira. O SQL padrão tem duas palavras para isso: ALL ("todos") e ANY ("algum", também escrito SOME). Elas existem no MySQL, no PostgreSQL e no SQL Server, e você vai encontrá-las em muito código. Mas há um detalhe importante: o SQLite não tem ANY nem ALL. Nesta aula você aprende o que elas significam, vê o erro real que o SQLite dá e aprende a escrever a mesma pergunta de um jeito que funciona em todos os bancos.

Você vai aprender
  • O que significa > ALL (…) e > ANY (…), com uma comparação do dia a dia.
  • O erro real que o SQLite dá quando encontra ANY ou ALL numa comparação.
  • Os equivalentes que rodam no SQLite: MAX e MIN numa subconsulta, IN, NOT IN, EXISTS e NOT EXISTS.
  • O caso traiçoeiro da lista vazia, em que MAX e ALL respondem diferente.
  • Uma tabela para traduzir qualquer ANY/ALL que você encontrar.

Esta aula usa subconsultas: se ainda não leu, veja Subconsultas e EXISTS. Os trechos com ANY/ALL marcados como ilustrativos são do MySQL, do PostgreSQL e do SQL Server e não rodam aqui; todos os outros rodam de verdade no seu navegador.


A ideia: mais alto que todos, mais alto que algum

Pense na fila da escola. O Pedro é mais alto que todos da turma B? Para isso ser verdade, basta ele ser mais alto que o mais alto da turma B. E ele é mais alto que algum aluno da turma B? Basta ser mais alto que o mais baixo de lá.

PerguntaNo SQL padrãoBasta comparar com…
Mais alto que todosaltura > ALL (alturas da turma B)o maior: MAX
Mais alto que algumaltura > ANY (alturas da turma B)o menor: MIN
Mais baixo que todosaltura < ALL (…)o menor: MIN
Mais baixo que algumaltura < ANY (…)o maior: MAX

Essa tabela é o segredo da aula: quase todo ANY/ALL com > ou < vira uma comparação com MAX ou MIN, que o SQLite tem.


1. Como se escreve nos outros bancos

A Ana quer os produtos mais caros que todos os produtos da Papelaria (categoria 1). No MySQL, no PostgreSQL ou no SQL Server, ela escreveria assim:

SELECT nome, preco
FROM produtos
WHERE preco > ALL (SELECT preco FROM produtos WHERE categoria_id = 1);
preco > ALL ( … )
"O preço é maior que cada um dos valores da subconsulta." O ALL vem sempre logo depois de um operador de comparação (>, <, =, <>, >=, <=).
(SELECT preco FROM produtos WHERE categoria_id = 1)
A subconsulta: uma lista de uma coluna, os 6 preços da Papelaria.

E para "mais caro que algum", troca-se ALL por ANY (ou pelo sinônimo SOME):

SELECT nome, preco
FROM produtos
WHERE preco > ANY (SELECT preco FROM produtos WHERE categoria_id = 1);

Segundo os manuais desses bancos: ALL é verdadeiro quando a comparação é verdadeira para todos os valores da subconsulta; ANY é verdadeiro quando ela é verdadeira para pelo menos um. Os manuais do MySQL e do PostgreSQL também dizem que IN é o mesmo que = ANY, e que NOT IN é o mesmo que <> ALL.


2. O que o SQLite faz com ANY e ALL

Agora a mesma consulta no editor do guia, que usa o SQLite de verdade:

SELECT nome, preco
FROM produtos
WHERE preco > ALL (SELECT preco FROM produtos WHERE categoria_id = 1);

A mensagem: near "ALL": syntax error, "erro de sintaxe perto de ALL". O SQLite conhece a palavra ALL (de UNION ALL e de SELECT ALL), mas não depois de um >. E com ANY:

SELECT nome, preco
FROM produtos
WHERE preco > ANY (SELECT preco FROM produtos WHERE categoria_id = 1);

A mensagem: near "SELECT": syntax error. Repare que o erro agora é "perto de SELECT", e não de ANY. Para o SQLite, ANY não é palavra especial nenhuma: ele leu ANY ( como a chamada de uma função chamada ANY, e uma função não pode ter um SELECT solto como argumento. Com SOME acontece o mesmo. Se você puser uma lista de valores no lugar da subconsulta, o SQLite confirma a suspeita:

SELECT nome
FROM produtos
WHERE preco = ANY (12.35, 2.79);

no such function: ANY, "não existe a função ANY". No terminal do sqlite3, as mensagens vêm com o prefixo Parse error e uma seta mostrando onde o SQLite parou de entender:

$ sqlite3 loja.db
SQLite version 3.50.4 2025-07-30 19:33:53
Enter ".help" for usage hints.
sqlite> SELECT nome FROM produtos WHERE preco > ALL (SELECT preco FROM produtos WHERE categoria_id = 1);
Parse error: near "ALL": syntax error
  SELECT nome FROM produtos WHERE preco > ALL (SELECT preco FROM produtos WHERE
                            error here ---^
sqlite> SELECT nome FROM produtos WHERE preco > ANY (SELECT preco FROM produtos WHERE categoria_id = 1);
Parse error: near "SELECT": syntax error
  SELECT nome FROM produtos WHERE preco > ANY (SELECT preco FROM produtos WHERE
                                 error here ---^

A seta (error here ---^, "o erro está aqui") aponta para a palavra onde o SQLite desistiu: o ALL na primeira, o SELECT na segunda.


3. > ALL vira > MAX

Volte à fila da escola: ser mais caro que todos os produtos da Papelaria é o mesmo que ser mais caro que o mais caro deles. O mais caro da Papelaria:

SELECT MAX(preco) FROM produtos WHERE categoria_id = 1;

A mochila, a 89.99. Agora a pergunta da Ana, que roda no SQLite (e em qualquer outro banco):

SELECT nome, preco
FROM produtos
WHERE preco > (SELECT MAX(preco) FROM produtos WHERE categoria_id = 1)
ORDER BY preco;

Cinco produtos, todos acima de 89.99: é exatamente o que > ALL devolveria. A subconsulta virou escalar (um valor só), do tipo que você viu em Subconsultas.

Missão: a Ana copiou de um tutorial de MySQL uma consulta que acha quem ganha mais que todos os vendedores e vendedoras. No SQLite, ela dá erro. Rode para ver, e depois reescreva sem ALL, comparando com o maior salário deles.

SELECT nome, salario
FROM funcionarios
WHERE salario > ALL (SELECT salario FROM funcionarios WHERE cargo LIKE 'Vendedor%');
SELECT nome, salario
FROM funcionarios
WHERE salario > (SELECT MAX(salario) FROM funcionarios WHERE cargo LIKE 'Vendedor%');

4. < ANY vira < MAX

Outra pergunta: quais brinquedos (categoria 5) são mais baratos que algum livro (categoria 4)? Na forma padrão, ilustrativa:

SELECT nome, preco
FROM produtos
WHERE categoria_id = 5
  AND preco < ANY (SELECT preco FROM produtos WHERE categoria_id = 4);

Ser mais barato que algum livro é o mesmo que ser mais barato que o livro mais caro. Se você é mais barato que o mais caro, pelo menos esse você venceu:

SELECT nome, preco
FROM produtos
WHERE categoria_id = 5
  AND preco < (SELECT MAX(preco) FROM produtos WHERE categoria_id = 4);

Quatro brinquedos. Só o Jogo de Tabuleiro (79.99) ficou de fora, porque o livro mais caro, o Atlas, custa 58.0. E se a pergunta fosse "mais barato que todos os livros" (< ALL)? Aí a comparação seria com o MIN, o livro mais barato (25.45).


5. = ANY é IN, <> ALL é NOT IN

Duas combinações têm tradução direta, e você já as conhece:

SQL padrão (ilustrativo)No SQLiteLê-se
id = ANY (SELECT produto_id FROM itens_pedido)id IN (SELECT produto_id FROM itens_pedido)"igual a algum da lista"
id <> ALL (SELECT produto_id FROM itens_pedido)id NOT IN (SELECT produto_id FROM itens_pedido)"diferente de todos da lista"

Os produtos que nunca foram vendidos, do jeito que roda aqui:

SELECT nome
FROM produtos
WHERE id NOT IN (SELECT produto_id FROM itens_pedido);

Lembre a armadilha da aula Subconsultas: se a lista tiver um NULL, o NOT IN (e o <> ALL, que é a mesma coisa) não devolve nada.


6. A tradução que nunca falha: EXISTS e NOT EXISTS

O MAX/MIN resolve quase sempre, mas tem um caso em que ele responde diferente do ALL: quando a subconsulta não devolve nenhuma linha. Os manuais do MySQL e do PostgreSQL dizem que, com a lista vazia, ALL é verdadeiro (não existe nenhum valor que desminta a comparação) e ANY é falso (não existe nenhum valor que a confirme).

Veja o MAX com uma categoria que não existe (a 99). A pergunta seria "brinquedos mais caros que todos os produtos da categoria 99":

SELECT nome, preco
FROM produtos
WHERE categoria_id = 5
  AND preco > (SELECT MAX(preco) FROM produtos WHERE categoria_id = 99);

Nenhuma linha. Sem produtos, o MAX vale NULL, e nenhuma comparação com NULL é verdadeira. Já o > ALL devolveria os 5 brinquedos. Para ter exatamente o comportamento do ALL, escreva a pergunta ao contrário com NOT EXISTS: "não existe nenhum produto da categoria 99 com preço maior ou igual ao meu":

SELECT b.nome, b.preco
FROM produtos AS b
WHERE b.categoria_id = 5
  AND NOT EXISTS (SELECT 1
                  FROM produtos AS x
                  WHERE x.categoria_id = 99
                    AND x.preco >= b.preco);

Os 5 brinquedos, como o ALL faria. Com uma categoria que existe, a forma NOT EXISTS dá o mesmo que o MAX. Os produtos mais caros que todos da Papelaria, da seção 3:

SELECT p.nome, p.preco
FROM produtos AS p
WHERE NOT EXISTS (SELECT 1
                  FROM produtos AS pap
                  WHERE pap.categoria_id = 1
                    AND pap.preco >= p.preco)
ORDER BY p.preco;

Os mesmos 5 produtos. E o ANY vira EXISTS: "existe algum livro mais caro que este brinquedo":

SELECT b.nome, b.preco
FROM produtos AS b
WHERE b.categoria_id = 5
  AND EXISTS (SELECT 1
              FROM produtos AS l
              WHERE l.categoria_id = 4
                AND l.preco > b.preco);

Os mesmos 4 brinquedos da seção 4.

Como inverter a comparação no NOT EXISTS

x > ALL (lista) quer dizer "não existe na lista um valor que não seja menor que x", ou seja, nenhum valor >= x. Por isso o > virou >= dentro do NOT EXISTS: é a comparação oposta. Do mesmo jeito, x < ALL vira NOT EXISTS (… valor <= x).


7. A tabela de tradução

Encontrou um ANY/ALL num código ou tutorial? Troque assim. Na coluna do meio, a forma curta, que serve quando a lista nunca é vazia; na da direita, a que se comporta igual ao padrão até com a lista vazia.

SQL padrão (não roda no SQLite)Com MAX/MIN, INCom EXISTS
x > ALL (SELECT v …)x > (SELECT MAX(v) …)NOT EXISTS (SELECT 1 … WHERE v >= x)
x < ALL (SELECT v …)x < (SELECT MIN(v) …)NOT EXISTS (SELECT 1 … WHERE v <= x)
x > ANY (SELECT v …)x > (SELECT MIN(v) …)EXISTS (SELECT 1 … WHERE v < x)
x < ANY (SELECT v …)x < (SELECT MAX(v) …)EXISTS (SELECT 1 … WHERE v > x)
x = ANY (SELECT v …)x IN (SELECT v …)EXISTS (SELECT 1 … WHERE v = x)
x <> ALL (SELECT v …)x NOT IN (SELECT v …)NOT EXISTS (SELECT 1 … WHERE v = x)

Com >= e <=, a ideia é a mesma: x >= ALL vira x >= (SELECT MAX…), e assim por diante.

E os NULL?

MAX e MIN ignoram os NULL da lista. Já no padrão, segundo os mesmos manuais, um NULL na lista pode deixar o ALL e o ANY "desconhecidos" em vez de verdadeiros, e aí a linha não passa no WHERE. Nos dados da Loja Aurora, as colunas usadas nesta aula (preco, salario) não têm NULL. Na sua tabela, se a coluna puder ter NULL, filtre com WHERE v IS NOT NULL dentro da subconsulta e você não precisa se preocupar.


Quando dá errado

ALL depois de uma comparação
SELECT nome, salario
FROM funcionarios
WHERE salario > ALL (SELECT salario FROM funcionarios WHERE cargo = 'Estoquista');

A mensagem: near "ALL": syntax error. O SQLite não tem ALL em comparações.

Como consertar: salario > (SELECT MAX(salario) …), ou NOT EXISTS com a comparação invertida.

ANY ou SOME antes de uma subconsulta
SELECT nome
FROM produtos
WHERE id = SOME (SELECT produto_id FROM itens_pedido);

A mensagem: near "SELECT": syntax error. O SQLite tratou SOME ( como uma função e não esperava um SELECT ali dentro.

Como consertar: = ANY e = SOME são o IN: WHERE id IN (SELECT produto_id FROM itens_pedido).

Trocou MAX por MIN

A Ana queria os produtos mais caros que todos os da Papelaria e usou o MIN:

SELECT COUNT(*) AS produtos
FROM produtos
WHERE preco > (SELECT MIN(preco) FROM produtos WHERE categoria_id = 1);

O que acontece: 44 produtos, quase a loja inteira, sem erro. Ela perguntou "mais caro que algum" (o lápis, de 1.15), e não "mais caro que todos".

Como consertar: lembre a fila da escola. Mais que todos → compare com o extremo do outro lado (> com MAX, < com MIN). Mais que algum → com o extremo do mesmo lado (> com MIN, < com MAX).

Nos outros bancos

O MySQL, o PostgreSQL e o SQL Server têm ANY, SOME e ALL depois de um operador de comparação, sempre com uma subconsulta de uma coluna. O manual do MySQL avisa que = ANY só funciona com subconsulta, não com uma lista de valores escrita à mão (para isso, use IN (1, 2, 3)). As traduções desta aula, com MAX/MIN, IN e EXISTS, rodam em todos esses bancos também, então são a escolha mais segura para quem escreve SQL que precisa funcionar em qualquer lugar.


Referência rápida

EscritaO que fazNo SQLite
x > ALL (subconsulta)Maior que todos os valoresNão existe: x > (SELECT MAX …)
x > ANY (subconsulta)Maior que algum valorNão existe: x > (SELECT MIN …)
SOMESinônimo de ANYNão existe
x = ANY (…)Igual a algumx IN (…)
x <> ALL (…)Diferente de todosx NOT IN (…)
Lista vaziaALL é verdadeiro, ANY é falsoUse NOT EXISTS / EXISTS
Mensagem do SQLiteQuer dizer
near "ALL": syntax errorALL depois de comparação: não existe no SQLite
near "SELECT": syntax errorANY (SELECT …) / SOME (SELECT …): o SQLite leu como função
no such function: ANYANY (valor, valor): não existe essa função

Exercícios

1. Mais caro que todos

Complete a tradução de preco > ALL (SELECT preco FROM produtos WHERE categoria_id = 2):

WHERE preco > (SELECT FROM produtos WHERE categoria_id = 2)

2. Igual a algum

Qual operador do SQLite faz o papel de = ANY?

WHERE id (SELECT produto_id FROM itens_pedido)

3. O que o SQLite responde a WHERE preco > ALL (SELECT preco FROM produtos)?

4. "Mais barato que algum livro" (< ANY) é o mesmo que:

5. Se a subconsulta não devolver nenhuma linha, qual tradução de x > ALL (…) continua dando o mesmo resultado do padrão?

6. NOT IN é o mesmo que:

Missão final: traduza o ANY

Missão: este código veio de um banco que tem ANY e procura os brinquedos (categoria 5) mais baratos que algum livro (categoria 4). No SQLite, ele dá erro. Reescreva-o de um jeito que funcione: com MAX ou com EXISTS, você escolhe.

SELECT nome, preco
FROM produtos
WHERE categoria_id = 5
  AND preco < ANY (SELECT preco FROM produtos WHERE categoria_id = 4);
SELECT nome, preco
FROM produtos
WHERE categoria_id = 5
  AND preco < (SELECT MAX(preco) FROM produtos WHERE categoria_id = 4);

Resumo

  • x > ALL (subconsulta): maior que todos; x > ANY (subconsulta) (ou SOME): maior que algum. Existem no MySQL, no PostgreSQL e no SQL Server.
  • O SQLite não tem ANY, SOME nem ALL em comparações. Os erros: near "ALL": syntax error e near "SELECT": syntax error.
  • Traduza com MAX/MIN: mais que todos → o extremo do outro lado; mais que algum → o extremo do mesmo lado.
  • = ANY é IN; <> ALL é NOT IN.
  • Com a lista vazia, ALL é verdadeiro e MAX vira NULL: para o comportamento exato do padrão, use NOT EXISTS (para ALL) e EXISTS (para ANY). Essas traduções rodam em todos os bancos.