SQL NOT
Às vezes é mais fácil dizer o que você não quer. A Ana vai mandar um cartão para todos os clientes da Loja Aurora que moram fora do Piauí. Em vez de listar os outros estados um por um, ela pode pedir "todos, menos os do Piauí". É para isso que serve o NOT ("não", em inglês): ele inverte uma condição do WHERE. O que era verdadeiro vira falso, e o que era falso vira verdadeiro.
- Como o
NOTinverte uma condição doWHERE. - O
NOTem colunas de "sim ou não" (0 e 1), comovipedescontinuado. - A ordem em que o SQL lê
NOT,ANDeOR, e por que os parênteses mudam tudo. - A troca de
NOT (A OR B)porNOT A AND NOT B. - A armadilha do
NULL: por que "não é do Piauí" deixa de fora quem não tem estado nenhum. - Os parentes do
NOT:NOT LIKE,NOT IN,NOT BETWEEN,IS NOT NULL.
Os exemplos usam o banco da Loja Aurora. Cada quadro "Resultado" é desenhado pelo SQLite de verdade, rodando no seu navegador. Se ainda não viu o WHERE, comece por WHERE; o NOT conversa muito com AND e OR, das aulas anteriores.
A ideia: tirar da pilha
Imagine as fichas dos 30 clientes da loja numa pilha em cima da mesa. A Ana pode fazer dois pedidos diferentes ao ajudante:
| Pedido | Em SQL | O que sobra na mão |
|---|---|---|
| "Separe as fichas do Piauí." | WHERE estado = 'PI' | Só as fichas do Piauí |
| "Separe as fichas que não são do Piauí." | WHERE NOT estado = 'PI' | Todas as outras |
O ajudante faz a mesma pergunta para cada ficha ("é do Piauí?"), mas, com o NOT, fica com as fichas em que a resposta foi não. O NOT não muda a pergunta; ele troca a resposta.
1. O primeiro NOT
Os clientes que não moram no Piauí:
SELECT nome, cidade, estado FROM clientes WHERE NOT estado = 'PI';
- WHERE
- "Onde": o filtro. Só passam as linhas em que a condição dá verdadeiro.
- NOT
- "Não": inverte a condição que vem logo depois dele.
- estado = 'PI'
- A condição original: "o estado é PI?". Para José Ribamar (PI) a resposta é sim; para Ana Beatriz (SP), não.
- NOT estado = 'PI'
- A condição invertida: para José Ribamar, o "sim" virou "não", e ele fica de fora; para Ana Beatriz, o "não" virou "sim", e ela entra.
Resultado: 16 clientes, de São Paulo, Fortaleza e Recife. Os outros 14 moram no Piauí (Teresina, Parnaíba, Picos, Oeiras, Floriano). 16 + 14 = 30, a tabela inteira: o NOT pegou exatamente o "resto".
Repare na posição: NOT estado = 'PI', com o NOT na frente. O SQL lê isso como NOT (estado = 'PI'): primeiro compara, depois inverte. Escrever o NOT no meio, como em português ("estado não igual a PI"), dá erro; você vai ver em Quando dá errado.
2. Verdadeiro e falso no SQLite: 1 e 0
Para entender o NOT de verdade, vale ver o que ele faz com um valor solto. No SQLite, "verdadeiro" é 1 e "falso" é 0. Você pode perguntar direto, sem tabela nenhuma:
SELECT NOT 1, NOT 0, NOT 5, NOT NULL;
| Expressão | Resultado | Por quê |
|---|---|---|
NOT 1 | 0 | 1 é verdadeiro; o contrário é falso (0). |
NOT 0 | 1 | 0 é falso; o contrário é verdadeiro (1). |
NOT 5 | 0 | Qualquer número diferente de 0 conta como verdadeiro. |
NOT NULL | NULL | NULL é "não sei". O contrário de "não sei" continua "não sei". Guarde isso: é a armadilha da seção 6. |
Colunas de sim ou não
A Loja Aurora guarda "sim ou não" como 1 ou 0. A coluna vip dos clientes é 1 para os 5 clientes VIP e 0 para os outros. A coluna descontinuado dos produtos é 1 para os produtos que a loja parou de vender. Para ver os produtos que não são "não descontinuados" (ou seja, os descontinuados):
SELECT nome, preco, estoque FROM produtos WHERE NOT descontinuado = 0;
Dois produtos, os dois com estoque 0. Esse exemplo é de propósito "torto", para você treinar a leitura: "não é igual a zero" é o mesmo que "é igual a 1". Na vida real, a Ana escreveria WHERE descontinuado = 1, que é mais fácil de ler.
Como 0 já é falso e 1 já é verdadeiro, no SQLite dá até para usar a coluna sozinha. Os clientes que não são VIP:
SELECT nome, vip FROM clientes WHERE NOT vip;
25 clientes, todos com vip 0. Para o cliente 2 (Maria Clara), vip é 0, NOT 0 é 1 (verdadeiro), e ela entra. Para o cliente 1 (José Ribamar), vip é 1, NOT 1 é 0, e ele fica de fora. É o mesmo que WHERE NOT vip = 1 ou WHERE vip = 0.
WHERE NOT vip funciona no SQLite e no MySQL, mas não em todos os bancos (veja Nos outros bancos). Se você quer um SQL que roda em qualquer lugar, escreva a comparação inteira: WHERE vip = 0.
Experimente: a categoria 1 da loja é a Papelaria. Mostre os produtos que não são da Papelaria, colocando um NOT no lugar certo. Devem aparecer 39 produtos.
SELECT nome, categoria_id FROM produtos WHERE categoria_id = 1;
SELECT nome, categoria_id FROM produtos WHERE NOT categoria_id = 1;
3. NOT junto com AND: a ordem importa
A Ana quer os clientes VIP que não são do Piauí. Ela escreve:
SELECT nome, cidade, estado, vip FROM clientes WHERE NOT estado = 'PI' AND vip = 1;
Três clientes: Ana Beatriz (SP), Fernanda e Mirela (CE). Deu certo porque o SQL lê as partes nesta ordem:
- Primeiro as comparações:
estado = 'PI'evip = 1. - Depois o
NOT, que inverte só a comparação que está colada nele:estado = 'PI'. - Depois o
AND, que junta as duas partes. - Por último, o
OR(se houver).
Ou seja, o SQL entendeu (NOT estado = 'PI') AND (vip = 1). Agora veja o que acontece com parênteses em volta das duas condições:
SELECT nome, cidade, estado, vip FROM clientes WHERE NOT (estado = 'PI' AND vip = 1);
28 clientes! Agora o NOT inverte a pergunta inteira que está dentro dos parênteses: "é do Piauí e é VIP?". Só dois clientes respondem "sim" a ela (José Ribamar e Larissa, os VIPs do Piauí); com o NOT, saem esses dois e ficam os outros 28. Parece uma diferença pequena na escrita, mas a pergunta mudou completamente.
| Escrita | Pergunta em português | Linhas |
|---|---|---|
NOT estado = 'PI' AND vip = 1 | Não é do Piauí, e é VIP | 3 |
NOT (estado = 'PI' AND vip = 1) | Não é verdade que é do Piauí e VIP ao mesmo tempo | 28 |
Parênteses nunca atrapalham. (NOT estado = 'PI') AND vip = 1 dá o mesmo resultado da primeira consulta, e quem lê não precisa lembrar da ordem. Quando juntar NOT, AND e OR, use parênteses para deixar a sua intenção clara.
4. NOT de um OR: a troca de De Morgan
A Ana quer os clientes que nem são do Piauí nem são VIP: os que vão receber um cupom de boas-vindas para conhecer a loja. Dá para escrever de dois jeitos:
SELECT nome, cidade, estado, vip FROM clientes WHERE NOT (estado = 'PI' OR vip = 1);
SELECT nome, cidade, estado, vip FROM clientes WHERE NOT estado = 'PI' AND NOT vip = 1;
Os dois devolvem os mesmos 13 clientes. Essa troca tem nome: é a lei de De Morgan, de um matemático inglês do século XIX. Ela diz:
| Isto… | …é o mesmo que isto | Em português |
|---|---|---|
NOT (A OR B) | NOT A AND NOT B | "Nem A nem B" |
NOT (A AND B) | NOT A OR NOT B | "Não as duas coisas ao mesmo tempo" |
Quando o NOT "entra" nos parênteses, ele inverte cada pedaço e troca o OR por AND (ou o AND por OR). O erro clássico é inverter os pedaços e esquecer de trocar o conector.
5. NOT com mais de uma condição negada
A Ana quer os clientes das cidades "pequenas" da lista, as que não são as três maiores (Teresina, São Paulo e Fortaleza):
SELECT nome, cidade FROM clientes WHERE NOT cidade = 'Teresina' AND NOT cidade = 'São Paulo' AND NOT cidade = 'Fortaleza';
Seis clientes, de Parnaíba, Recife, Picos, Oeiras e Floriano. Funciona, mas repare como ficou comprido. Para "não é nenhum destes", o SQL tem um jeito mais curto, o NOT IN, que você vai ver na aula IN:
SELECT nome, cidade
FROM clientes
WHERE cidade NOT IN ('Teresina', 'São Paulo', 'Fortaleza');
O NOT aparece "grudado" em vários outros operadores do SQL. Cada um tem a sua aula; aqui vai só o mapa:
| Operador | Quer dizer | Exemplo | Aula |
|---|---|---|---|
NOT LIKE | Não combina com o padrão de texto | nome NOT LIKE 'Ana %' | LIKE |
NOT IN | Não é nenhum dos valores da lista | cidade NOT IN ('Teresina', 'Recife') | IN |
NOT BETWEEN | Está fora da faixa | preco NOT BETWEEN 10 AND 200 | BETWEEN |
IS NOT NULL | Tem algum valor (não está vazio) | email IS NOT NULL | IS NULL |
Nos três primeiros, o NOT pode ir antes da coluna também: NOT nome LIKE 'Ana %' e nome NOT LIKE 'Ana %' dão o mesmo resultado (28 clientes, todos menos as duas Anas). A forma com o NOT no meio é a mais comum, porque se lê como português.
6. A armadilha do NULL
A Loja Aurora tem 6 fornecedores. Dois são do Piauí (a Distribuidora Piauí Center e a Norte Suprimentos). A Ana pede os que não são do Piauí, esperando ver os outros quatro:
SELECT nome, estado FROM fornecedores WHERE NOT estado = 'PI';
Vieram só dois. Sumiram a Lusitana Comércio (de Portugal) e a Pampa Sur (da Argentina). Elas não são do Piauí, então por que não apareceram? Porque fornecedor estrangeiro não tem estado brasileiro: a coluna estado deles é NULL (vazia, "não sei"). Veja o que o SQLite calcula em cada linha:
SELECT nome, estado, estado = 'PI', NOT estado = 'PI' FROM fornecedores;
Nas duas últimas linhas da conta, a comparação estado = 'PI' deu NULL: o SQLite não sabe se um valor desconhecido é igual a 'PI'. E, como você viu na seção 2, NOT NULL continua NULL. O WHERE só deixa passar o que é verdadeiro; "não sei" não é verdadeiro, e a linha fica de fora.
| estado | estado = 'PI' | NOT estado = 'PI' | Passa no WHERE? |
|---|---|---|---|
| PI | 1 (verdadeiro) | 0 (falso) | Não |
| CE | 0 (falso) | 1 (verdadeiro) | Sim |
| NULL | NULL (não sei) | NULL (não sei) | Não |
O mesmo acontece com os 3 clientes sem email: WHERE NOT email LIKE '%@exemplo.com.br' devolve 0 linhas, e não os 3 clientes que não têm esse email. Linhas com NULL na coluna testada nunca passam por um NOT, nem pela condição sem NOT.
Para incluir os estrangeiros, diga com todas as letras que o NULL também serve:
SELECT nome, estado FROM fornecedores WHERE NOT estado = 'PI' OR estado IS NULL;
Agora são os quatro. O IS NULL ("é vazio") é a única pergunta que responde "sim" para um NULL; ele tem aula própria, IS NULL e IS NOT NULL, e o próprio NULL é explicado em O Valor NULL.
Missão: a consulta abaixo deveria listar os fornecedores de fora do Piauí, mas esquece os dois estrangeiros. Conserte para que apareçam os 4 fornecedores.
SELECT nome, cidade, estado, pais FROM fornecedores WHERE NOT estado = 'PI';
SELECT nome, cidade, estado, pais FROM fornecedores WHERE NOT estado = 'PI' OR estado IS NULL;
7. Formas diferentes de dizer "não"
Para "o estado não é PI", o SQL aceita várias escritas. Nos clientes, todas dão os mesmos 16:
| Escrita | Observação |
|---|---|
WHERE NOT estado = 'PI' | O NOT desta aula. |
WHERE NOT (estado = 'PI') | Igual, com parênteses deixando claro o que é invertido. |
WHERE estado <> 'PI' | "Diferente de", o operador padrão do SQL (aula Comparações). |
WHERE estado != 'PI' | Outra escrita de "diferente", aceita pelo SQLite e pelos bancos mais usados. |
Todas as quatro têm a mesma armadilha do NULL: nenhuma delas deixa passar os fornecedores estrangeiros.
| Situação | Qual usar |
|---|---|
| Uma comparação simples ("não é igual a") | <>: mais curto e todo mundo entende. |
Inverter uma condição composta, com AND/OR | NOT ( … ), sempre com parênteses. |
| "Não combina", "não está na lista", "fora da faixa", "não é vazio" | NOT LIKE, NOT IN, NOT BETWEEN, IS NOT NULL. |
| Coluna de sim/não (0 e 1) | vip = 0, que roda em qualquer banco; NOT vip só onde o banco aceita. |
A coluna pode ter NULL e você quer essas linhas também | Acrescente OR coluna IS NULL. |
Quando dá errado
Em português se diz "estado não igual a PI", e a Ana escreveu assim:
SELECT nome FROM clientes WHERE estado NOT = 'PI';
A mensagem: near "=": syntax error, "perto de =: erro de sintaxe". O SQL até aceita um NOT depois da coluna, mas só quando vem um LIKE, IN, BETWEEN… em seguida. Um = logo depois do NOT não faz sentido para ele. No terminal do sqlite3, a mesma mensagem vem com o prefixo Parse error e uma seta apontando o lugar:
$ sqlite3 loja.db
SQLite version 3.50.4 2025-07-30 19:33:53
Enter ".help" for usage hints.
sqlite> SELECT nome FROM clientes WHERE estado NOT = 'PI';
Parse error: near "=": syntax error
SELECT nome FROM clientes WHERE estado NOT = 'PI';
error here ---^
sqlite> .quit
Como consertar: o NOT vai antes da condição (NOT estado = 'PI'), ou use o "diferente": estado <> 'PI'.
! no lugar de NOT
Quem vem de outras linguagens de programação (JavaScript, PHP, C) está acostumado com o ! para "não":
SELECT nome FROM clientes WHERE ! vip;
A mensagem: unrecognized token: "!", "símbolo desconhecido: !". No SQLite, o ! sozinho não existe; ele só aparece grudado no =, em !=.
Como consertar: escreva a palavra NOT: WHERE NOT vip, ou melhor ainda, WHERE vip = 0.
SELECT nome FROM clientes WHERE NOT estado = PI;
A mensagem: no such column: PI, "não existe a coluna PI". Sem aspas, o SQL acha que PI é o nome de uma coluna. Isso não tem nada a ver com o NOT, mas acontece muito quando se reescreve a condição às pressas.
Como consertar: texto vai entre aspas simples: 'PI'.
O caso mais perigoso não tem mensagem: é o da seção 6. WHERE NOT estado = 'PI' nos fornecedores roda sem reclamar e devolve 2 linhas, quando a Ana esperava 4. O SQLite fez exatamente o que foi pedido: as linhas com estado NULL deram "não sei", e "não sei" não passa no WHERE.
Como evitar: antes de usar NOT (ou <>) numa coluna, pergunte-se: "essa coluna pode estar vazia?". Se puder, acrescente OR coluna IS NULL. Uma conferência rápida: o "sim" mais o "não" tem de dar o total da tabela. Aqui, 2 do Piauí + 2 fora = 4, mas a tabela tem 6.
Também sem mensagem de erro: NOT estado = 'PI' AND vip = 1 (3 linhas) e NOT (estado = 'PI' AND vip = 1) (28 linhas) são perguntas diferentes, como você viu na seção 3. Se o número de linhas parecer estranho, confira onde estão os parênteses.
- MySQL e MariaDB aceitam o
!como sinônimo deNOT(WHERE ! vip), mas a documentação do MySQL diz que ele é uma extensão fora do padrão, obsoleta, e que pode ser removida: useNOT. - PostgreSQL tem um tipo
booleande verdade (true/false), e oNOTdele é feito para esse tipo. Numa coluna de números 0 e 1 como avip, o atalhoNOT vipnão serve; escreva a comparação inteira:vip = 0. Ilustrativo (não roda no SQLite):
-- PostgreSQL, com uma coluna do tipo boolean SELECT nome FROM clientes WHERE NOT vip; -- vip é boolean: funciona SELECT nome FROM clientes WHERE vip = false; -- o mesmo, por extenso
Com NOT coluna = valor, NOT ( … ) e os parentes (NOT LIKE, NOT IN…), o SQL desta aula é o padrão e funciona igual nos bancos mais usados, inclusive a armadilha do NULL.
Referência rápida
| Escrita | O que faz |
|---|---|
WHERE NOT condição | Passam as linhas em que a condição é falsa |
NOT (A AND B) | Inverte a condição composta inteira; o mesmo que NOT A OR NOT B |
NOT (A OR B) | "Nem A nem B"; o mesmo que NOT A AND NOT B |
NOT A AND B | O NOT pega só o A: (NOT A) AND B |
NOT 1 / NOT 0 / NOT NULL | 0 / 1 / NULL |
NOT LIKE, NOT IN, NOT BETWEEN, IS NOT NULL | As formas "não" dos outros operadores |
| Ordem de leitura | Comparações → NOT → AND → OR (parênteses primeiro) |
| Mensagem do SQLite | Quer dizer |
|---|---|
near "=": syntax error | Um NOT no meio, antes do = (estado NOT = 'PI') |
unrecognized token: "!" | Usou ! no lugar de NOT |
no such column: PI | Texto sem aspas simples |
Exercícios
1. Os produtos que ainda estão à venda
Complete para mostrar os produtos que não estão descontinuados:
2. O valor do NOT
Quanto o SQLite responde para SELECT NOT 0;?
3. Qual destas escritas dá erro de sintaxe no SQLite?
4. Qual escrita é igual a NOT (estado = 'PI' OR vip = 1)?
5. A tabela fornecedores tem 6 linhas: 2 com estado PI, 2 com CE e SP, e 2 com estado NULL. Quantas linhas WHERE NOT estado = 'PI' devolve?
Missão final: o cupom de boas-vindas
Missão: a Ana vai mandar um cupom para os clientes que não moram em Teresina e que não são VIP. A consulta abaixo traz os clientes de Teresina; mude o WHERE para trazer os clientes certos (são 17).
SELECT nome, cidade, vip FROM clientes WHERE cidade = 'Teresina';
SELECT nome, cidade, vip FROM clientes WHERE NOT cidade = 'Teresina' AND NOT vip = 1;
Conseguiu? Tente também a forma com parênteses, NOT (cidade = 'Teresina' OR vip = 1), e confira que dá os mesmos 17. Nenhum cliente tem cidade vazia, então aqui a armadilha do NULL não aparece.
Resumo
NOT("não") inverte uma condição: oWHEREpassa a ficar com as linhas em que ela é falsa.- No SQLite, verdadeiro é 1 e falso é 0:
NOT 1dá 0,NOT 0dá 1. Por issoWHERE NOT vipfunciona, masvip = 0roda em qualquer banco. - O
NOTvem antes da condição:NOT estado = 'PI'. Escrito no meio (estado NOT = 'PI'), dánear "=": syntax error. - Ordem: comparações, depois
NOT, depoisAND, depoisOR.NOT A AND Binverte só oA; para inverter tudo, use parênteses. - De Morgan:
NOT (A OR B)=NOT A AND NOT B;NOT (A AND B)=NOT A OR NOT B. NOT NULLcontinua NULL: linhas com a coluna vazia ficam de fora tanto da condição quanto doNOT. Para incluí-las, acrescenteOR coluna IS NULL.