2012-10-31 20 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中的递归是一件好事。但它并不总是表格的合适替代品。


我创建了一个包含10年10分钟桶的表格。 (这是大约4兆字节的数据,我没有试图计算出多少磁盘索引和行开销)。然后我创建了一个包含2000万个随机时间戳的测试数据表,这些数据全部在与桶表相同的10年内。

在添加适合问题的索引之后,测试系统在大约100ms内“随机”存储一个随机日数据。 (PostgreSQL dbms没有进行调整,在一台5年的戴尔计算机上运行,​​内存为1G内存,我在Linux系统上,所以我无法测试SQL Server本身,但我期望类似的结果。 )

+0

不幸的是,我使用的SQL Express和磁盘空间是MEE太:) – eternity

+0

10年1分钟桶关键的是小于45兆字节的数据。 –

+0

是的,我明白。这个例子被简化了。实际的数据量要多得多。 – eternity