我需要一个带有日期的sql查询循环,按天递减,直到中断日期。

1 人不认可

我需要一个日期按天递减的sql查询循环。

StartDate : 3/15/2015    [Date param] [MM/dd/yyyy]
EndDate   : 3/5/2015     [Date param]  
Operation : Decrement by a day toward EndDate
BreakDate : 3/10/2015    [Date param]

当前日期(在循环中)应该从开始日期到结束日期按天递减打印。

如果达到中断日期,那么循环应该停止[内部循环]。

上述输入的结果样本。

3/15/2015 
3/14/2015 
3/13/2015 
3/11/2015 
3/10/2015 

请帮助我们。

3 个评论
你的意思是 "打印",如 "使用打印语句",而不是 "作为查询结果返回"? 因为打印它确实需要一个循环,但作为查询结果返回它则需要一个递归的CTE。
gvee
使用一个日历表。那么答案就无比简单了。 SELECT the_date FROM dbo.calendar WHERE ...
不知道你为什么要选择最差的答案。循环是个坏主意。选择@GarethD的答案。那会让我睡得更好
sql-server
sql-server-2008
Senthil Arumugam
Senthil Arumugam
发布于 2015-03-05
4 个回答
GarethD
GarethD
发布于 2015-03-05
0 人赞同

你不需要一个循环。你 永远不 应该用这种心态来处理SQL中的问题,它应该是最后的手段。

如果要处理日期问题,最简单的办法是使用 日历表 ,如果你没有日历表,就创建一个,然后你可以直接使用。

DECLARE @StartDate DATE = '20150315',
        @EndDate DATE = '20150305',
        @BreakDate DATE = '20150310';
SELECT  Date
FROM    Calendar
WHERE   Date <= @StartDate
AND     Date > @EndDate
AND     Date > @BreakDate;

然而,我知道创建一个日历表并不总是一种选择,但在飞行中生成一个日期列表是非常容易的。从以下文章中可以看出。

  • 生成一个没有循环的集合或序列--第一部分
  • 生成一个没有循环的集合或序列--第二部分
  • 生成一个没有循环的集合或序列--第3部分
  • 最好的方法是使用常数的交叉连接(在文章中被称为堆叠CTE)。这只是从一个10行的表值构造器开始,将其与自身交叉连接,得到100行,然后再得到100x100=10,000行,以此类推。

    DECLARE @StartDate DATE = '20150315',
            @EndDate DATE = '20150305',
            @BreakDate DATE = '20150310';
    WITH N1 (N) AS (SELECT 1 FROM (VALUES (1), (1), (1), (1), (1), (1), (1), (1), (1), (1)) n (N)),
    N2 (N) AS (SELECT 1 FROM N1 AS N1 CROSS JOIN N1 AS N2),
    N3 (N) AS (SELECT 1 FROM N2 AS N1 CROSS JOIN N2 AS N2),
    N4 (N) AS (SELECT ROW_NUMBER() OVER(ORDER BY N1.N) FROM N3 AS N1 CROSS JOIN N3 AS N2)
    SELECT  Date = DATEADD(DAY, 1 - N, @StartDate)
    FROM    N4
    WHERE   N <= DATEDIFF(DAY, @EndDate, @StartDate) + 1
    AND     N <= DATEDIFF(DAY, @BreakDate, @StartDate) + 1;
    

    如果你的中断日期可以在开始日期之后,那么你只需要一个额外的逻辑来解决这个问题,这样你的查询就变成了。

    DECLARE @StartDate DATE = '20150315',
            @EndDate DATE = '20150305',
            @BreakDate DATE = '20150310';
    SELECT  Date
    FROM    Calendar
    WHERE   Date <= @StartDate
    AND     Date > @EndDate
    AND (   Date > @BreakDate
        OR  @BreakDate >= @StartDate
    
    DECLARE @StartDate DATE = '20150315',
            @EndDate DATE = '20150305',
            @BreakDate DATE = '20150320';
    WITH N1 (N) AS (SELECT 1 FROM (VALUES (1), (1), (1), (1), (1), (1), (1), (1), (1), (1)) n (N)),
    N2 (N) AS (SELECT 1 FROM N1 AS N1 CROSS JOIN N1 AS N2),
    N3 (N) AS (SELECT 1 FROM N2 AS N1 CROSS JOIN N2 AS N2),
    N4 (N) AS (SELECT ROW_NUMBER() OVER(ORDER BY N1.N) FROM N3 AS N1 CROSS JOIN N3 AS N2)
    SELECT  Date = DATEADD(DAY, 1 - N, @StartDate)
    FROM    N4
    WHERE   N <= DATEDIFF(DAY, @EndDate, @StartDate) + 1
    AND (   N <= DATEDIFF(DAY, @BreakDate, @StartDate) + 1
        OR  @BreakDate >= @StartDate
        
    你犯了和我当初一样的错误。试试在'20150320'处设置一个断裂日期,它将排除所有的行。
    gvee
    很容易就找到了最好的方法。
    gvee
    @t-clausen.dk 简单修复: IF @BreakDate <= @EndDate BEGIN SET @EndDate = @BreakDate END ,然后删除WHERE子句的最后一点。
    gvee
    @t-clausen.dk 你可能错过了我的编辑:需要删除WHERE子句中提到BreakDate的部分。基本上,我们要的是开始日期和结束日期中最先出现的日期之间的日子。如果我们计算出哪一个先到,我们就把它作为我们的上限。 :-)
    @gvee 没有,没有看到你的编辑,评论已被删除。然而,是什么使这种方法比我的好得多?除了统计表之外,其他的都差不多。
    t-clausen.dk
    t-clausen.dk
    发布于 2015-03-05
    0 人赞同

    这个解决方案它被限制在2048天,但可以用不同的理财方式来扩展。

    SELECT cast(dateadd(day, -number, @startdate) as date) date
    FROM master..spt_values
    WHERE 
      type = 'P' 
      AND dateadd(day, -number, @StartDate) >= @EndDate
      AND @BreakDate NOT BETWEEN dateadd(day, 1 - number, @StartDate) AND @startdate
        
    SelvaS
    SelvaS
    发布于 2015-03-05
    已采纳
    0 人赞同

    你可以通过 WHILE Loop来做到这一点。但在进行循环之前,请考虑 RBAR

    这将给出预期的准确输出。

    DECLARE @FromDate DATE = '3/15/2015', 
            @EndDate DATE = '3/5/2015',
            @BreakDate DATE = '3/10/2015' 
    WHILE (@FromDate >= @endDate)
    BEGIN
        PRINT @FromDate
        -- Perform your operations here
        IF(@FromDate = @BreakDate)
            Break;
        --Incrementing to next date
        SELECT @FromDate = DATEADD(DAY, -1, @FromDate)
        
    cypizek
    cypizek
    发布于 2015-03-05
    0 人赞同

    家庭作业?)

    DECLARE @dateStart SMALLDATETIME = '20150315'
    DECLARE @dateEnd SMALLDATETIME = '20150305'
    DECLARE @dateBreak SMALLDATETIME = '20150310'
    WHILE (@dateStart>@dateEnd)
        BEGIN