【问题标题】:while loop problem, calculate days from periodwhile循环问题,从期间计算天数
【发布时间】:2019-03-02 15:24:05
【问题描述】:

餐桌顺序

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 限制的情况下运行查询。删除限制将导致以下错误 = Msg 512, Level 16, State 1, Line 5 子查询返回超过 1 个值。当子查询跟随 =、!=、、>= 或子查询用作表达式时,这是不允许的。

    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

【问题讨论】:

  • 从您发布的代码中,我可以看到发生这种情况的唯一地方是您设置“开始”和“结束”日期的位置。但我会问你为什么要循环执行此操作?这应该是一个单一的查询。此外,避免使用“Order”等保留字作为对象名称。工作很痛苦。
  • 不要为此使用循环。

标签: sql-server while-loop


【解决方案1】:

这是一种解决方案。对于需要覆盖gaps in date and time 的查询,我会保留一个日期 CTE sn-p。如果您将所有日期都放入临时存储中,那么您的查询将变得更自然且可读。

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)

【讨论】:

  • 感谢罗斯的帮助。该解决方案适用于 3 个 OrderID,但如果我扩展到所有 OrderID,我会遇到 MAXRECURSION 问题。
  • 该值可以从 0..32,767 开始。但是,如果您要定期编写这样的查询,那么我建议您创建一个日历表来保存所有日期,以便有足够大的范围来满足您的需求。
  • 嗨罗斯,如果期限超过 2 年,解决方案会给出错误的结果。示例 2015-07-19--2016-07-18
  • 天数计算错误。上例显示 2016 年第 1 个月 = 30 天(应为 31)
  • @Mag 我已更新,因此 Calendar.CalendarDate 被计算为一天的结束而不是一天的开始。这应该可以解决休息日问题
【解决方案2】:
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)  

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-07-23
    • 1970-01-01
    • 2012-03-23
    • 2011-06-19
    • 2015-01-04
    • 1970-01-01
    • 2011-03-04
    • 1970-01-01
    相关资源
    最近更新 更多