【问题标题】:SQL Server - Efficient generation of dates in a rangeSQL Server - 有效生成范围内的日期
【发布时间】:2018-04-03 08:34:35
【问题描述】:

使用 SQL Server 2016。

我有一个存储过程,它针对一系列日期生成选项列表。为了清楚起见,针对天数的运输选项,但对此处的细节并不重要。

存储过程的第一步会生成一个日期列表以存储其他数据,生成此列表所花费的时间比代码的其余部分要长得多。虽然这个过程很短,但调用的数量意味着这段代码给系统带来的负载比其他任何东西都多。

考虑到这一点,我一直在测试几个选项的效率。

迭代公用表表达式:

CREATE FUNCTION [dbo].[udf_DateRange_CTE] (@StartDate DATE,@EndDate DATE)
RETURNS @Return TABLE (Date DATE NOT NULL)
AS
    BEGIN
        WITH    dates(date)
                  AS (SELECT    @StartDate [Date]
                      UNION ALL
                      SELECT    DATEADD(dd, 1, [Date])
                      FROM      dates
                      WHERE     [Date] < @EndDate
                     )
            INSERT  INTO @Return 
            SELECT  date
            FROM    dates
            OPTION  (MAXRECURSION 0)
        RETURN 
    END

一个while循环:

CREATE FUNCTION [dbo].[udf_DateRange_While] (@StartDate DATE,@EndDate DATE)
RETURNS @Retun TABLE (Date DATE NOT NULL,PRIMARY KEY (Date))
AS
    BEGIN
        WHILE @StartDate <= @EndDate
            BEGIN
                INSERT  INTO @Retun
                VALUES  (@StartDate)
                SET @StartDate = DATEADD(DAY,1,@StartDate)
            END
        RETURN 
    END

从预先填充的日期表中查找:

CREATE FUNCTION [dbo].[udf_DateRange_query] (@StartDate DATE,@EndDate DATE)
RETURNS @Return TABLE (Date DATE NOT NULL)
AS
    BEGIN
        INSERT  INTO @Return
        SELECT  Date
        FROM    DateLookup
        WHERE   Date >= @StartDate
                AND Date <= @EndDate
        RETURN 
    END

在效率方面,我已经测试了生成一年的日期,1000 次,结果如下:

  • CTE:10.0 秒
  • 同时:7.7 秒
  • 查询:2.6 秒

从这里查询绝对是更快的选择,但确实需要一个需要创建和维护的永久日期表。这意味着查询不再是“独立的”,并且可以请求给定日期范围之外的日期。

有谁知道为某个范围生成日期的任何更有效的方法,或者我可以对上述内容进行任何优化吗?

非常感谢。

【问题讨论】:

  • 它是自包含的,如果它是所有日期但那是很多日期

标签: sql sql-server performance query-tuning


【解决方案1】:

您可以尝试如下。这应该是比较快的CTEWHILE 循环。

DECLARE @StartDate DATETIME = Getdate() - 1000 
DECLARE @EndTime DATETIME = Getdate() 

SELECT * 
FROM   (SELECT @StartDate + RN AS DATE 
        FROM   (SELECT ROW_NUMBER() 
                         OVER ( 
                           ORDER BY (SELECT NULL)) RN 
                FROM   master..[spt_values]) T) T1 
WHERE  T1.DATE <= @EndTime 
ORDER  BY DATE 

注意:这适用于日差

如果你想支持更多的范围,你可以在master..[spt_values] 上使用CROSS JOIN 来生成0 - 6436369 天之间的范围,如下所示。

DECLARE @StartDate DATETIME = Getdate() - 10000
DECLARE @EndTime DATETIME = Getdate() 
SELECT @StartDate + RN AS DATE FROM
(   
    SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) RN 
    FROM   master..[spt_values] T1
    CROSS JOIN  master..[spt_values] T2
) T 
WHERE RN <= DATEDIFF(DAY,@StartDate,@EndTime)

【讨论】:

  • 我已经使用与我的原始方法相同的方法检查了这一点,并且几乎与我使用的表查找一样有效。 2.7秒进入。我对此投了赞成票,如果没有更好的结果,我将标记为已接受的答案。非常感谢,这是一个不错的选择。
  • 我刚刚使用了这个解决方案,并将其推送给我们的测试人员。不过,我发现了一个小问题,该列表不包括开始日期。我在您的解决方案中添加了一个 SELECT @Startdate UNION ALL,效果很好。
  • 没错,但需要对每一行进行 -1 操作,对 WHERE 进行 +1 操作。效率受到严重影响,但工会需要的资源更少。不过里面的内容太少了。
猜你喜欢
  • 2012-02-21
  • 1970-01-01
  • 1970-01-01
  • 2012-02-26
  • 1970-01-01
  • 2010-09-29
  • 1970-01-01
  • 2017-03-22
  • 2018-12-06
相关资源
最近更新 更多