1

in my component I have created an agenda, where user can save their appointments.
Agenda table is quite simple: there is a title, description and start/end datetime fields.

When a user adds a new event, I'd wish to hint him with the first empty spot.

How can I achieve that?
Is that possible with a single/bunch queries, or I have to create a loop until I find the first empty spot?

For example this is my table:

|  ID |       Start date     |       End date      |
|  1  | 2012-06-14 09:00:00  | 2012-06-14 09:32:00 |
|  2  | 2012-06-14 15:00:00  | 2012-06-14 15:45:00 |
|  3  | 2012-06-14 18:20:00  | 2012-06-14 18:55:00 |

The first free datetime should be 2012-06-14 09:33:00, how can I fetch this date?

4

1 回答 1

2

Interesting challenge to be done in one query :) Took me a while but I came with solution. With these assumptions:

  • minimum appointment duration is 30 minutes
  • appointment starts and ends in one day
  • day starts at 9:00 and ends at 17:00
  • minimum interval between ending and starting times is 1 minute

There are 4 cases to take into consideration:

  1. There is some free time in the morning
  2. There is some free time during the day
  3. First free slot is next day to the last appointment in db
  4. You have all free slots from now on

So yo have to select minimum form these dates.

And the query:

SELECT
    MIN(next_start_date) AS next_start_date
FROM
( 
    (
        SELECT 
            DATE_FORMAT(t1.start_date,'%Y-%m-%d 09:00:00') AS next_start_date
        FROM
            agenda t1
        WHERE
            t1.start_date > NOW()
        AND
            TIME(t1.start_date) > '09:30:00'
        AND
            NOT EXISTS(
                SELECT 1 FROM agenda t2 WHERE DATE(t2.start_date) = DATE(t1.start_date) AND TIME(t2.start_date) <= '09:30:00'
            )
        LIMIT 1
    )
    UNION
    (
        SELECT
            t3.end_date + INTERVAL 1 MINUTE AS next_start_date
        FROM
            agenda t3
        WHERE
            t3.start_date > NOW()
        AND
            TIME(t3.start_date) >= '09:00:00'
        AND
            TIME(t3.end_date) < '16:30:00'
        AND NOT EXISTS
            (SELECT 1 FROM agenda t4 WHERE TIMESTAMPDIFF(MINUTE,t3.end_date,t4.start_date) BETWEEN 0 AND 30 )
        ORDER BY
            t3.start_date ASC
        LIMIT 1
    )
    UNION
    (
        SELECT CONCAT(CAST(DATE((SELECT MAX(t5.start_date) + INTERVAL 1 DAY FROM agenda t5 WHERE t5.start_date > NOW())) AS CHAR), ' 09:00:00') AS next_start_date
    )
    UNION
    (
        SELECT 
            IF( 
                TIME(NOW()) < '09:00:00', 
                DATE_FORMAT(NOW(),'%Y-%m-%d 09:00:00'), 
                IF(
                    TIME(NOW()) < '16:30', 
                    NOW(),
                    DATE_FORMAT(NOW() + INTERVAL 1 DAY,'%Y-%m-%d 09:00:00' ) 
                )
            ) AS next_start_date
        FROM
            (SELECT 1) t6
        WHERE NOT EXISTS(
            SELECT 1 FROM agenda t7 WHERE t7.start_date > NOW()
        )
        LIMIT 1
    )
) t

Of course it is not perfect - when there next free slot occurs in the next day, there's a chance that it is Saturday or other day off from work. Taking it into consideration, the best way will be to check if returned is valid work day, if not then repeat the query with NOW() replaced by datestring of next valid work day.

于 2012-06-14T21:23:04.967 回答