2012-10-31 3 views
2

일부 날짜 간격을 분으로 나누어야합니다. (예 : 2012-01-01 10:00 - 2012-01-01 10:00 간격은 2012-01-01 10:01, 2012-01-01 10:02, ... 2012-01로 나뉩니다. -01 10:10). 예를 들어,SQL Server에서 날짜 간격을 분 단위로 나누십시오.

CREATE TABLE [dbo].[Events](
    [ID] [int] IDENTITY(1,1) NOT NULL, 
    [EventStart] [datetime] NOT NULL, 
    [EventEnd] [datetime] NOT NULL, 
    [Amount] [float] NOT NULL, 
CONSTRAINT [PK_Events] PRIMARY KEY CLUSTERED 
(
    [ID] ASC 
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] 
) ON [PRIMARY] 

GO 

이 표는 그 결과

DECLARE @i integer = 0; 
DECLARE @initial_date datetime = '2012-01-01'; 

WHILE @i < 50000 
BEGIN 
    INSERT INTO [Events] (EventStart, EventEnd, Amount) VALUES (DATEADD(MINUTE, 10*@i, @initial_date), DATEADD(MINUTE, 10*(@i + 1), @initial_date), @i); 
    SET @i = @i + 1; 
END 

처럼 가득 테이블 우리는 많은 10 분 간격을 가지고있다.

나는 다음과 같은 재귀 CTE를 사용하는 분을 분할하려면

DECLARE @start_date datetime = '2012-01-01'; 
DECLARE @end_date datetime = '2013-01-02'; 


WITH Date_Ranges (StatDate, Amount, IntervalStart, CurrentMinute) AS (
    SELECT 
    DATEADD(MINUTE, 0, ev.EventStart) AS StatDate, ev.Amount, ev.EventStart AS IntervalStart, 1 AS CurrentMinute 
    FROM [Events] ev 
    WHERE ev.EventStart BETWEEN @start_date AND @end_date 
    UNION ALL 
    SELECT 
    DATEADD(MINUTE, CurrentMinute, ev.EventStart), ev.Amount, ev.EventStart AS IntervalStart, CurrentMinute + 1 
    FROM [Events] ev 
    INNER JOIN Date_Ranges ranges ON (ranges.IntervalStart = ev.EventStart AND 
    ranges.StatDate >= ev.EventStart AND 
    ranges.StatDate < ev.EventEnd) 
    WHERE DATEADD(MINUTE, CurrentMinute, ev.EventStart) BETWEEN @start_date AND @end_date AND 
     ev.EventStart BETWEEN @start_date AND @end_date 
) 

SELECT * 
FROM Date_Ranges --ORDER BY StatDate 

가장 큰 문제는 많은 데이터 양에 대한이 재귀 CTE의 너무 느린 실행합니다.

그럼 어떻게 속도를 높일 수 있습니까?

답변

2

이렇게하면 재귀 CTE의 대략 1/2 시간에 550,000 개의 행이 모두 반환됩니다.

DECLARE @start_date datetime = '2012-01-01'; 
DECLARE @end_date datetime = '2013-01-02'; 

SELECT DATEADD(MINUTE, x.number, ev.EventStart) AS StartDate, 
     ev.Amount, 
     ev.EventStart as IntervalStart, 
     x.number as CurrentMinute 
FROM master.dbo.spt_values x 
CROSS JOIN Events ev 
WHERE x.type = 'P'   
AND  x.number <= DATEDIFF(MINUTE, ev.EventStart, ev.EventEnd) 
AND  ev.EventStart BETWEEN @start_date and @end_date 
+0

고맙습니다. 그것이 내가 필요한 것입니다. 실제 데이터에서 여러 번 CTE보다 재귀 적으로 빠릅니다. – eternity

1

버킷의 가장 빠른 세트는 테이블이 될 것이라고 생각합니다. 10 분 버킷의 테이블을 만들고 채우고 합치십시오. 이렇게하면 재귀를 완전히 피할 수 있으며 SQL dbms가 실제로 잘 작동하는 것 중 하나 인 조인을 활용할 수 있습니다.

10 분 버킷 10 년 만 50 만 행입니다.

CTE에서 재귀는 BOM과 같은 것을 처리 할 때 좋은 일입니다. 그러나 항상 테이블을 대체하는 것은 아닙니다.


나는 10 년 동안 버킷 테이블을 만들었습니다. (약 4 메가 ​​바이트의 데이터입니다. 디스크 인덱스 및 행 오버 헤드가 얼마나되는지 계산하지 않았습니다.) 그리고 나서, 양동이 테이블과 동일한 10 년 내에 2 천만 개의 무작위 타임 스탬프가 포함 된 테스트 데이터 테이블을 만들었습니다 .

문제에 적절한 색인을 추가 한 후 테스트 시스템은 약 100ms 내에 임의의 하루 데이터를 "버킷으로 처리"합니다. (1 기가의 RAM을 장착 한 5 년 된 Dell 컴퓨터에서 튜닝하지 않고 PostgreSQL dbms를 실행합니다.) 저는 리눅스 시스템을 사용 중이기 때문에 SQL Server 자체를 테스트 할 수 없었습니다.)

+0

불행하게도, 내가는 SQL Express를 사용하고 디스크 공간이 너무 MEE : 1 분 버킷 – eternity

+0

10 년 동안 중요한 것은 데이터를 적은 45 메가 바이트. –

+0

네, 이해합니다. 이 예제는 단순화되었습니다. 실제 데이터 양은 훨씬 더 많습니다. – eternity

관련 문제