SQL BETWEEN

A Ana vai montar uma vitrine de "presentes de 20 a 30 reais" na Loja Aurora. Ela precisa da lista de produtos cujo preço está dentro dessa faixa. Dá para escrever isso com duas comparações e um AND, como você viu nas aulas anteriores, mas o SQL tem uma palavra feita sob medida para faixas: BETWEEN ("entre", em inglês). Nesta aula você vai usá-la com preços, salários, datas e até nomes, e vai ver as três armadilhas que pegam quase todo mundo: as pontas entram, a ordem importa e data em formato brasileiro não funciona.

Você vai aprender
  • A forma coluna BETWEEN menor AND maior e o que cada parte faz.
  • Por que as duas pontas da faixa entram no resultado.
  • Por que o menor valor vem sempre primeiro, e o que acontece se você inverter.
  • NOT BETWEEN: tudo o que está fora da faixa.
  • Faixas de datas com texto no formato ISO ('2026-01-01') e a armadilha do último dia.
  • BETWEEN com texto (nomes por letra) e com outras condições no mesmo WHERE.
  • Por que as linhas com NULL ficam de fora tanto do BETWEEN quanto do NOT BETWEEN.

Todos os exemplos rodam de verdade, no seu navegador, contra o banco da Loja Aurora. O quadro "Resultado" embaixo de cada código é a resposta real do SQLite.


A ideia: a régua com duas marcas

Pense numa régua. Você faz uma marca no 20 e outra no 30 e pergunta: "quais produtos caem entre as duas marcas?". Um produto de R$ 24,75 cai dentro. Um de R$ 12,35 fica antes da primeira marca; um de R$ 89,99 fica depois da segunda. E um produto de exatamente R$ 20,00, bem em cima da marca? Para o BETWEEN, ele entra.

Na réguaNo SQL
O que você está medindoA coluna (ou expressão): preco
A primeira marca (a menor)O valor depois de BETWEEN: 20
A segunda marca (a maior)O valor depois de AND: 30
Em cima de uma marcaEntra no resultado (as pontas são inclusivas)

1. O primeiro BETWEEN

A vitrine da Ana: produtos de 20 a 30 reais.

SELECT nome, preco
FROM produtos
WHERE preco BETWEEN 20 AND 30;
WHERE
"Onde": o filtro de linhas, que você conheceu em WHERE. Só passam as linhas em que a condição é verdadeira.
preco
O valor que vai ser medido em cada linha: a coluna preco da tabela produtos.
BETWEEN
"Entre". Avisa que vem uma faixa.
20
A ponta de baixo da faixa (o menor valor).
AND
Aqui o AND não é o "e" lógico da aula AND: ele faz parte do BETWEEN e só separa as duas pontas. Leia "entre 20 e 30".
30
A ponta de cima da faixa (o maior valor).

Saíram 9 produtos, do Avental de Cozinha (R$ 22,90) ao Pen Drive (R$ 29,90). Repare que os preços aparecem como o SQLite os guarda: 22.9, com ponto e sem o zero do fim. No SQL, número decimal se escreve sempre com ponto.

A ordem das linhas é a ordem em que elas estão na tabela. Para ver a vitrine do mais barato ao mais caro, junte um ORDER BY (aula ORDER BY) no fim; o BETWEEN não muda nada nisso:

SELECT nome, preco
FROM produtos
WHERE preco BETWEEN 20 AND 30
ORDER BY preco;

2. As pontas entram

O BETWEEN é inclusivo: um valor igual a uma das pontas também passa. Para ver isso, escolha pontas que são preços que existem no banco. O Estojo Duplo custa exatamente 19.5 e o Avental de Cozinha, exatamente 22.9:

SELECT nome, preco
FROM produtos
WHERE preco BETWEEN 19.5 AND 22.9;

Os dois aparecem, junto com a Garrafa Squeeze (21.75), que está no meio. Por isso o BETWEEN é exatamente a mesma coisa que duas comparações com >= e <= (as da aula Comparações):

SELECT nome, preco
FROM produtos
WHERE preco >= 19.5 AND preco <= 22.9;
Com BETWEENÉ o mesmo que
preco BETWEEN 19.5 AND 22.9preco >= 19.5 AND preco <= 22.9
x BETWEEN a AND bx >= a AND x <= b

O resultado é idêntico. A vantagem do BETWEEN é ler melhor e escrever o nome da coluna uma vez só. A desvantagem: ele sempre inclui as duas pontas. Se você quer "maior que 20 e menor que 30", sem as pontas, volte para preco > 20 AND preco < 30.

Experimente: a Loja Aurora quer saber quais funcionários ganham de R$ 3.000 a R$ 3.500. Hoje a consulta mostra todos os 8. Acrescente um WHERE com BETWEEN no salario. (Lembre: no SQL, 3.000 reais se escreve 3000, sem ponto de milhar.)

SELECT nome, salario
FROM funcionarios;
SELECT nome, salario
FROM funcionarios
WHERE salario BETWEEN 3000 AND 3500;

3. A ordem importa: o menor vem primeiro

Na régua, tanto faz dizer "entre o 20 e o 30" ou "entre o 30 e o 20". No SQL, não. Como x BETWEEN a AND b quer dizer x >= a AND x <= b, trocar as pontas de lugar dá uma condição impossível: "maior ou igual a 30 e menor ou igual a 20".

SELECT nome, preco
FROM produtos
WHERE preco BETWEEN 30 AND 20;

Nenhuma linha, e nenhuma mensagem de erro. O SQLite fez exatamente o que foi pedido. É por isso que esse engano é perigoso: a consulta "funciona" e só devolve vazio. Se um BETWEEN voltar sem nada quando você esperava resultado, confira a ordem das pontas antes de qualquer outra coisa.

Dá para ver o BETWEEN trabalhando sozinho, sem tabela, com um SELECT de valores soltos. Para o SQLite, uma condição verdadeira vale 1 e uma falsa vale 0:

SELECT 5 BETWEEN 1 AND 10,
       5 BETWEEN 10 AND 1,
       10 BETWEEN 1 AND 10,
       NULL BETWEEN 1 AND 10;
  1. 5 BETWEEN 1 AND 10 → 1: o 5 está na faixa.
  2. 5 BETWEEN 10 AND 1 → 0: com as pontas invertidas, nada cabe.
  3. 10 BETWEEN 1 AND 10 → 1: em cima da ponta de cima também conta.
  4. NULL BETWEEN 1 AND 10 → NULL: um valor desconhecido não está nem dentro nem fora. Isso volta na seção 8.

4. NOT BETWEEN: o que está fora da faixa

O estoquista da Aurora quer ver os produtos com estoque "fora do normal": abaixo de 20 unidades (hora de repor) ou acima de 100 (estoque parado). É o contrário de "entre 20 e 100", então basta pôr NOT antes do BETWEEN:

SELECT nome, estoque
FROM produtos
WHERE estoque NOT BETWEEN 20 AND 100
ORDER BY estoque;
NOT BETWEEN 20 AND 100
"Não entre 20 e 100": passa quem está abaixo de 20 ou acima de 100.
As pontas
Ficam de fora. Um produto com estoque exatamente 20 (o Vaso de Cerâmica Médio) está dentro da faixa, então o NOT BETWEEN não o mostra. O mesmo valeria para um estoque de exatamente 100.

Os 12 produtos vão dos dois descontinuados, com estoque 0, até a Caneta Esferográfica Azul, com 500. Sem o BETWEEN, a mesma pergunta usa OR (aula OR):

SELECT nome, estoque
FROM produtos
WHERE estoque < 20 OR estoque > 100
ORDER BY estoque;

Repare que no NOT BETWEEN as comparações viram < e > (sem o "igual"), e o AND vira OR. É a mesma regra do NOT que você viu em NOT.


5. Faixas de datas

O SQLite não tem um tipo especial para datas. Na Loja Aurora, a coluna pedidos.data guarda texto no formato ISO: 'AAAA-MM-DD' (ano com 4 dígitos, mês com 2, dia com 2), como '2026-01-08'. Texto se escreve entre aspas simples.

Os pedidos do primeiro trimestre de 2026 (janeiro a março):

SELECT id, data, frete
FROM pedidos
WHERE data BETWEEN '2026-01-01' AND '2026-03-31';

Sete pedidos, do 65 (8 de janeiro) ao 71 (20 de março). Mas espere: se a data é texto, como o SQLite sabe que '2026-02-13' vem depois de '2026-01-01'? Ele não sabe nada de datas. Ele compara os textos letra por letra, da esquerda para a direita, como na ordem do dicionário:

'2026-02-13'
'2026-01-01'
 ^^^^^^         iguais: 2, 0, 2, 6, -, 0
       ^        primeira diferença: '2' > '1', então '2026-02-13' vem depois

O formato ISO foi pensado exatamente para isso: o mais importante (o ano) vem primeiro, depois o mês, depois o dia, sempre com o mesmo número de dígitos. Assim, a ordem do texto é a ordem do calendário. É por isso que as datas da Aurora estão nesse formato, e é a primeira coisa a conferir em qualquer banco que guarda data como texto. A aula Datas e Horas aprofunda o assunto.

Funciona com qualquer coluna de data. Os clientes nascidos de 1990 a 1994:

SELECT nome, nascimento
FROM clientes
WHERE nascimento BETWEEN '1990-01-01' AND '1994-12-31';

A armadilha do último dia

No Aurora, as datas não têm hora, então AND '2026-03-31' pega o dia 31 inteiro. Mas muitos bancos guardam data e hora juntas, como '2026-03-31 14:20'. Veja o que o BETWEEN faz com uma dessas:

SELECT '2026-03-31 14:20' BETWEEN '2026-01-01' AND '2026-03-31';

Deu 0: ficou de fora! Comparando letra por letra, '2026-03-31 14:20' é maior que '2026-03-31' (é o mesmo começo, com mais coisa depois, como "casaco" vem depois de "casa" no dicionário). Tudo o que aconteceu no dia 31 depois da meia-noite escaparia do relatório. A forma segura, que funciona com ou sem hora, é "a partir do primeiro dia, e antes do primeiro dia do período seguinte":

SELECT id, data, frete
FROM pedidos
WHERE data >= '2026-01-01' AND data < '2026-04-01';

Mesmo resultado aqui (7 pedidos), e à prova de hora. Bônus: você não precisa saber se o mês acaba no dia 28, 29, 30 ou 31.


6. BETWEEN com texto

Como o BETWEEN só compara, ele funciona com qualquer texto, não só com datas. A Ana quer ligar para os clientes cujo nome começa com A, B ou C, e tenta:

SELECT nome
FROM clientes
WHERE nome BETWEEN 'A' AND 'C'
ORDER BY nome;

Só 4 nomes, e nenhum com C. Onde estão Camila Duarte, Carlos Eduardo Lima e Conceição Prado? O texto 'Camila Duarte' começa igual a 'C' e tem mais letras depois, então é maior que 'C' e passa da ponta de cima. É a mesma armadilha do último dia. O conserto é o mesmo: "a partir de A, e antes de D":

SELECT nome
FROM clientes
WHERE nome >= 'A' AND nome < 'D'
ORDER BY nome;

Agora vêm os 7. Para "começa com" de uma letra só, a aula LIKE costuma ser mais clara (nome LIKE 'C%').

Letras com acento ficam no fim

O SQLite compara texto pelo código de cada caractere, e as letras acentuadas têm códigos maiores que todas as letras sem acento. Por isso SELECT 'Ávila' BETWEEN 'A' AND 'Z'; dá 0: para o SQLite, "Á" vem depois de "Z". Na Aurora, nenhum cliente tem acento na primeira letra, mas tenha isso em mente com nomes como Ângela ou Érica.


7. BETWEEN junto com outras condições

A vitrine de 20 a 30 reais só pode ter produtos com bom estoque, mais de 50 unidades. Junte as duas condições com AND:

SELECT nome, preco, estoque
FROM produtos
WHERE preco BETWEEN 20 AND 30 AND estoque > 50;

Aqui aparecem dois AND com papéis diferentes. Como o SQLite sabe qual é qual? Depois de um BETWEEN, o primeiro AND sempre pertence a ele (separa as pontas). O segundo é o "e" lógico, que junta as condições. Saíram 7 produtos: o Pen Drive e o Regador ficaram de fora porque têm estoque 0 e 31.

Para quem lê, os parênteses deixam tudo mais claro, e o resultado é o mesmo:

SELECT nome, preco, estoque
FROM produtos
WHERE (preco BETWEEN 20 AND 30) AND estoque > 50;

8. E o NULL?

Dois clientes da Aurora não têm data de nascimento cadastrada: Antônio Carlos Neves e Sebastião Moura. No lugar da data, a coluna tem NULL, que quer dizer "valor desconhecido". Veja quem nasceu fora da faixa de 1980 a 1999:

SELECT nome, nascimento
FROM clientes
WHERE nascimento NOT BETWEEN '1980-01-01' AND '1999-12-31';

Antônio e Sebastião não aparecem. Nem aqui, nem no BETWEEN sem o NOT. Se a data é desconhecida, o SQLite não tem como dizer se ela está dentro ou fora; a resposta é NULL (você viu na seção 3), e o WHERE só deixa passar o que for verdadeiro. Somando os dois lados, faltam sempre essas duas pessoas. Por que isso acontece, e como encontrar essas linhas, é o assunto das próximas aulas: O Valor NULL e IS NULL e IS NOT NULL.


Formas diferentes de fazer a mesma faixa

A mesma pergunta, "pedidos de fevereiro de 2026", escrita de três jeitos:

SELECT id, data FROM pedidos
WHERE data BETWEEN '2026-02-01' AND '2026-02-28';
SELECT id, data FROM pedidos
WHERE data >= '2026-02-01' AND data < '2026-03-01';
SELECT id, data FROM pedidos
WHERE data LIKE '2026-02%';

As três devolvem os pedidos 67, 68 e 69. Qual usar:

FormaQuando usarCuidado
x BETWEEN a AND bFaixas de números e datas sem hora, com as duas pontas incluídas. É a mais fácil de ler.Menor primeiro. Com hora, perde o último dia.
x >= a AND x < bPeríodos de datas (com ou sem hora) e faixas de texto ("de A até antes de D").Lembre que o b é o começo do período seguinte.
x > a AND x < bQuando as pontas não podem entrar.O BETWEEN não faz isso.
x NOT BETWEEN a AND bTudo fora da faixa.As pontas ficam de fora; o NULL também.
data LIKE '2026-02%'Um mês ou um ano inteiro, rapidinho.Só serve para "começa com"; não faz faixas como "15 de janeiro a 10 de março".

Quando dá errado

Pontas invertidas: nenhum resultado, nenhum erro
SELECT id, data FROM pedidos
WHERE data BETWEEN '2026-03-31' AND '2026-01-01';

O que acontece: o SQLite procura datas que sejam ao mesmo tempo "depois de 31 de março" e "antes de 1º de janeiro". Não existe nenhuma, e o resultado vem vazio, sem aviso.

Como consertar: o menor valor (a data mais antiga) sempre logo depois do BETWEEN: BETWEEN '2026-01-01' AND '2026-03-31'.

Data no formato brasileiro

A Ana escreveu as datas do jeito que usa no dia a dia:

SELECT id, data FROM pedidos
WHERE data BETWEEN '01/01/2026' AND '31/03/2026';

O que acontece: vieram os 90 pedidos, de 2024 a 2026! O SQLite não sabe que aquilo é uma data; ele compara texto letra por letra. Todo pedido começa com '2' (de 2024, 2025, 2026). '2' é maior que o '0' de '01/01/2026' e menor que o '3' de '31/03/2026', então tudo cabe na "faixa". Sem erro, e com o resultado completamente errado: é o pior tipo de engano.

Como consertar: escreva as datas no mesmo formato da coluna, o ISO: '2026-01-01' e '2026-03-31'.

Vírgula no lugar do ponto (ou do AND)
SELECT nome, preco FROM produtos
WHERE preco BETWEEN 19,90 AND 30;

A mensagem: near ",": syntax error, ou seja, "perto da vírgula: erro de sintaxe". No SQL, a vírgula separa itens de uma lista (como as colunas do SELECT); ela nunca é a vírgula decimal. O mesmo erro aparece se você trocar o AND por vírgula: BETWEEN 20, 30.

Como consertar: decimal com ponto (19.90) e as pontas separadas por AND: BETWEEN 19.90 AND 30.

Esqueceu a segunda ponta
SELECT nome, preco FROM produtos
WHERE preco BETWEEN 20;

A mensagem: near ";": syntax error. O SQLite leu BETWEEN 20 e ficou esperando o AND e a segunda ponta; em vez disso, chegou o ponto e vírgula. Quando a mensagem aponta para o ;, quase sempre está faltando um pedaço antes dele. O mesmo acontece com BETWEEN 20 OR 30: o OR não serve de separador, e o SQLite reclama do ;. Já BETWEEN 20 TO 30 dá near "TO": syntax error.

Como consertar: sempre as duas pontas, com AND no meio: BETWEEN 20 AND 30.

Data entre aspas duplas
SELECT id, data FROM pedidos
WHERE data BETWEEN "2026-01-01" AND "2026-03-31";

A mensagem: no such column: "2026-01-01" - should this be a string literal in single-quotes? ("não existe a coluna "2026-01-01"; isto deveria ser um texto entre aspas simples?"). No SQL padrão, aspas duplas são para nomes de coluna e de tabela; texto vai entre aspas simples. O próprio SQLite dá a dica.

Como consertar: BETWEEN '2026-01-01' AND '2026-03-31'.

Nos outros bancos

O BETWEEN é do SQL padrão e funciona igual no MySQL, no PostgreSQL e no SQL Server: pontas incluídas, menor primeiro. Duas diferenças que valem saber:

  • O PostgreSQL tem o BETWEEN SYMMETRIC, que aceita as pontas em qualquer ordem (ele troca as duas se precisar). O SQLite não tem essa forma. Trecho só ilustrativo:
    SELECT nome, preco FROM produtos
    WHERE preco BETWEEN SYMMETRIC 30 AND 20;
    
  • Esses bancos têm tipos próprios de data (DATE, DATETIME, TIMESTAMP). A armadilha do último dia continua valendo numa coluna com hora: o manual do MySQL recomenda converter os valores para o mesmo tipo da coluna (com CAST()) ao usar BETWEEN com datas. A forma >= início AND < início-do-próximo funciona em todos.

Referência rápida

EscritaO que faz
x BETWEEN a AND bVerdadeiro se a <= x <= b (pontas incluídas)
x NOT BETWEEN a AND bVerdadeiro se x < a ou x > b
BETWEEN 30 AND 20Nunca é verdadeiro: o menor vem primeiro
data BETWEEN '2026-01-01' AND '2026-03-31'Faixa de datas em texto ISO (sem hora)
data >= '2026-01-01' AND data < '2026-04-01'Período de datas à prova de hora
NULL BETWEEN a AND bDá NULL: a linha não passa no WHERE
Mensagem do SQLiteQuer dizer
near ",": syntax errorVírgula decimal (19,90) ou vírgula no lugar do AND
near ";": syntax errorFaltou o AND e a segunda ponta
no such column: "2026-01-01" - should this be a string literal in single-quotes?Data entre aspas duplas; use simples
(sem mensagem, 0 linhas)Pontas invertidas

Exercícios

1. Complete a faixa

Produtos de 10 a 50 reais:

SELECT nome FROM produtos WHERE preco 10 50;

2. Fora da faixa

Pedidos com frete abaixo de 5 ou acima de 20 reais:

SELECT id, frete FROM pedidos WHERE frete BETWEEN 5 AND 20;

3. O Estojo Duplo custa exatamente 19.5. Ele aparece em WHERE preco BETWEEN 19.5 AND 25?

4. O que WHERE preco BETWEEN 100 AND 50 devolve?

5. Na coluna data, que guarda texto, qual forma acha os pedidos de março de 2026?

6. Um cliente tem nascimento NULL. Em qual destas consultas ele aparece?

Missão final: o frete de 2025

Missão: o gerente quer os pedidos feitos em 2025 cujo frete ficou de 10 a 20 reais (pontas incluídas). A consulta abaixo já pega o ano de 2025, mas ainda não olha o frete. Acrescente a segunda condição. Dica: são dois BETWEEN ligados por um AND.

SELECT id, data, frete
FROM pedidos
WHERE data BETWEEN '2025-01-01' AND '2025-12-31';
SELECT id, data, frete
FROM pedidos
WHERE data BETWEEN '2025-01-01' AND '2025-12-31'
  AND frete BETWEEN 10 AND 20;

Se você chegou a 12 pedidos, acertou. Repare que nenhum deles tem frete de exatamente 10 ou 20; as pontas entrariam, mas nenhum pedido de 2025 caiu em cima delas.


Resumo

  • x BETWEEN a AND b é o mesmo que x >= a AND x <= b: as duas pontas entram.
  • O menor valor vem primeiro. Pontas invertidas não dão erro: dão zero linhas.
  • NOT BETWEEN pega o que está fora (abaixo ou acima); as pontas ficam de fora.
  • Datas em texto só funcionam no formato ISO 'AAAA-MM-DD', entre aspas simples. '01/01/2026' compara errado, sem aviso.
  • Com hora na data, ou com texto ("de A a C"), prefira >= início AND < começo-do-próximo.
  • Decimal com ponto (19.90); vírgula dá near ",": syntax error.
  • Linhas com NULL na coluna não aparecem nem no BETWEEN nem no NOT BETWEEN.