Sql server 当循环出现问题时,从周期开始计算天数

Sql server 当循环出现问题时,从周期开始计算天数,sql-server,while-loop,Sql Server,While Loop,表顺序 OrderID | FromDate | ToDate 4523691 | 2015-01-23 | 2015-04-22 4523692 | 2015-05-07 | 2015-06-23 4523693 | 2015-02-09 | 2015-05-08 决定性结果 | OrderID | Year | Month | Days | | 4523691 | 2015 | 1 | 9 | | 452

表顺序

OrderID | FromDate   | ToDate    
4523691 | 2015-01-23 | 2015-04-22     
4523692 | 2015-05-07 | 2015-06-23  
4523693 | 2015-02-09 | 2015-05-08
决定性结果

 | OrderID  |  Year  |  Month  |  Days  |      
 | 4523691  |  2015  |   1     |   9 |   
 | 4523691  |  2015  |   2     |   28 |   
 | 4523691  |  2015  |   3     |   31 |   
 | 4523691  |  2015  |   4     |   22 |   
 | 4523692  |  2015  |   5     |   25 |   
 | 4523692  |  2015  |   6     |   23 |   
 | 4523693  |  2015  |   2     |   20 |   
 | 4523693  |  2015  |   3     |   31 |   
 | 4523693  |  2015  |   4     |   30 |   
 | 4523693  |  2015  |   5     |   8 | 
如果对每个OrderID使用where语句运行脚本,则该脚本可以正常工作。这就是我需要帮助的地方,在不限制订单ID的情况下运行quire。删除限制将导致以下错误=Msg 512,级别16,状态1,第5行 子查询返回了多个值。当子查询在=、!=、=或者当子查询用作表达式时

    DECLARE @FromDate as datetime  
    DECLARE @Todate as date  
    DECLARE @Month as date  

    SET @FromDate = (select fromdate from Order where orderid = '4523693')
    SET @ToDate = (select todate from Order where orderid = '4523693')  
    SET @Month = @FromDate  

    WHILE (eomonth(@Month) <= eomonth(@ToDate))  
    BEGIN  
    SELECT 
    OrderID
    ,year(dateadd(month, 0, eomonth(@Month)))
    ,month(dateadd(month, 0, eomonth(@Month)))
    ,case
    when eomonth(@Month) = eomonth(fromdate) then datediff(d, fromdate, eomonth(fromdate))+1       
    when eomonth(@Month) = eomonth(todate) then datediff(day,DATEADD(m, DATEDIFF(m, 0, todate), 0) , todate)+1
    else DATEPART(dd, DATEADD(dd, DATEPART(dd, DATEADD(mm, 1, dateadd(month, 0, eomonth(@Month)))) * -1, DATEADD(mm, 1, dateadd(month, 0, eomonth(@Month)))))
    end as 'Days'
    FROM Order
    WHERE dateadd(month, 0, eomonth(fromdate)) <= eomonth(todate)
    AND FROMDATE IS NOT NULL
    AND ORDERID = '4523693'
    SET @Month = dateadd(month, 1, eomonth(@Month))
END

这里有一个解决方案。我保留了一个日期CTE片段,用于需要覆盖日期和时间间隔的查询。如果您将所有日期倒入临时存储中,那么您的查询将变得更自然,也更可读

DECLARE @Orders TABLE(OrderID INT,FromDate DATETIME,ToDate DATETIME)
INSERT @Orders VALUES (100,'01/23/2015','04/22/2015'),(200,'05/07/2015','06/23/2015'),(300,'02/09/2015','05/08/2015')

DECLARE @StartDate DATETIME = (SELECT MIN(FromDate) FROM @Orders)
DECLARE @EndDate DATETIME = (SELECT MAX(ToDate) FROM @Orders)

;WITH Calendar as 
( 
    SELECT CalendarDate = @StartDate, CalendarYear = DATEPART(YEAR, @StartDate), CalendarMonth = DATEPART(MONTH, @StartDate) 
    UNION ALL 
    SELECT CalendarDate = DATEADD(MILLISECOND, -2, DATEADD(DAY, 1, DATEDIFF(dd, 0, DATEADD(DAY, 1, CalendarDate)))), CalendarYear = DATEPART(YEAR, CalendarDate), CalendarMonth = DATEPART(MONTH, DATEADD(DAY, 1, CalendarDate))        FROM Calendar WHERE DATEADD (DAY, 1, CalendarDate) <= @EndDate 
)

SELECT 
    OrderID,
    CalendarYear,
    CalendarMonth,
    Days = COUNT(*)
FROM
    Calendar C
    LEFT JOIN @Orders O ON C.CalendarDate BETWEEN O.FromDate AND O.ToDate 
GROUP BY
    OrderID,CalendarYear,CalendarMonth,O.FromDate,O.ToDate
ORDER BY
    O.OrderID,O.FromDate,O.ToDate
OPTION (MAXRECURSION 1000)

从您发布的代码中,我唯一能看到这种情况发生的地方是您设置起始日期和截止日期的地方。但我想问你为什么要这样做?这应该是一个查询。另外,避免使用Order之类的保留字作为对象名。工作起来很痛苦。不要为此使用循环。谢谢罗斯的帮助。该解决方案对于3个OrderID非常有效,但是如果我扩展到所有OrderID,就会出现MAXRECURSION问题。该值可以是0..32767。但是,如果你打算定期编写这样的查询,那么我建议你创建一个日历表,以容纳足够大范围的所有日期,以满足你的需要。嗨,Ross,如果周期超过2年,解决方案会给出错误的结果。示例2015-07-19-2016-07-18和天数计算错误。上述示例显示2016年,第1个月=30天应为31@Mag我进行了更新,因此Calendar.CalendarDate计算为一天的结束,而不是一天的开始。这将解决休假问题
SET @FromDate = (select fromdate from Order where orderid = '4523693')
    SET @ToDate = (select todate from Order where orderid = '4523693')  

Change this to 
SET @FromDate = (select top 1 fromdate from Order where orderid = '4523693' order by fromdate desc )
    SET @ToDate = (select  top 1 todate from Order where orderid = '4523693' order by todate desc)