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)。例如,有一张桌子

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 太慢。

那么,我怎样才能加快速度呢?

4

2 回答 2

2

这将在大约 1/2 的递归 CTE 时间内返回所有 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
于 2012-10-31T11:26:09.823 回答
1

我认为最快的一组桶将是一张桌子。创建一个包含 10 分钟存储桶的表,填充它,然后加入它。这完全避免了递归,并利用了 SQL dbms 真正擅长的一件事——连接。

10 年的 10 分钟存储桶只有 50 万行。

当您处理诸如物料清单之类的东西时,CTE 中的递归是一件好事。但它并不总是适合替代桌子。


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

添加适合问题的索引后,测试系统在大约 100 毫秒内“存储”随机一天的数据。(没有调整的 PostgreSQL dbms,运行在一台 5 年前的戴尔计算机上,具有 1 gig 的 RAM。我在这里使用的是 Linux 系统,所以我无法测试 SQL Server 本身。不过,我希望得到类似的结果。 )

于 2012-10-31T10:55:13.710 回答