A cláusula "entre" do MySQL não é inclusiva?

142

Se eu executar uma consulta com uma betweencláusula, parece excluir o valor final.
Por exemplo:

select * from person where dob between '2011-01-01' and '2011-01-31'

Isso obtém todos os resultados dobde '2011-01-01' a '2011-01-30'; pulando registros onde dobé '2011-01-31'. Alguém pode explicar por que essa consulta se comporta dessa maneira e como eu poderia modificá-la para incluir registros onde dobestá '2011-01-31'? (sem adicionar 1 à data final porque ela foi selecionada pelos usuários.)

ASD
fonte
Não. A minha instalação do MySQL (versão?) BETWEENÉ inclusiva para ambos os valores. Eu tenho MySQL Server 5.7no Windows 10.
Verde

Respostas:

181

O campo dobprovavelmente tem um componente de tempo.

Para truncá-lo:

select * from person 
where CAST(dob AS DATE) between '2011-01-01' and '2011-01-31'
tiago2014
fonte
59
Em vez de CAST(dob AS DATE)você pode usar o mais sucinto DATE(dob).
Jkndrkn
11
Enquanto isso funcionar, você obterá melhor desempenho usando >=e <não between.
David Harkness
112
Você obterá melhor desempenho usando dob BETWEEN '2011-01-01 00:00:00' AND '2011-01-31 23:59:59. Isso ocorre porque DATE(dob)é necessário calcular um valor para cada linha e não pode usar nenhum índice nesse campo.
Joshuahedlund
2
@joshuahedlund Por favor, adicione uma resposta com esta solução. O CAST não é tão eficiente.
doc_id 27/05
3
@joshuahedlund Isso funciona até você ter dados com horários t > 23:59:59 and t < 24:00:00. Por que lidar com mal especificado BETWEEN? Em vez seguir o conselho e uso de David: WHERE dob >= '2011-01-01' AND dob < '2011-02-01'. Melhor desempenho e funciona sempre.
Desiludido
300

No manual do MySQL :

Isso é equivalente à expressão (min <= expr AND expr <= max)

Frank Heikens
fonte
3
O manual vinculado nesta resposta mostra que uma conversão é preferida ao comparar objetos DATE e DATETIME. Então, acho que o @tiagoinu tem a resposta mais completa no sentido mais estrito, mas ambos estão no local.
precisa saber é o seguinte
@jemminger pode ser porque a resposta é do arquirrival -postgres cara: P
Nawfal
27
Em resumo, é inclusivo ... é por isso que essa resposta é ótima.
Rafael
6
Comentário antigo, mas eu queria vincular isso à consulta específica. "ENTRE" é inclusivo, mas datas sem horário especificado para 00:00:00. A comparação de um período perderá, portanto, o último dia. Ligue para DATE (dob) ou especifique o final do dia.
wintermute92
eles dizem que a prática é ouro, do meu caso de uso não é inclusivo, pergunto-me por que isso acontece comigo. Eu tentei e às vezes funciona às vezes não. usando-o no campo de dados TIME.
Jeffery ThaGintoki
99

O problema é que 2011-01-31 é realmente 2011-01-31 00:00:00. Esse é o começo do dia. Tudo durante o dia não está incluído.

Daniel Hilgarth
fonte
19
Isso realmente explica o que está acontecendo e responde à pergunta.
Ivan P #
3
Depois de todos esses anos, essa resposta ainda é a melhor. Muito obrigado.
25717 Strabek
31
select * from person where dob between '2011-01-01 00:00:00' and '2011-01-31 23:59:59'
Gaurav
fonte
1
Eu acho que vale a pena notar que isso não incluirá datas em, 2011-01-31 23:59:59mas incluirá as que 2011-01-31 23:59:58 não estiverem incluídas até o último segundo do dia. Pode ser menor, mas alguém se beneficiará.
doc_id 27/05
1
rahmanisback da documentação do MySQL Posso confirmar que o último segundo será incluído, pois BETWEEN é de ambos os modos, inclusive. consulte dev.mysql.com/doc/refman/5.5/en/…
Felype
1
Sim, @Felype, você está certo. Eu verifiquei isso sozinho no banco de dados mysql. Inclui também o 23:59:59no resultado. Portanto, as duas maneiras são inclusivas.
Sorte
2
Se a dobcoluna for um carimbo de data / hora com precisão de menos de um segundo, BETWEENainda assim não ocorrerá eventos no segundo final do dia, a menos que '2011-02-01 00:00:00' seja usado?
nitrogênio
1
-1. Não inclui 2011-01-31 23:59:59.003. @nitrogen usando 2011-02-01 000:00:00irá incorretamente incluem tempo zero em 1º de fevereiro .... E é por isso >=e <deve ser usado em seu lugar.
Desiludido
6

O campo que você está referenciando na sua consulta é um tipo de data ou um tipo de data e hora ?

Uma causa comum do comportamento que você descreve é ​​quando você usa um tipo DateTime em que realmente deveria estar usando um tipo Date. Ou seja, a menos que você realmente precise saber a que horas alguém nasceu, basta usar o tipo Data.

O motivo pelo qual o dia final não está sendo incluído nos seus resultados é o modo como a consulta está assumindo a parte do tempo das datas que você não especificou na sua consulta.

Ou seja: sua consulta está sendo interpretada como Meia-noite entre 30/01/2011 e 31/01/2011, mas os dados podem ter um valor posteriormente no dia 2011/01/01.

Sugestão: Altere o campo para o tipo Data se for do tipo DateTime.

JohnFx
fonte
4

Olá, esta consulta funciona para mim,

select * from person where dob between '2011-01-01' and '2011-01-31 23:59:59'
infinito84
fonte
2
select * from person where DATE(dob) between '2011-01-01' and '2011-01-31'

Surpreendentemente, essas conversões são soluções para muitos problemas no MySQL.

betty.88
fonte
10
Surpreendentemente, é exatamente isso que a resposta aceita (e várias outras) disse ... 2 anos antes de você.
Chris Chris Baker
0

Defina a data superior como + 1 dia; portanto, no seu caso, defina-a para 01-02-2011.

Rafal
fonte
1
Isso incluirá incorretamente o tempo zero no dia 1º de fevereiro .... É por isso que BETWEENdeve ser ignorado; mas >=e <deve ser usado em seu lugar.
Desiludido
0

Você pode executar a consulta como:

select * from person where dob between '2011-01-01' and '2011-01-31 23:59:59'

como outros apontaram, se suas datas são codificadas.

Por outro lado, se a data estiver em outra tabela, você poderá adicionar um dia e subtrair um segundo (se as datas forem salvas sem o segundo / horário), como:

select * from person JOIN some_table ... where dob between some_table.initial_date and (some_table.final_date + INTERVAL 1 DAY - INTERVAL 1 SECOND)

Evite fazer transmissões nos dobcampos (como na resposta aceita), pois isso pode causar grandes problemas de desempenho (como não poder usar um índice em dobcampo, supondo que exista). O plano de execução pode mudar de using index conditionpara using wherese você criar algo como DATE(dob)ou CAST(dob AS DATE), portanto, tenha cuidado!

Lucas Basquerotto
fonte
0

No MySql, os valores são inclusivos, portanto, quando você tenta obter entre '2011-01-01' e '2011-01-31'

ele irá incluir de 2011-01-01 00:00:00até 2011-01-31 00:00:00 portanto, nada realmente em 2011-01-31 desde o seu tempo deve ir de2011-01-31 00:00:00 ~ 2011-01-31 23:59:59

Para o limite superior, você pode alterar para, em 2011-02-01seguida, todos os dados serão atualizados2011-01-31 23:59:59

Ambleu
fonte