Como posso truncar uma data e hora no SQL Server?

280

Qual é a melhor maneira de truncar um valor de data e hora (como remover horas, minutos e segundos) no SQL Server 2008?

Por exemplo:

declare @SomeDate datetime = '2009-05-28 16:30:22'
select trunc_date(@SomeDate)

-----------------------
2009-05-28 00:00:00.000
Julio Cesar
fonte

Respostas:

494

Isso continua a reunir votos adicionais, mesmo vários anos depois, e por isso preciso atualizá-lo para versões modernas do Sql Server. Para o Sql Server 2008 e posterior, é simples:

cast(getDate() As Date)

Observe que os últimos três parágrafos próximos à parte inferior ainda se aplicam, e muitas vezes você precisa dar um passo atrás e encontrar uma maneira de evitar o elenco em primeiro lugar.

Mas há outras maneiras de conseguir isso também. Aqui estão os mais comuns.

A maneira correta (nova desde o Sql Server 2008):

cast(getdate() As Date)

A maneira correta (antiga):

dateadd(dd, datediff(dd,0, getDate()), 0)

Agora é mais antigo, mas ainda vale a pena saber porque também pode se adaptar facilmente a outros pontos do tempo, como o primeiro momento do mês, minuto, hora ou ano.

Essa maneira correta usa funções documentadas que fazem parte do padrão ansi e que garantem o trabalho, mas podem ser um pouco mais lentas. Ele funciona descobrindo quantos dias existem do dia 0 ao dia atual e adicionando esses dias ao dia 0. Ele funcionará independentemente da data e hora armazenadas e da localidade.

O caminho mais rápido:

cast(floor(cast(getdate() as float)) as datetime)

Isso funciona porque as colunas datetime são armazenadas como valores binários de 8 bytes. Lance-os para flutuar, aplique-os no chão para remover a fração e a parte do tempo dos valores desaparece quando você os lança de volta à data e hora. Tudo muda um pouco, sem lógica complicada e é muito rápido.

Esteja ciente de que depende de um detalhe de implementação que a Microsoft pode mudar a qualquer momento, mesmo em uma atualização automática de serviço. Também não é muito portátil. Na prática, é muito improvável que essa implementação mude em breve, mas ainda é importante estar ciente do perigo se você optar por usá-la. E agora que temos a opção de lançar como uma data, raramente é necessário.

O caminho errado:

cast(convert(char(11), getdate(), 113) as datetime)

O caminho errado funciona convertendo para uma string, truncando a string e convertendo novamente em um datetime. Está errado , por duas razões: 1) pode não funcionar em todos os locais e 2) é a maneira mais lenta possível de fazer isso ... e não apenas um pouco; é como uma ordem de magnitude ou duas mais lenta que as outras opções.


Atualizar Isso tem recebido alguns votos recentemente e, por isso, quero acrescentar que, desde que publiquei isso, vi evidências bastante sólidas de que o Sql Server otimizará a diferença de desempenho entre a maneira "correta" e a "rápida" , o que significa que agora você deve favorecer o primeiro.

Em ambos os casos, você deseja escrever suas consultas para evitar a necessidade de fazer isso em primeiro lugar . É muito raro você fazer esse trabalho no banco de dados.

Na maioria dos lugares, o banco de dados já é seu gargalo. Geralmente, é o servidor mais caro para adicionar hardware para aprimoramentos de desempenho e o mais difícil de obter essas adições (você precisa equilibrar discos com memória, por exemplo). É também o mais difícil de expandir, tanto tecnicamente quanto do ponto de vista comercial; tecnicamente, é muito mais fácil adicionar um servidor da Web ou de aplicativos do que um servidor de banco de dados e, mesmo que isso fosse falso, você não paga mais de US $ 20.000 por licença de servidor para IIS ou apache.

O que estou tentando enfatizar é que, sempre que possível, você deve fazer esse trabalho no nível do aplicativo. O único momento em que você deve truncar uma data e hora no Sql Server é quando precisa agrupar por dia e, mesmo assim, você provavelmente deve ter uma coluna extra configurada como coluna computada, mantida no momento da inserção / atualização ou mantida na lógica da aplicação. Tire esse trabalho pesado de cpu e de índice do seu banco de dados.

Joel Coehoorn
fonte
6
o "caminho rápido" ainda é a maneira mais rápida para o SQL 2008 de acordo com um benchmark Eu corri
Sam Saffron
3
FYI: stackoverflow.com/q/1177449/27535 e stackoverflow.com/q/133081/27535 O dateadd / datediff "vence ...". Para uma única variável, quem se importa, é claro, e espera-se que você tenha calculado colunas ou mais de um milhão de linhas :-)
gbn
9
Essa maneira "correta" funciona apenas acidentalmente. A maneira como é escrita é como se a sintaxe do DateAdd fosse (intervalo, data, incremento), mas não é. É (intervalo, incremento, data). Eu me deparei com isso quando tentei truncar uma data para o primeiro do mês: SELECT DATEADD (m, 0, DATEDIFF (m, 0, GETDATE ())) não funciona, mas SELECT DATEADD (m, DATEDIFF (m, 0, GETDATE ()), 0) sim. Pelo menos, é o que vejo em 2008R2.
21711 Kelly Cline
1
@ Kelly em 2008R2, por que não apenas cast(getdate() as date)?
Joel Coehoorn
2
Todos eles trabalham em uma coluna de data e hora. getdate()aqui está um substituto para qualquer fonte de data e hora que você possa ter.
Joel Coehoorn
44

Somente para o SQL Server 2008

CAST(@SomeDateTime AS Date) 

Em seguida, transmita-o de volta para data e hora, se desejar

CAST(CAST(@SomeDateTime AS Date) As datetime)
DJ.
fonte
Bom ponto: ainda estou em 2005 e, portanto, para 2008 essa é provavelmente a nova maneira "correta" e pode até igualar o desempenho da maneira "rápida".
Joel Coehoorn
1
O desempenho dessa nova maneira é ainda mais rápido que o modo "rápido".
ErikE 13/09/10
21

Apenas para obter uma resposta mais completa, aqui está uma maneira de truncar qualquer parte da data e incluir minutos (substitua GETDATE()pela data a ser truncada).

Isso é diferente da resposta aceita, pois você pode usar não apenas dd(dias), mas qualquer parte da data (veja aqui ):

dateadd(minute, datediff(minute, 0, GETDATE()), 0)

Observe que na expressão acima, 0é uma data constante no início de um ano (1900-01-01). Se você precisar truncar para peças menores, como segundos ou milissegundos, precisará de uma data constante mais próxima da data a ser truncada para evitar um estouro.

Lucero
fonte
1
Isso foi monstruosamente útil. Procurei em todo o lado uma maneira de truncar a data e hora em um local inferior ao dia inteiro.
Michael - de onde Clay Shirky
1
@ Michael, obrigado pelo feedback, bom saber que isso o ajudou!
Lucero
1
Com +1, isso deve ter mais votos positivos; é uma ótima resposta que se expande na resposta selecionada.
Jtate 28/08
1
Para que a internet saiba, você não precisa se restringir a períodos completos de datas. Aqui está um exemplo para intervalos de 15 minutos, usando divisão inteira:dateadd(minute, datediff(minute, 0, GETDATE()) / 15 * 15, 0)
Michael - de onde Clay Shirky
7

O trecho que encontrei na Web quando precisei fazer isso foi:

 dateadd(dd,0, datediff(dd,0, YOURDATE))
 e.g.
 dateadd(dd,0, datediff(dd,0, getDate()))
Tom Ritter
fonte
Estou em 2005, mas pensei que 2008 tivesse alguma função nova para isso?
KM.
2
Arrumado! Eu teria recorrido a dividir as partes da data e usando o manuseio de cordas para juntá-las novamente. Pode não ser relevante, mas o SQL2008 possui um tipo de dados puro somente para data sem um elemento de hora.
284 Frans
1
E observe que você tem os operandos DateAdd misturados DateAdd(dd, DateDiff(...), 0). Isso pode morder você, se você não for cuidadoso.
ErikE
1

No SQl 2005, sua função trunc_date poderia ser escrita assim.

(1)

CREATE FUNCTION trunc_date(@date DATETIME)
RETURNS DATETIME
AS
BEGIN
    CAST(FLOOR( CAST( @date AS FLOAT ) )AS DATETIME)
END

O primeiro método é muito mais limpo. Ele usa apenas três chamadas de método, incluindo o CAST final (), e não executa concatenação de cadeias, que é uma vantagem automática. Além disso, não há modelos enormes aqui. Se você pode imaginar que os carimbos de data / hora podem ser representados, a conversão de datas para números e de volta para datas é um processo bastante fácil.

2)

CREATE FUNCTION trunc_date(@date DATETIME)
RETURNS DATETIME
AS
BEGIN
      SELECT CONVERT(varchar, @date,112)
END

Se você está preocupado com a implementação de data / hora da Microsoft (2) ou (3), tudo bem.

(3)

CREATE FUNCTION trunc_date(@date DATETIME)
RETURNS DATETIME
AS
BEGIN
SELECT CAST((STR( YEAR( @date ) ) + '/' +STR( MONTH( @date ) ) + '/' +STR( DAY(@date ) )
) AS DATETIME
END

Terceiro, o método mais detalhado. Isso requer que a data seja dividida em partes do ano, mês e dia, reunindo-as no formato "aaaa / mm / dd" e, em seguida, convertendo-a novamente em uma data. Este método envolve 7 chamadas de método, incluindo o CAST final (), sem mencionar a concatenação de cadeias.

AlejandroR
fonte
1
CONVERT(DATE, <yourdatetime>) or CONVERT(DATE, GetDate()) or CONVERT(DATE, CURRENT_TIMESTAMP)
reitor
fonte
0

Para aqueles que vieram aqui procurando uma maneira de truncar um campo DATETIME para algo menos que um dia inteiro, por exemplo, a cada minuto, você pode usar o seguinte:

SELECT CAST(FLOOR(CAST(GETDATE() AS FLOAT)) + (FLOOR((CAST(GETDATE() AS FLOAT) - FLOOR(CAST(GETDATE() AS FLOAT))) * 1440.0) + (3.0/86400000.0)) / 1440.0 AS DATETIME)

então, se hoje fosse 2010-11-26 14:54:43.123, isso retornaria 2010-11-26 14:54:00.000.

Para alterar o intervalo para o qual ele substitui, substitua 1440.0 pelo número de intervalos em um dia, por exemplo:

24hrs          =   24.0  (for every hour)
24hrs / 0.5hrs =   48.0  (for every half hour)
24hrs / (1/60) = 1440.0  (for every minute)

(Sempre coloque um .0no final para converter implicitamente em um flutuador.)


Para aqueles que se perguntam para que servem os (3.0/86400000)meus cálculos, o SQL Server 2005 não parece ser transmitido FLOATcom DATETIMEprecisão, portanto, isso adiciona 3 milissegundos antes de ser aplicado.

BG100
fonte
1
Tenha cuidado com erros de arredondamento devido a limites de precisão de ponto flutuante ... além disso, isso não funciona com o datetime2tipo de dados.
Lucero
Para Hora, SELECT DATEADD (hora, DATEDIFF (hora, 0, GETDATE ()), 0) também funciona. Minuto também, mas o Segundo resultará em um estouro.
Kelly Cline
A conversão para flutuar e voltar para data e hora não funciona corretamente .
ErikE
0

Essa consulta deve fornecer um resultado equivalente ao trunc(sysdate)Oracle.

SELECT  * 
FROM    your_table
WHERE   CONVERT(varchar(12), your_column_name, 101)
      = CONVERT(varchar(12), GETDATE(), 101)

Espero que isto ajude!

Sandeep Gaadhe
fonte
0

Você também pode extrair a data using Substringda variável datetime e a conversão para datetime ignorará a parte do tempo.

declare @SomeDate datetime = '2009-05-28 16:30:22'
SELECT cast(substring(convert(varchar(12),@SomeDate,111),0,12) as Datetime) 

Além disso, você pode acessar partes da variável datetime e mesclá-las a uma data truncada de construção, algo como isto:

SELECT cast(DATENAME(year, @Somedate) + '-' + 
       Convert(varchar(2),DATEPART(month, @Somedate)) + '-' +
       DATENAME(day, @Somedate) 
       as datetime)
NeverHopeless
fonte
0

Oráculo:

TRUNC(SYSDATE, 'MONTH')

Servidor SQL:

DATEADD(DAY, - DATEPART(DAY, DateField) + 1, DateField)

Pode ser usado da mesma forma para truncar minutos ou horas a partir de uma data.

Markus
fonte
0

você poderia fazer isso (SQL 2008):

declarar @SomeDate date = getdate ()

select @SomeDate

28-05-2009

Hagai Danenberg-Lerner
fonte