while循环问题,计算期间的天数

时间:2018-09-26 19:15:23

标签: 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 |   
 | 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。删除限制将导致以下错误=消息512,级别16,状态1,第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

2 个答案:

答案 0 :(得分:2)

这是一种解决方案。对于需要涵盖gaps in date and time的查询,我保留了日期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)

答案 1 :(得分:0)

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)