Searching for availability with MySQL (and PHP)?

前端 未结 2 1376
别跟我提以往
别跟我提以往 2020-12-30 17:23

I have two MySQL (MyIsAm) tables that represent letting units and bookings:

  • LettingUnits (ID, Name, etc...)
  • LettingUnitBooki
2条回答
  •  暖寄归人
    2020-12-30 17:48

    It's not pretty.

    • Join LettingUnitBookings to itself
    • Find the start and end of gaps between bookings for each F_LU_ID
    • Get the size of the gaps - the available 'slots'
    • Consider the case where there are no existing bookings that 'bracket' a suitable slot, add in outlier dates for this
    • Join that projection to the LettingUnits table and apply WHERE criteria (start, end, duration)

    I've neglected to include BookingUnits that have no bookings at all.

    Ends up looking like this:

    SELECT @StartOfWindow := '2009-01-01',
           @EndOfWindow   := '2009-02-01',
           @WindowSize    := 5
    ;
    
    SELECT
        lu.Name,
        Slots.*
    FROM (
        SELECT
            lub1.F_LU_ID,
            DATE_ADD( MAX( lub2.date_time ), INTERVAL 1 DAY )     AS StartOfSlot,
            DATE_SUB( lub1.date_time, INTERVAL 1 DAY )            AS EndOfSlot,
            DATEDIFF( lub1.date_time, MAX( lub2.date_time ) ) - 1 AS AvailableDays
        FROM
        ( SELECT F_LU_ID, Start AS date_time FROM LettingUnitBookings
          UNION
          SELECT F_LU_ID, CAST( '9999-12-31' AS DATE ) FROM LettingUnitBookings
        ) AS lub1,
        ( SELECT F_LU_ID, End   AS date_time FROM LettingUnitBookings
          UNION
          SELECT F_LU_ID, CAST( '1000-01-01' AS DATE ) FROM LettingUnitBookings
        ) AS lub2
        WHERE
            lub2.date_time <= lub1.date_time
        AND lub2.F_LU_ID = lub1.F_LU_ID
        GROUP BY
            lub1.F_LU_ID,
            lub1.date_time
        ) Slots
    JOIN LettingUnits lu
    ON   lu.ID = Slots.F_LU_ID
    WHERE
        Slots.AvailableDays >= @WindowSize
    AND (
       (     DATEDIFF( Slots.EndOfSlot, @EndOfWindow )     >= @WindowSize
         AND DATEDIFF( @StartOfWindow, Slots.StartOfSlot ) >= @WindowSize
       )
       OR
       (     DATEDIFF( @EndOfWindow, Slots.StartOfSlot ) >= @WindowSize
         AND DATEDIFF( Slots.EndOfSlot, @StartOfWindow ) >= @WindowSize
       )
    )
    

    Gives

    Name        F_LU_ID StartOfSlot EndOfSlot  AvailableDays
    Foo Cottage 123     2009-01-06  2009-01-09 5
    Foo Cottage 123     2009-01-21  2009-01-24 5
    

    Hopefully that can be adapted to suit your needs.

    Alternatively, if a booking can start on the same day that the previous booking ends, you can adapt slightly...

    SELECT
        lu.Name,
        Slots.*
    FROM (
        SELECT
            lub1.F_LU_ID,
            MAX( lub2.date_time ) AS StartOfSlot,
            lub1.date_time        AS EndOfSlot,
            DATEDIFF( lub1.date_time, MAX( lub2.date_time )) AS AvailableDays
        FROM
        ( SELECT F_LU_ID, Start AS date_time FROM LettingUnitBookings
          UNION
          SELECT F_LU_ID, CAST( '9999-12-31' AS DATE ) FROM LettingUnitBookings
        ) AS lub1,
        ( SELECT F_LU_ID, End   AS date_time FROM LettingUnitBookings
          UNION
          SELECT F_LU_ID, CAST( '1000-01-01' AS DATE ) FROM LettingUnitBookings
        ) AS lub2
        WHERE
            lub2.date_time <= lub1.date_time
        AND lub2.F_LU_ID = lub1.F_LU_ID
        GROUP BY
            lub1.F_LU_ID,
            lub1.date_time
        ) Slots
    JOIN LettingUnits lu
    ON   lu.ID = Slots.F_LU_ID
    WHERE
        Slots.AvailableDays >= @WindowSize
    AND
       (     DATEDIFF( Slots.EndOfSlot, @EndOfWindow )     >= @WindowSize
         AND DATEDIFF( @StartOfWindow, Slots.StartOfSlot ) >= @WindowSize
       )
       OR
       (     DATEDIFF( @EndOfWindow, Slots.StartOfSlot ) >= @WindowSize
         AND DATEDIFF( Slots.EndOfSlot, @StartOfWindow ) >= @WindowSize
       )
    

提交回复
热议问题