SQL Server 2008 e superior
No SQL Server 2008 e superior, é claro que a maneira mais rápida é
Convert(date, @date)
. Isso pode ser convertido de volta para um datetime
ou datetime2
se necessário. O que é realmente melhor no SQL Server 2005 e versões anteriores?
Já vi afirmações inconsistentes sobre o que é mais rápido para truncar o tempo de uma data no SQL Server, e algumas pessoas até disseram que fizeram testes, mas minha experiência foi diferente. Então, vamos fazer alguns testes mais rigorosos e deixar que todos tenham o script para que, se eu cometer algum erro, as pessoas possam me corrigir.
As conversões flutuantes não são precisas
Primeiro, eu ficaria longe de converter
datetime
para float
, porque não converte corretamente. Você pode se safar fazendo a remoção de tempo com precisão, mas acho que é uma má ideia usá-lo porque ele comunica implicitamente aos desenvolvedores que esta é uma operação segura e não é . Dê uma olhada:declare @d datetime;
set @d = '2010-09-12 00:00:00.003';
select Convert(datetime, Convert(float, @d));
-- result: 2010-09-12 00:00:00.000 -- oops
Isso não é algo que devemos ensinar às pessoas em nosso código ou em nossos exemplos online.
Além disso, nem é o caminho mais rápido!
Prova – Teste de desempenho
Se você quiser realizar alguns testes para ver como os diferentes métodos realmente se acumulam, precisará deste script de configuração para executar os testes mais abaixo:
create table AllDay (Tm datetime NOT NULL CONSTRAINT PK_AllDay PRIMARY KEY CLUSTERED);
declare @d datetime;
set @d = DateDiff(Day, 0, GetDate());
insert AllDay select @d;
while @@ROWCOUNT != 0
insert AllDay
select * from (
select Tm =
DateAdd(ms, (select Max(DateDiff(ms, @d, Tm)) from AllDay) + 3, Tm)
from AllDay
) X
where Tm < DateAdd(Day, 1, @d);
exec sp_spaceused AllDay; -- 25,920,000 rows
Observe que isso cria uma tabela de 427,57 MB em seu banco de dados e levará cerca de 15 a 30 minutos para ser executado. Se seu banco de dados for pequeno e estiver configurado para um crescimento de 10%, levará mais tempo do que se você fosse grande o suficiente primeiro.
Agora, para o script de teste de desempenho real. Observe que é proposital não retornar linhas de volta ao cliente, pois isso é muito caro em 26 milhões de linhas e ocultaria as diferenças de desempenho entre os métodos.
Resultados de desempenho
set statistics time on;
-- (All queries are the same on io: logical reads 54712)
GO
declare
@dd date,
@d datetime,
@di int,
@df float,
@dv varchar(10);
-- Round trip back to datetime
select @d = CONVERT(date, Tm) from AllDay; -- CPU time = 21234 ms, elapsed time = 22301 ms.
select @d = CAST(Tm - 0.50000004 AS int) from AllDay; -- CPU = 23031 ms, elapsed = 24091 ms.
select @d = DATEDIFF(DAY, 0, Tm) from AllDay; -- CPU = 23782 ms, elapsed = 24818 ms.
select @d = FLOOR(CAST(Tm as float)) from AllDay; -- CPU = 36891 ms, elapsed = 38414 ms.
select @d = CONVERT(VARCHAR(8), Tm, 112) from AllDay; -- CPU = 102984 ms, elapsed = 109897 ms.
select @d = CONVERT(CHAR(8), Tm, 112) from AllDay; -- CPU = 103390 ms, elapsed = 108236 ms.
select @d = CONVERT(VARCHAR(10), Tm, 101) from AllDay; -- CPU = 123375 ms, elapsed = 135179 ms.
-- Only to another type but not back
select @dd = Tm from AllDay; -- CPU time = 19891 ms, elapsed time = 20937 ms.
select @di = CAST(Tm - 0.50000004 AS int) from AllDay; -- CPU = 21453 ms, elapsed = 23079 ms.
select @di = DATEDIFF(DAY, 0, Tm) from AllDay; -- CPU = 23218 ms, elapsed = 24700 ms
select @df = FLOOR(CAST(Tm as float)) from AllDay; -- CPU = 29312 ms, elapsed = 31101 ms.
select @dv = CONVERT(VARCHAR(8), Tm, 112) from AllDay; -- CPU = 64016 ms, elapsed = 67815 ms.
select @dv = CONVERT(CHAR(8), Tm, 112) from AllDay; -- CPU = 64297 ms, elapsed = 67987 ms.
select @dv = CONVERT(VARCHAR(10), Tm, 101) from AllDay; -- CPU = 65609 ms, elapsed = 68173 ms.
GO
set statistics time off;
Algumas análises desconexas
Algumas notas sobre isso. Em primeiro lugar, se estiver apenas realizando um GROUP BY ou uma comparação, não há necessidade de converter novamente para
datetime
. Assim, você pode economizar um pouco de CPU evitando isso, a menos que precise do valor final para fins de exibição. Você pode até GROUP BY o valor não convertido e colocar a conversão apenas na cláusula SELECT:select Convert(datetime, DateDiff(dd, 0, Tm))
from (select '2010-09-12 00:00:00.003') X (Tm)
group by DateDiff(dd, 0, Tm)
Além disso, veja como as conversões numéricas levam um pouco mais de tempo para serem convertidas de volta para
datetime
, mas o varchar
conversão quase dobra? Isso revela a parte da CPU que é dedicada ao cálculo de data nas consultas. Há partes do uso da CPU que não envolvem cálculo de data, e isso parece ser algo próximo a 19.875 ms nas consultas acima. Em seguida, a conversão leva algum valor adicional, portanto, se houver duas conversões, esse valor será usado aproximadamente duas vezes. Mais exames revelam que, em comparação com
Convert(, 112)
, o Convert(, 101)
consulta tem alguma despesa de CPU adicional (uma vez que usa um varchar
mais longo ?), porque a segunda conversão volta para date
não custa tanto quanto a conversão inicial para varchar
, mas com Convert(, 112)
está mais próximo do mesmo custo básico de CPU de 20.000 ms. Aqui estão os cálculos sobre o tempo de CPU que usei para a análise acima:
method round single base
----------- ------ ------ -----
date 21324 19891 18458
int 23031 21453 19875
datediff 23782 23218 22654
float 36891 29312 21733
varchar-112 102984 64016 25048
varchar-101 123375 65609 7843
-
rodada é o tempo de CPU para uma viagem de ida e volta paradatetime
.
-
único é o tempo de CPU para uma única conversão para o tipo de dados alternativo (aquele que tem o efeito colateral de remover a parte do tempo).
-
base é o cálculo da subtração desingle
a diferença entre as duas invocações:single - (round - single)
. É um valor aproximado que assume a conversão de e para esse tipo de dados edatetime
é aproximadamente o mesmo em qualquer direção. Parece que essa suposição não é perfeita, mas é próxima porque os valores estão todos próximos de 20.000 ms, com apenas uma exceção.
Mais uma coisa interessante é que o custo base é quase igual ao único
Convert(date)
(que tem que ser quase 0 de custo, pois o servidor pode extrair internamente a parte do dia inteiro dos primeiros quatro bytes do datetime
tipo de dados). Conclusão
Então, o que parece é que o
varchar
de direção única o método de conversão leva cerca de 1,8 μs e o DateDiff
de direção única método leva cerca de 0,18 μs. Estou baseando isso no tempo de "CPU base" mais conservador em meus testes de 18.458 ms no total para 25.920.000 linhas, portanto, 23.218 ms / 25920.000 =0,18 μs. A aparente melhoria de 10x parece muito, mas é francamente muito pequena até que você esteja lidando com centenas de milhares de linhas (617k linhas =1 segundo de economia). Mesmo com essa pequena melhoria absoluta, na minha opinião, o
DateAdd
método ganha porque é a melhor combinação de desempenho e clareza. A resposta que requer um "número mágico" de 0.50000004
vai morder alguém algum dia (cinco zeros ou seis???), além de ser mais difícil de entender. Observações adicionais
Quando eu tiver algum tempo vou mudar
0.50000004
para '12:00:00.003'
e veja como fica. Ele é convertido para o mesmo datetime
valor e acho muito mais fácil de lembrar. Para os interessados, os testes acima foram executados em um servidor onde @@Version retorna o seguinte:
Microsoft SQL Server 2008 (RTM) - 10.0.1600.22 (Intel X86) 9 de julho de 2008 14:43:34 Copyright (c) 1988-2008 Microsoft Corporation Standard Edition no Windows NT 5.2 (Build 3790:Service Pack 2)