O "entre" do MS SQL Server inclui os limites do intervalo?

234

Por exemplo, pode

SELECT foo
FROM bar
WHERE foo BETWEEN 5 AND 10

selecione 5 e 10 ou eles são excluídos do intervalo?

Lea Verou
fonte

Respostas:

258

O operador BETWEEN é inclusivo.

Dos Livros Online:

BETWEEN retornará VERDADEIRO se o valor da expressão de teste for maior ou igual ao valor de expressão de início e menor ou igual ao valor de expressão de final.

DateTime Advertência

NB: Com o DateTimes, você deve ter cuidado; se apenas uma data for fornecida, o valor será obtido a partir da meia-noite desse dia; para evitar horas perdidas na data de término ou repetir a captura dos dados do dia seguinte à meia-noite em vários intervalos, a data de término deve ser de 3 milissegundos antes da meia-noite do dia seguinte à sua data de validade. 3 milissegundos porque menos do que isso e o valor será arredondado para meia-noite no dia seguinte.

por exemplo, para obter todos os valores em junho de 2016, você precisa executar:

where myDateTime between '20160601' and DATEADD(millisecond, -3, '20160701')

ie

where myDateTime between '20160601 00:00:00.000' and '20160630 23:59:59.997'

datetime2 e datetimeoffset

Subtrair 3 ms de uma data deixará você vulnerável a linhas ausentes da janela de 3 ms. A solução correta também é a mais simples:

where myDateTime >= '20160601' AND myDateTime < '20160701'
DJ.
fonte
11
Ao usar BETWEEN para filtrar DateTimes entre duas datas, você também pode converter o DateTime para uma Data, por exemplo: where CONVERT (DATE, MyDate) ENTRE '2017-09-01' e '2017-09-30' Essa abordagem faz a hora elemento do DateTime irrelevante
Pete
1
Certifique-se de não tentar subtrair 3 ms de uma data; você perderá itens desses 3 ms. E você também não deseja CONVERTum datetime para uma data , pois isso tornará os índices inúteis. Use o padrão WHERE OrderDate >= '20160601' AND OrderDate < '20160701'. Além disso, certifique-se de usar yyyymmdd, pois yyyy-mm-dddepende da localidade e será mal interpretado, dependendo da mdy, dmy, ymd, ydm, myd, and dymconfiguração do servidor .
22818 Ian Boyd
254

Sim, mas tenha cuidado ao usar entre datas.

BETWEEN '20090101' AND '20090131'

é realmente interpretado como 12h, ou

BETWEEN '20090101 00:00:00' AND '20090131 00:00:00'

perderá tudo o que ocorreu durante o dia 31 de janeiro. Nesse caso, você terá que usar:

myDate >= '20090101 00:00:00' AND myDate < '20090201 00:00:00'  --CORRECT!

ou

BETWEEN '20090101 00:00:00' AND '20090131 23:59:59' --WRONG! (see update!)

ATUALIZAÇÃO : É perfeitamente possível criar registros dentro desse último segundo do dia, com data e hora posteriores a 20090101 23:59:59.997!!

Por esse motivo, a BETWEEN (firstday) AND (lastday 23:59:59)abordagem não é recomendada.

Use a myDate >= (firstday) AND myDate < (Lastday+1)abordagem em seu lugar.

Bom artigo sobre esta questão aqui .

BradC
fonte
1
Problemas semelhantes com cadeias também WHERE col BETWEEN 'a' AND 'z'excluirão a maioria das linhas z, por exemplo.
Martin Smith
8
Este ponto está certo, é claro; mas não deve ser surpreendente se você estiver trabalhando com essas datas. É análogo ao apontar que BETWEEN 5 AND 10não inclui 10.2...
Andrzej Doyle
4
CASTing o datetimecomo DATEiria funcionar: CAST(DATE_TIME_COL AS DATE) BETWEEN '01/01/2009' AND '01/31/2009'.
Craig
2
@ Craig, isso é verdade, desde que você esteja usando o SQL 2008 ou superior, que é quando o tipo de dados Date foi introduzido. Além disso, essa sintaxe converterá esse valor para cada linha, portanto, não será possível usar nenhum índice nesse campo (se isso for um problema).
BradC
It is entirely possible to have records created within that last second of the day, with a datetime as late as 01/01/2009 23:59:59.997<- você não poderia usar então AND '01/31/2009 23:59:59.99999999'ou são necessários muitos 9's
287
16

Exemplo do mundo real do SQL Server 2008.

Dados de origem:

ID    Start
1     2010-04-30 00:00:01.000
2     2010-04-02 00:00:00.000
3     2010-05-01 00:00:00.000
4     2010-07-31 00:00:00.000

Inquerir:

SELECT
    *
FROM
    tbl
WHERE
    Start BETWEEN '2010-04-01 00:00:00' AND '2010-05-01 00:00:00'

Resultados:

ID    Start
1     2010-04-30 00:00:01.000
2     2010-04-02 00:00:00.000

texto alternativo

Ryan Rodemoyer
fonte
Não recebi sua resposta, para ser sincero. Talvez meu provedor de internet tenha ocultado sua captura de tela se você postou uma.
Anar khalilov
2
Por que a linha está ID = 3excluída? Seu Startvalor é igual ao BETWEENvalor do limite superior e BETWEENé um intervalo inclusivo, não um intervalo exclusivo do limite superior.
Dai
Melhor resposta com resultados.
Sam
13

se você acertar isso e realmente não quiser tentar adicionar um dia no código, deixe o banco de dados fazer isso ..

myDate >= '20090101 00:00:00' AND myDate < DATEADD(day,1,'20090101 00:00:00')

Se você incluir a parte da hora: verifique se ela faz referência à meia-noite. Caso contrário, você pode simplesmente omitir a hora:

myDate >= '20090101' AND myDate < DATEADD(day,1,'20090101')

e não se preocupe com isso.

Shaun
fonte
12

ENTRE (Transact-SQL)

Especifica um intervalo ( n ) ( inclusivo ) a ser testado.

test_expression [ NOT ] BETWEEN begin_expression AND end_expression

Argumentos

test_expression

É a expressão a ser testada no intervalo definido por begin_expression e end_expression. test_expression deve ser o mesmo tipo de dados que begin_expression e end_expression.

NOT

Especifica que o resultado do predicado seja negado.

begin_expression

É qualquer expressão válida. begin_expression deve ser o mesmo tipo de dados que test_expression e end_expression.

end_expression

É qualquer expressão válida. end_expression deve ser o mesmo tipo de dados que test_expression e begin_expression.

AND

Atua como um espaço reservado que indica expressão de teste deve estar dentro do intervalo indicado por expressão de início e expressão de fim.

Observações

Para especificar um intervalo exclusivo, use os operadores maior que (>) e menor que (<). Se alguma entrada para o predicado BETWEEN ou NOT BETWEEN for NULL, o resultado será UNKNOWN.

Valor do resultado

BETWEEN retornará VERDADEIRO se o valor da expressão de teste for maior ou igual ao valor de expressão de início e menor ou igual ao valor de expressão de final.

NOT BETWEEN retorna TRUE se o valor de expressão_express for menor que o valor de expressão_início ou maior que o valor de expressão_exemplo.

Russ Cam
fonte
3

Se o tipo de dados da coluna for datetime, você poderá fazer o seguinte para eliminar o tempo do datetime e comparar apenas o período.

where cast(getdate() as date) between cast(loginTime as date) and cast(logoutTime as date)
Kahlil Vanz
fonte
Isso funciona melhor do que adicionar +1 à data final. Concordo com Andrew Morton - se não for sargável, pode melhorar o desempenho para alterar o tipo de dados da coluna ou adicionar uma segunda coluna apenas com datas pré-calculadas.
Arno Peters
0

Inclui limites.

declare @startDate date = cast('15-NOV-2016' as date) 
declare @endDate date = cast('30-NOV-2016' as date)
create table #test (c1 date)
insert into #test values(cast('15-NOV-2016' as date))
insert into #test values(cast('20-NOV-2016' as date))
insert into #test values(cast('30-NOV-2016' as date))
select * from #test where c1 between @startDate and @endDate
drop table #test
RESULT    c1
2016-11-15
2016-11-20
2016-11-30


declare @r1 int  = 10
declare @r2 int  = 15
create table #test1 (c1 int)
insert into #test1 values(10)
insert into #test1 values(15)
insert into #test1 values(11)
select * from #test1 where c1 between @r1 and @r2
drop table #test1
RESULT c1
10
11
15
Halim
fonte
-3

Eu sempre usei isso:

ONDE myDate ENTRE startDate AND (endDate + 1)

user2296528
fonte