检查SQL中的日期范围

时间:2013-06-20 11:21:43

标签: sql-server tsql

继昨天提出的问题之后,我需要为笔记本电脑推出“预订系统”返回一系列“可用”日期。我想通过检查每个日期可能的总插槽数,并减去已经预订的插槽数来填充用户可以预订插槽的可用日期表。

逻辑如下:

  • 技术人员每天可以制作3台笔记本电脑。
  • 在任何一天,可能有1,2或3名技术人员。
  • 表格包含预订
  • 我不想要一张包含所有可能日期的表格,我想要动态计算

相关表格是:

tl_sb_slotBooking 这包含已经预订的

enter image description here

tl_sb_availabilityPeriods 这用于计算给定日期的可用插槽总数 enter image description here

我可以带回一个固定最大数量(在这种情况下为3)的插槽日期列表:

    DECLARE @startDate DATE
    DECLARE @endDate DATE

    SET @startDate = GETDATE()
    SET @endDate = DATEADD(m,3,@startDate)
    ;
    WITH dates(Date) AS 
    (
       SELECT @startdate as Date
       UNION ALL
       SELECT DATEADD(d,1,[Date])
         FROM dates 
         WHERE DATE < @enddate
    )

    SELECT Date
      FROM dates 
    EXCEPT
    SELECT date
    FROM tl_sb_booking
    GROUP BY date
    HAVING COUNT(date) >= 3

但是,最大值并不总是3,每天都会更改。

我可以找到给定日期的最大可能插槽:

    DECLARE @myDate DATETIME = '2013-06-22'

    SELECT SUM(laptopsPerDay) AS totalSlots
       FROM tl_sb_technicianAvailability
       WHERE startDate <= @myDate AND endDate >= @myDate
       AND availabiltyStateID=3

它将带回6作为2013-06-22可用的插槽总数。 (availabilityStateID字段用于存储可用/不可用等。)

所以,我坚持的一点是将两者结合起来。

我想要的是每个可能的日期,如果已经预订的插槽数量少于当天可能的插槽数量,请将其添加到要返回的表格中(否则不要)。

2 个答案:

答案 0 :(得分:2)

首先,虽然你只是生成一个小列表using a CTE to generate a sequential list performs terribly,但最好避免使用。

为此,我将使用系统表Master..spt_values作为序列号的列表,但如果您担心使用未记录的系统表,那么上面的链接中还有其他方法。

我要做的第一件事就是将技术人员的可用日期分成每天一行,这样就可以让技术人员只需要部分的peiod(例如,如果你想查询,可以从表格中获取)从6月18日到6月26日,没有任何技术人员使用您发布的查询显示为可用:

SELECT  Date = DATEADD(DAY, spt.Number, ta.StartDate),
        ta.TechnicianID,
        ta.LapTopsPerDay
FROM    tl_sb_technicianAvailability ta
        INNER JOIN Master..spt_values spt
            ON spt.Type = 'P'
            AND spt.Number BETWEEN 0 AND DATEDIFF(DAY, ta.startDate, ta.EndDate)

This would simply turn:

TechnicianID    StartDate   EndDate     LapTopsPerDay
1               20130620    20130624    3

Date        TechnicianID    LapTopsPerDay
20130620    1               3
20130621    1               3
20130622    1               3
20130623    1               3
20130624    1               3

然后,您可以将此列表限制为所需的日期范围,并总结笔记本电脑的总数,因为在技术级别上不需要这样做:

WITH ExplodedAvailability AS
(       SELECT  Date = DATEADD(DAY, spt.Number, ta.StartDate),
                ta.TechnicianID,
                ta.LapTopsPerDay
        FROM    tl_sb_technicianAvailability ta
                INNER JOIN Master..spt_values spt
                    ON spt.Type = 'P'
                    AND spt.Number BETWEEN 0 AND DATEDIFF(DAY, ta.startDate, ta.EndDate)
)
SELECT  Date, TotalLaptops = SUM(LapTopsPerDay)
FROM    ExplodedAvailability
WHERE   Date >= @StartDate
AND     Date < @EndDate
GROUP BY Date;

最后,您可以将LEFT JOIN连接到预订表以获取每日可用的插槽

WITH ExplodedAvailability AS
(       SELECT  Date = DATEADD(DAY, spt.Number, ta.StartDate),
                ta.TechnicianID,
                ta.LapTopsPerDay
        FROM    tl_sb_technicianAvailability ta
                INNER JOIN Master..spt_values spt
                    ON spt.Type = 'P'
                    AND spt.Number BETWEEN 0 AND DATEDIFF(DAY, ta.startDate, ta.EndDate)
), Availability AS
(   SELECT  Date, TotalLaptops = SUM(LapTopsPerDay)
    FROM    ExplodedAvailability
    WHERE   Date >= @StartDate
    AND     Date < @EndDate
    GROUP BY Date
), Bookings AS
(   SELECT  Date, SlotsBooked = COUNT(*)
    FROM    tl_sb_booking
    GROUP BY Date
)
SELECT  Availability.Date,
        Availability.TotalLaptops,
        RemainingSlots = Availability.TotalLaptops - ISNULL(Bookings.SlotsBooked, 0)
FROM    Availability
        LEFT JOIN Bookings
            ON Bookings.Date = Availability.Date;

我认为您所追求的是在下一个可用日期添加预订,因此执行此操作的查询将是:

DECLARE @UserID INT = 1;

WITH ExplodedAvailability AS
(       SELECT  Date = DATEADD(DAY, spt.Number, ta.StartDate),
                ta.TechnicianID,
                ta.LapTopsPerDay
        FROM    tl_sb_technicianAvailability ta
                INNER JOIN Master..spt_values spt
                    ON spt.Type = 'P'
                    AND spt.Number BETWEEN 0 AND DATEDIFF(DAY, ta.startDate, ta.EndDate)
), Availability AS
(   SELECT  Date, TotalLaptops = SUM(LapTopsPerDay)
    FROM    ExplodedAvailability
    WHERE   Date >= CAST(GETDATE() AS DATE)
    GROUP BY Date
), Bookings AS
(   SELECT  Date, SlotsBooked = COUNT(*)
    FROM    tl_sb_booking
    GROUP BY Date
)
INSERT tl_sb_slotBooking (UserID, Date)
SELECT  @UserID, MIN(Availability.Date)
FROM    Availability
        LEFT JOIN Bookings
            ON Bookings.Date = Availability.Date
WHERE   Availability.TotalLaptops > ISNULL(Bookings.SlotsBooked, 0)

答案 1 :(得分:0)

这应该对任何人都有用,这是我最终做到的方式:

DECLARE @startDate DATE
DECLARE @endDate DATE

SET @startDate = GETDATE()
SET @endDate = DATEADD(m,3,@startDate)
;
WITH dates(currentDate) AS 
(
   SELECT @startdate as currentDate
   UNION ALL
   SELECT DATEADD(d,1,[currentDate])
     FROM dates 
     WHERE currentDate < @enddate
)

SELECT currentDate
  FROM dates
     WHERE              /* slots booked for date */
                       (      
                              SELECT count([date])
                              FROM tl_sb_booking
                              where [date] = currentDate
                       ) 

                       <

                       /* total slots available */
                       (
                              SELECT SUM(laptopsPerDay) AS totalSlots
                              FROM tl_sb_technicianAvailability
                              WHERE startDate <= currentDate AND endDate >= currentDate
                              AND availabiltyStateID=3
                       )