简体   繁体   中英

Most efficient way to check a range of dates using c# and SQL

I am writing a booking system for a laptop refresh we are doing at our company. I need to be able to calculate which days have available slots and return them to be rendered on a calendar control (ie if a day has available days it is selectable, otherwise not.

The logic is as follows:

  • A technician can build 3 laptops per day.
  • On any day there may be 1, 2 or 3 technicians available.
  • A table will hold the bookings already made

The total number of available slots per day is (in pseudo code):

((laptops per day) * (technicians available)) - slots already booked

My question is, what is the most efficient way to derive this? I guess it best done at the SQL side, with a function to return a table of dates with available slots. (It doesn't matter how many there are, so long as at least one slot is available.)

I can get my head round all of this so far. The bit I'm stuck on is that I don't want to have a table of all possible dates as it seems a bit inefficient. What I want to be able to do is effectively iterate over a range of dates between now and 3 months in the future and calculate the available dates from there.

I could do this in c#, but it strikes me as inefficient as it will have to keep hitting the SQL server for each possible day. Seems better to do it on the SQL side, but I don't know how to iterate over possible dates in this manner.

SOLUTION SO FAR

Using @Chris's method I can get a range of dates where the slots books don't exceed a set maximum:

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

This is just setting an arbitrary maximum of 3 bookings. Next step is to add the technician availability!

This answer addresses the part of the question that says: "What I want to be able to do is effectively iterate over a range of dates between now and 3 months in the future and calculate the available dates from there."

If you are using MSSQL then CTEs may help. To dynammically generate your date range code like the following can be used:

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

That last SELECT can then join onto your other tables to get the dat for that date, do calculations, etc.

However, this isn't necessarily the best way to do this. Assuming you have some way to get a list of how many engineers are free on each day then there is no need to iterate over any other dates (since you know there will be no availability on those dates). So to this end something like:

SELECT count(1), Date
From EngineerWorkDays
GROUP BY Date
Where Date>= Getdate()
and Date < DATEADD(m,3,Getdate())

Would return a list of the dates where there is a possibility of work being done. Another select to get the work already allocated would then give you all the data you need. In your display code in c# you could then iterate over each day and check your datasets for any relevant data (whether engineers are available, how many, how much work already booked, etc.).

And don't forget if this is a shared app to check that there is still space on a date before booking something in in case somebody else booked something in after your did your initial query.

Try this:

Select [DatesAvailable] From TableName
Where [DatesAvailable] Between BeginningDate And DateAdd(m, 3, BeginningDate) And
      SlotsAvailable > 0

This should give you any dates that have a slot available for the next 3 months...

Here's one way to do it:

DECLARE @iToday As INT; SET @iToday = CAST(GetDate() As INT);
DECLARE @iDays As INT;  SET @iDays = 90;

WITH cte_0to9 As
(
    Select 0 As Num UNION ALL Select 1 UNION ALL Select 2 UNION ALL Select 3
    UNION ALL Select 4 UNION ALL Select 5 UNION ALL Select 6 UNION ALL Select 7
    UNION ALL Select 8  UNION ALL Select 9
)
, cte_0to99 As
(
    SELECT  (Tens.Num * 10) + Ones.Num As Num
    FROM        cte_0to9 As Ones
    CROSS JOIN  cte_0to9 As Tens
)
, cteDays As 
(
    SELECT  CAST(@iToday + Num As DATETIME) As PossibleDate
    FROM    cte_0to99
    WHERE   Num <= @iDays
)
, cteTechnicianUtilization As
(
    SELECT  t.Technician,
            d.PossibleDate,
            (Select COUNT(*) From Bookings b
             Where b.Technician = t.Technician
               And b.BookDate = d.PossibleDate) As SlotsUsed
    FROM    Technicians As t
    CROSS JOIN cteDays d
)
SELECT  PossibleDate, SUM(Util-3) As TotalAvailableSlots
FROM    cteTechnicianUtilization
WHERE   Util < 3
GROUP BY PossibleDate
HAVING  SUM(Util-3) > 0

Note that this approach, while longer (code-wise) is much more efficient than using recursion.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM