날짜/시간 값의 시간 부분을 제거하는 방법(SQL Server)?
-
08-06-2019 - |
문제
내가 사용하는 것은 다음과 같습니다.
SELECT CAST(FLOOR(CAST(getdate() as FLOAT)) as DATETIME)
더 좋고 우아한 방법이 있을 수 있다고 생각합니다.
요구사항:
- 가능한 한 빨라야 합니다(캐스팅이 적을수록 좋습니다).
- 최종 결과는 다음과 같아야 합니다.
datetime
문자열이 아닌 유형을 입력하세요.
해결책
SQL 서버 2008 이상
물론 SQL Server 2008 이상에서 가장 빠른 방법은 Convert(date, @date)
.이는 다시 캐스팅될 수 있습니다. datetime
또는 datetime2
필요하다면.
SQL Server 2005 및 이전 버전에서 실제로 가장 좋은 것은 무엇입니까?
SQL Server의 날짜에서 시간을 자르는 가장 빠른 방법에 대한 일관되지 않은 주장을 본 적이 있으며 일부 사람들은 테스트를 했다고 말하기도 했지만 내 경험은 달랐습니다.따라서 좀 더 엄격한 테스트를 수행하고 모든 사람이 스크립트를 갖게 하여 내가 실수를 하면 사람들이 나를 수정할 수 있도록 합시다.
부동 소수점 변환이 정확하지 않습니다
첫째, 전환을 멀리하겠습니다. datetime
에게 float
, 올바르게 변환되지 않기 때문입니다.시간 제거 작업을 정확하게 수행하면 무사할 수도 있지만, 이것이 안전한 작업이고 개발자에게 암묵적으로 전달되기 때문에 사용하는 것은 좋지 않은 생각이라고 생각합니다. 그렇지 않다.구경하다:
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
이것은 우리의 코드나 온라인 예제를 통해 사람들에게 가르쳐야 할 내용이 아닙니다.
또한 가장 빠른 방법도 아닙니다!
증명 – 성능 테스트
다양한 방법이 실제로 어떻게 작동하는지 확인하기 위해 직접 몇 가지 테스트를 수행하려는 경우 테스트를 더 자세히 실행하려면 다음 설정 스크립트가 필요합니다.
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
이렇게 하면 데이터베이스에 427.57MB의 테이블이 생성되며 실행하는 데 15~30분 정도 소요됩니다.데이터베이스가 작고 10% 증가로 설정된 경우 먼저 크기를 충분히 크게 설정한 경우보다 시간이 더 오래 걸립니다.
이제 실제 성능 테스트 스크립트를 살펴보겠습니다.행을 클라이언트에 다시 반환하지 않는 것이 목적입니다. 이는 2,600만 개의 행에 엄청난 비용이 들고 메서드 간의 성능 차이를 숨길 수 있기 때문입니다.
성과 결과
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;
일부 혼란스러운 분석
이에 대한 몇 가지 참고 사항.우선, GROUP BY나 비교만 수행하는 경우 다시 변환할 필요가 없습니다. datetime
.따라서 표시 목적으로 최종 값이 필요하지 않은 한 이를 피함으로써 일부 CPU를 절약할 수 있습니다.변환되지 않은 값을 GROUP BY로 처리하고 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)
또한 숫자 변환이 다시 변환하는 데 시간이 약간 더 걸리는지 확인하세요. datetime
, 하지만 varchar
전환율이 거의 두 배가 되나요?이는 쿼리에서 날짜 계산에 사용되는 CPU 부분을 나타냅니다.CPU 사용량 중 날짜 계산과 관련되지 않은 부분이 있으며 이는 위 쿼리에서 19875ms에 가까운 것으로 보입니다.그런 다음 전환에는 약간의 추가 금액이 필요하므로 두 번의 전환이 있는 경우 해당 금액은 약 두 배로 사용됩니다.
더 많은 조사 결과에 비해 Convert(, 112)
, Convert(, 101)
쿼리에는 추가 CPU 비용이 발생합니다(더 긴 시간을 사용하기 때문에). varchar
?), 두 번째 변환이 다시 date
초기 전환 비용만큼 비용이 들지 않습니다. varchar
, 하지만 함께 Convert(, 112)
동일한 20000ms CPU 기본 비용에 더 가깝습니다.
위 분석에 사용한 CPU 시간 계산은 다음과 같습니다.
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
둥근 다시 왕복하는 데 소요되는 CPU 시간입니다.
datetime
.하나의 대체 데이터 유형(시간 부분을 제거하는 부작용이 있는 유형)으로의 단일 변환에 대한 CPU 시간입니다.
베이스 에서 빼는 계산입니다.
single
두 호출의 차이점은 다음과 같습니다.single - (round - single)
.해당 데이터 유형과의 변환을 가정하는 야구장 수치입니다.datetime
어느 방향에서나 거의 동일합니다.이 가정은 완벽하지는 않지만 한 가지 예외를 제외하고 값이 모두 20000ms에 가깝기 때문에 유사합니다.
또 하나 흥미로운 점은 기본 비용이 단일 비용과 거의 동일하다는 것입니다. Convert(date)
방법(서버가 내부적으로 처음 4바이트에서 바로 정수 일 부분을 추출할 수 있으므로 비용은 거의 0이어야 함) datetime
데이터 형식).
결론
그래서 보이는 것은 단일 방향입니다. varchar
변환 방법은 약 1.8μs가 소요되며 단방향 DateDiff
방법에는 약 0.18μs가 소요됩니다.이는 25,920,000개 행에 대해 총 18458ms의 테스트에서 가장 보수적인 '기본 CPU' 시간을 기반으로 하므로 23218ms / 25920000 = 0.18μs입니다.명백한 10배 개선은 많은 것처럼 보이지만 수십만 개의 행(617,000개 행 = 1초 절약)을 처리하기 전까지는 솔직히 매우 작습니다.
이 작은 절대적인 개선에도 불구하고 제 생각에는 DateAdd
방법은 성능과 명확성의 최상의 조합이기 때문에 승리합니다."마법의 숫자"가 필요한 대답 0.50000004
언젠가 누군가를 물게 될 것입니다 (0이 5개 또는 6???). 게다가 이해하기가 더 어렵습니다.
추가 참고 사항
시간나면 바꿔야지 0.50000004
에게 '12:00:00.003'
어떻게 되는지 확인해 보세요.동일하게 변환됩니다. datetime
가치가 있고 기억하기가 훨씬 쉽다고 생각합니다.
관심 있는 분들을 위해 위 테스트는 @@Version이 다음을 반환하는 서버에서 실행되었습니다.
Microsoft SQL Server 2008(RTM) - 10.0.1600.22(Intel X86) 2008년 7월 9일 14:43:34 Copyright (c) 1988-2008 Windows NT 5.2의 Microsoft Corporation Standard Edition(빌드 3790:서비스 팩 2)
다른 팁
SQL Server 2008에는 새로운 기능이 있습니다. 날짜 데이터 형식 이는 이 문제를 다음과 같이 단순화합니다.
SELECT CAST(CAST(GETDATE() AS date) AS datetime)
이직 벤간 DATETIME 계산, 1부 (SQL Server Magazine, 2007년 2월)에서는 이러한 변환을 수행하는 세 가지 방법을 보여줍니다.가장 느린 것에서 가장 빠른 것;두 번째 방법과 세 번째 방법의 차이는 작습니다.)
SELECT CAST(CONVERT(char(8), GETDATE(), 112) AS datetime)
SELECT DATEADD(day, DATEDIFF(day, 0, GETDATE()), 0)
SELECT CAST(CAST(GETDATE() - 0.50000004 AS int) AS datetime)
귀하의 기술(캐스팅 뜨다)은 잡지 4월호에 한 독자가 제안한 내용입니다.그에 따르면, 위에서 제시한 두 번째 기술과 비슷한 성능을 가지고 있다고 합니다.
당신의 CAST
-FLOOR
-CAST
적어도 MS SQL Server 2005에서는 이미 최적의 방법인 것 같습니다.
내가 본 다른 솔루션에는 다음과 같은 문자열 변환이 있습니다. Select Convert(varchar(11), getdate(),101)
그 속도는 10배 정도 느립니다.
시도해 보십시오:
SELECT CONVERT(VARCHAR(10),[YOUR COLUMN NAME],105) [YOURTABLENAME]
SQL2005:dateadd 대신 캐스트를 권장합니다.예를 들어,
select cast(DATEDIFF(DAY, 0, datetimefield) as datetime)
평균 10% 정도 더 빠르게 내 데이터세트에서는
select DATEADD(DAY, DATEDIFF(DAY, 0, datetimefield), 0)
(그리고 smalldatetime으로 캐스팅하는 것이 더 빨랐습니다)