setting event to happen inbetween certain time - php

so i have a database that have a set of events that are suppose to happen and end at certain time. So say my table is (the event here is an actual event, not a code thing)
Event | TimeStart | TimeEnd | Day
A | 0800 | 1400 | 2
B | 2000 | 2300 | 3
C | 1200 | 1900 | 4
What i want is that i want the event to occur IF its between the TimeEnd and TimeEnd + 3 hours. The problem that i encounter is what if its 2300? I used the days with date(N) that correspond to monday (1) - Sunday (7)
So if its Event B, i need to have a code that take the current time, reduce it by 3 hours (so that i doesn't go to day 4) then i get the TimeEnd and add on 3 hours to it.
The problem now that i tested is that How do i make Event B to be on Day 3? even if i used strtotime() Event B still shows day 4 after it passed 2400 hour.
To make the matter clearer, i am creating a voting poll that only starts after the event ended and it only last for 3 hours.
Edit found the answer, didn't know using "Monday this week" made sense for date() :D

Your representation of times in the table is kinda weird. If each event will happen only one time (doesn't repeat for instance every monday), I suggest you use unix timestamp to represent the times. Then you will have no such problems.

Related

Complex MySQL Query - Checking for overlapping DATE intervals

I'm trying to write a function to move an scheduled task. The schedule can not overlap with any other event. My user inputs are as follows:
schedule_id (int)
new_start_time (DATETIME)
My table structure is as follows:
Schedules
| schedule_id | start_time | end_time | task_id
| 1 | 2015-12-21 02:00:00 | 2015-12-21 04:00:00 | 1
| 2 | 2015-12-21 08:30:00 | 2015-12-21 09:30:00 | 1
| 3 | 2015-12-22 01:00:00 | 2015-12-22 02:00:00 | 2
Tasks
| task_id | name | max_duration
| 1 | do things | 2
| 2 | do stuff | 1
A user has between start_time and end_time to start a "task". The user can not begin the "task" until that window. Once that user begins the task they have whatever the max_duration for that task ID is to complete it. There is also a 15 minute window to set up for the next task. That means a user who starts a task 1 second before the end of the window still has max_duration amount of time to complete the task. Therefore the "actual window" that nothing can be scheduled in is start_time to (end_time+max_duration+15). I would like to move an event (or insert a new one) but I must check for overlaps. Essentially I must ensure:
Does the start_time from user input run into any other schedule's end_time+max_duration+15?
Does the end_time+max_duration+15 run into any other schedule's start time. end_time is simply obtained by taking the new start_time and adding the original duration (end_time = (orig_end_time-orig_start_time)+start_time
For example, the above table is valid for schedule_id's 1 and 2 because a user can start any time between 2:00 and 4:00. Assuming he starts right at the end, 3:59:59 the event will last at max until 5:59:59. Even with the cleanup window of 15 minutes this still leads to 6:14:59 and since the next schedule starts at 8:30 this is ok.
I've been wrapping my head around this for hours. I would like to do it in pure MySQL however I am considering using PHP if I really have to. Even in PHP this problem seems difficult. Sure I could grab every schedule with a start time a day or two earlier and an end time a day or two later then compare my interval but that seems very hacky.
Any ideas?

Calculating a Running Total Efficiently

I'm having trouble figuring out how to store, retrieve and calculate running balances in PHP. It is not related to PHP - it is more programming theory, but anyway:
Imagine the following table:
id | user_id | date | start_time | end_time | total | balance | notes
19 | 17 | 2014-04-01 | 09:00:00 | 17:30:00 | 7.50 | 0 |
20 | 18 | 2014-04-02 | 09:00:00 | 18:30:00 | 8 | 0.5 |
20 | 18 | 2014-04-01 | 09:00:00 | 17:00:00 | 7 | -0.5 |
Displaying the balance for the month is relatively easy:
Figure out how many hours are available in the month (April 2014). (Let's assume 7.5 working hours per day and we don't work weekends):
22 Days * 7.5 = 165 Hours
Count the total time booked for a month using SQL, eg the following:
SELECT SUM(total) FROM booking WHERE date >= '2014-04-01' AND date <= '2014-04-01';
//30
Balance then is total hours worked minus total available hours:
30 - 165 = -135
I need to display a running total of these values in an efficient way across multiple months, therefore I didn't just want to SUM() the total column as there could be thousands of rows in the future, surely this is in-efficient?
Therefore I thought about having a running_totals table, with a row per-user, per-month which would auto-update whenever a record is edited, added or removed. However, this would be problematic due to the fact the records in previous months can be updated, it would not only require updating the current month row, it would have to cascade to the months in front of that month. For example if you were to go back to March to update a record, I would have to update the running_balance row for March and April.
Unless you SUM all records in running_balance, and work out the balance in PHP? Again this seems inefficient to me as the application grows, I would rather get it right at the beginning.
Other Problems
I only want to calculate the running balance up until today, however you are allowed to book time in the future, this should be ignored when calculating the running balance.
How I currently do it
I imagine hopefully pretty fast, but I think it's too complicated and there has already been bugs in it. So I need to get a simple, efficient solution in quick.
Inserting a new record
First time you book a day in a new month, I work out the total hours available in that month( EG April 2014)
165
Load the running_balance row (1 per user) and minus the month total from it:
0 - 165 = -165
Add the currently booked total on (Eg 7.5):
-165 + 7.5 = -157.5
Displaying the running balance
Load the row from running_balance. Calculate how many hours are remaining in the current month. Eg we are on 28th April so there are 15 hours remaining (2 Days * 7.5).
Add that to the running balance:
-157.5 + 15 = -142.5
Your running balance is therefore: -142.5
This is just proving to be too complicated with lots of edge cases especially when editing & removing. In particular when removing the last entry for a particular month. So, I am wondering if there is a simpler approach, you clever folk can think of?
Thanks.
PS: The source for the code is here: https://github.com/WeareJH/Flexitime
It's not really in any state to be used by the public before issues have been ruled out and docs written, but it may help with context.
A "materialized view" can store aggregated values which are maintained by the database, resulting in fast read performance at the expense of write performance.
However, "MySQL doesn't support materialized views natively, but workarounds can be implemented by using triggers or stored procedures or by using the open-source application Flexviews."
See also other answers discussing stored aggregates and normalization in general:
Storing vs calculating aggregate values
Does storing aggregated data go against database normalization?
When is it useful to store aggregated data in SQL [closed]
How do you determine how far to normalize a database?
For other background, see also:
Denormalization Patterns

Best practice for PHP/MySQL Appointment/Booking system

I need some people to battle a "best practice" for a PHP/MySQL appointment system for a hairdresser I'm currently working on. Hopefully, together we can clear some things up to avoid having to re-do the system afterwards. I've been looking at SO and Google for some tutorials, best practices etc. but I haven't found anything that fits the needs I have.
Basic Information
There are multiple hairdressers available each day, each hairdresser has his/her own agenda containing his/her appointments with customers. Connected to a hairdresser is a table containing the times he/she is available during the week.
Table: people
+----+------+-------+-----------+
| id | name | email | available |
+----+------+-------+-----------+
| 1 | John | i#a.c | Y |
| 2 | Sara | c#i.a | N |
+----+------+-------+-----------+
Table: times (there is a primary key for this table, I left it out of the schedule below)
+-----------+-------------+-----------+-----------+
| people_id | day_of_week | start | end |
+-----------+-------------+-----------+-----------+
| 1 | 1 | 08:00 | 12:00 |
| 1 | 1 | 12:30 | 17:00 |
| 1 | 3 | 09:00 | 12:00 |
| 1 | 4 | 10:30 | 16:00 |
| 1 | 5 | 09:00 | 17:00 |
| 1 | 6 | 09:00 | 12:00 |
| 2 | 1 | 09:00 | 12:00 |
| 2 | 2 | 09:00 | 12:00 |
| 2 | 3 | 09:00 | 12:00 |
+-----------+-------------+-----------+-----------+
As you can see there is a one to many relationship between people and times (obviously, since there are 7 days in a week). But besides that there is also an option to add a break between the hours of a day (see people_id = 1, day_of_week = 1: 08:00-12:00 and 12:30-17:00).
Furthermore there is a table called 'hairdress_types', this is a table containing the various types of appointments one can make (like coloring hair, cutting hair, washing, etc). This table contains the amount of time this appointment takes in minutes.
At last I have a table appointments which is pretty simple:
id, people_id, types_id, sex, fullname, telephone, emailaddress, date_start, date_end
Date start and date end would be full DATETIME fields in MySQL making it easier to calculate using MySQL query functions.
What's the best practice?
So, the way I set things up, a front-end user would select a date in a field triggering an ajax/jquery function that finds all hairdressers available at the choosen date. The user then specificies a hairdresser (this is not mandatory: a user can also choose to select 'Any' hairdresser option) and the type of appointment he/she wants to make.
After submitting the first form, the following information can be used:
date (day, month and year)
people_id (can be 0 for 'Any' or an ID if a hairdresser was selected)
hairdress_type (which is linked to an amount of minutes the appointment takes)
Using this information I would either select the available dates from the selected hairdresser OR I would loop al available hairdressers and their available dates.
This is where my minds gets a mental breakdown! Because what is the best way to check the available dates. The way I thought would be the best was:
Query the hairdressers times for the given date (1 at a time)
Query the appointments table using the startdate of the result of query1 + the amount in minutes the appointment type takes (so: SELECT * FROM appointments WHERE ((date_start >= $start AND date_start <= ($start+$time)) OR (date_end > $start AND date_end <= ($start+$time)) AND people_id = 1)
As soon as no results are found I assume this spot is free and this is presented as an option to the user
The biggers problem I'm facing is point 2: My mind is really going crazy on this query, is this the complete query I need to find appointments matching a specific timespan?
Thanks for reading & thinking along! :-)
// Edit: a little more "testdata":
John - Monday - 12:00 - 17:00.
Appointments:
- 12:00 - 12:30
- 14:30 - 15:30
A user wants to have an appointment which takes 2 hours, in the exampel above I would check:
Is there an appointment between 12:00 and 14:00? Yes,.. proceed to next spot
Is there an appointment between 14:00 and 16:00? Yes,.. proceed to next spot
Is there an appointment between 16:00 and 18:00? Error, not available after 17:00
Thus.. it might be a better option to use "timeblocks" of 10/15 minutes. Making the checks:
12:00 - 14:00
12:10 - 14:10
12:20 - 14:20 etc..
This would find the available spot between 12:30 and 14:30.
// Edit 2: Possbile query to step 2
I've been working out some stuff on paper (a table with appointments and possible empty spots to use). And I came up with the following. An appointment can not be made in case:
There is an appointment with start_date BETWEEN $start and $end
There is an appointment with end_date BETWEEN $start and $end
There is an appointment with start_date < $start and end_date > $end
Querying the above to the appointment table together with people_id would result in either no rows (= free spot) or one/multiple row(s) in which case the spot is taken.
I guess the best way to find open spots is to query the database for blocks of X minutes with a start interval of 10 minutes. The bad side of this solution is that I would neet 6 queries for every hour, which would be about 48 queries for every hairdresser... Any ideas on how to reduce that amount of queries?
In the end I went for a system that generated timestamps for the start and end dates in the database. While checking I added one second to the start and subtracted one second from the end to avoid any overlapping time for appointments.
What did I end up doing
Obviously I'm not sure this is the best practice, but it did work for me. The user starts by selecting their sex and a preference for the day. This sends an AJAX request to get the available personel and various kinds of appointment types (f.e. coloring hair, cutting hair, etc).
When all settings have been choosen (sex, date, personel and type) I start by some simple validations: checking the date, checking if the date("N") is not 7 (sunday). If everything is okay, the more important stuff is started:
1) The appointment type is fetched from the database including the total amount of time this type takes (30 minutes, 45 minutes, etc)
2) The available personal is fetched (a complete list of people on that day or just a single person if one is chosen) including their available times
The personel (or one person) is then looped, starting with their own starting time. At this point I have a set of data containing:
$duration (of the appointment type)
$startTime (starting time of the person selected in the loop)
$endTime (= $startTime + $duration)
$personStart (= starting time of the person)
$personEnd (= end time of the person)
Let's take this demo data:
$duration = 30 min
$startTime = 9.00h
$endTime = 9.30h
$personStart = 9.00h
$personEnd = 12.00h
What I'm doing here is:
while( $endTime < $personEnd )
{
// Check the spot for availability
$startTime = $endTime;
$endTime = $startTime + $duration;
}
Obviously, it's al simplified in this case. Because when I check for availability, and the spot is not free. I set the $startTime to be equal to the latest appointment found and start from there in the loop.
Example:
I check for a free spot at 9.00 but the spot is not free because there's an appointment from 9.00 till 10.00, then 10.00 is returned and $startTime is set to 10.00h instead of 9.30h. This is done to keep the number of queries to a minimum since there can be quiet a lot.
Check availability function
// Check Availability
public static function checkAvailability($start, $end, $ape_id)
{
// add one second to the start to avoid results showing up on the full hour
$start += 1;
// remove one second from the end to avoid results showing up on the full hour
$end -= 1;
// $start and $end are timestamps
$getAppointments = PRegistry::getObject('db')->query("SELECT * FROM appointments WHERE
((
app_start BETWEEN '".date("Y-m-d H:i:s", $start)."' AND '".date("Y-m-d H:i:s", $end)."'
OR
app_end BETWEEN '".date("Y-m-d H:i:s", $start)."' AND '".date("Y-m-d H:i:s", $end)."'
)
OR
(
app_start < '".date("Y-m-d H:i:s", $start)."' AND app_end > '".date("Y-m-d H:i:s", $end)."'
))
AND
ape_id = ".PRegistry::getObject('db')->escape($ape_id));
if(PRegistry::getObject('db')->num_rows($getAppointments) == 0) {
return true;
} else {
$end = 0;
foreach(PRegistry::getObject('db')->fetch_array(MYSQLI_ASSOC, $getAppointments, false) as $app) {
if($app['app_end'] > $end) {
$end = $app['app_end'];
}
}
return $end;
}
}
Since I'm storing appointments as "From:10.00 Till:11.00" I have to make sure to check spots from 11:00:01 till 11:59:59, because otherwise the appointment at 11:00 will show in the results.
At the end of the function, in case an appointment is found, I loop the results and return the latest end. This is the next start in the loop I mentioned above.
Hopefully this can be of any help to anyone. Just as info: ape_id is the ID of the "Appointment Person" it is linked with.
Use MySQL keyword BETWEEN.
SELECT * FROM mytable WHERE mydate BETWEEN start_date AND end_date;
If you want to see if an appointment is available between now and a day, you could do this:
$enddate = strtotime('+1 day', time());
SELECT * FROM mytable WHERE mydate BETWEEN NOW() AND {$enddate};
Source

Determine time blocks from multiple concurrent time stamps in PHP

We have a time tracking system where we start a timer for each project we are working on, then stop when complete. There are many times that we have multiple timers going in the same block of time. for example:
Given three timers:
Start Time | End Time | Time Spent
10/23/2012 10:15:00 AM | 10/23/2012 10:45:00 AM | 30
10/23/2012 10:15:00 AM | 10/23/2012 11:15:00 AM | 60
10/23/2012 11:05:00 AM | 10/23/2012 11:35:00 AM | 30
Both are running at the same time, and if used with our time reporting would amount to 2 hours being counted, even though the times are inside of each other when the valid amount of time spent is 1 hour and 20 minutes.
So I am trying to figure out how to go through the time entries and only count the valid time periods.
Any ideas how to calculate this in PHP?

SQL Infinite Calendar Pattern

I'm going to make a Mysql based calendar system where you can have repeating pattern for lets say every monday forever and ever. It must also cover static/once-only events. What I'm wondering about, is which solution would be most logical (and best) for me to use. I have four methods which I'm wondering to chose between.
Method #1
Make a function which accepts parameters from and to. This function would create a temporary table table which imports existing static schedule through INSERT ... SELECT. Afterward it would read of the pattern table and populate the temporary table through the peroid based on from and to.
This solution seems nice from the point of view that queries will be simplier to fetch data with and it works into infinity since you can just repopulate the table depending of which month you're loading. What I'm curious about is whenever this might be a laggy way to do it or not.
Method #2
Create and join given patterns through a subquery and JOIN with static calendar.
This seems to be rather annoying since the queries would be a lot more bigger and would probably not be good at all(?).
Method #3
Basicly just INSERT pattern for lets say one year ahead. Then I guess a cron job would repopulate to make it one year ahead always.
This is a simple way to do it, but it feels like a lot of unneeded data stored and it doesn't really give the infinity which I'm after.
Method #4 (Suggested by Veger)
If I understand correctly, this method would fetch the pattern from another query and creates events upon execution. It's similar to my thoughts regarding Method #1 in that way that I consider simple pattern to create several rows.
However if this would be implemented outside Mysql, I would loose some database functionality which I'm after.
I hope you guys understood my situation, and if you could suggest either given and argue why it's the best or give another solution.
Personally I like the Method #1 the most, but I'm curious if it's laggy to repopulate the calendar table each and every call.
I have built this kind of calendar before. I found the best way to do it is to approach it the way that crons are scheduled. So in the database, make a field for minute, hour, day of month, month, and day of week.
For an event every Friday in June and August at 10:00pm your entry would look like
Minute Hour DayOfMonth Month DayOfWeek
0 22 * 6,8 5
You could then have a field that flags it as a one time event which will ignore this information and just use the start date and duration. For events that repeat that end eventually (say every weekend for 3 months) you just need to add an end date field.
This will allow you to select it back easily and reduce the amount of data that needs to be stored. It simplifies your queries as well.
I don't think there is a need to create temporary tables. To select back the relevant events you would select them by the calendar view. If your calendar view is by the month, your select would look something like:
SELECT Events.*
FROM Events
WHERE (Month LIKE '%,'.$current_month.',%' OR Month = '*')
AND DATE(StartDate) >= "'.date('Y-m-d', $firstDayOfCurrentMonth).'"
AND DATE(EndDate) <= "'.date('Y-m-d', $lastDayOfCurrentMonth).'"
Obviously this should be in a prepared statement. It also assumes that you have a comma before and after the first and last value in the comma separated list of months (ie. ,2,4,6,). You could also create a Month table and a join table between the two if you would like. The rest can be parsed out by php when rendering your calendar.
If you show a weekly view of your calendar you could select in this way:
SELECT Events.*
FROM Events
WHERE (DayOfMonth IN ('.implode(',', $days_this_week).','*')
AND (Month LIKE '%,'.$current_month.',%' OR Month = '*'))
AND DATE(StartDate) >= "'.date('Y-m-d', $firstDayOfCurrentMonth).'"
AND DATE(EndDate) <= "'.date('Y-m-d', $lastDayOfCurrentMonth).'"
I haven't tested those queries so there maybe some messed up brackets or something. But that would be the general idea.
So you could either run a select for each day that you are displaying or you could select back everything for the view (month, week, etc) and loop over the events for each day.
I like Veger's solution best .. instead of populating multiple rows you can just populate the pattern. I suggest the crontab format .. it works so well anyway.
You can query all patterns for a given customer when they load the calendar and fill in events based on the pattern. Unless you have like thousands of patterns for a single user this should not be all that slow. It should also be faster than storing a large number of row events for long periods. You will have to select all patterns at once and do some preprocessing but once again, how many patterns do you expect per user? Even 1000 or so should be pretty fast.
I've had this idea since I was still programming in GW Basic ;-) though, back then, I took option #3 and that was it. Looking back at it, and also some of the other responses, this would be my current solution.
table structure
start (datetime)
stop (datetime, nullable)
interval_unit ([hour, day, week, month, year?])
interval_every (1 = every <unit>, 2 every two <units>, etc.)
type ([positive (default), negative]) - will explain later
Optional fields:
title
duration
The type field determines how the event is treated:
positive; normal treatment, it shows up in the calendar
negative; this event cancels out another (e.g. every Monday but not on the 14th)
helper query
This query will narrow down the events to show:
SELECT * FROM `events`
WHERE `start` >= :start AND (`stop` IS NULL OR `stop` < :stop)
Assuming you query a range by dates alone (no time component), the the value of :stop should be one day ahead of your range.
Now for the various events you wish to handle.
single event
start = '2012-06-15 09:00:00'
stop = '2012-06-15 09:00:00'
type = 'positive'
Event occurs once on 2012-06-15 at 9am
bounded repeating event
start = '2012-06-15 05:00:00'
interval_unit = 'day'
interval_every = 1
stop = '2012-06-22 05:00:00'
type = 'positive'
Events occur every day at 5am, starting on 2012-06-15; last event is on the 22nd
unbounded repeating event
start = '2012-06-15 13:00:00'
interval_unit = 'week'
interval_every = 2
stop = null
type = 'positive'
Events occur every two weeks at 1pm, starting on 2012-06-15
repeating event with exceptions
start = '2012-06-15 16:00:00'
interval_unit = 'week'
interval_every = 1
type = 'positive'
stop = null
start = '2012-06-22 16:00:00'
type = 'negative'
stop = '2012-06-22 16:00:00'
Events occur every week at 4pm, starting on 2012-06-22; but not on the 22nd
I would suggest something around the lines of this:
Split your Events table into 2 because there are clearly 2 different types recurring events and static events and depending on the type they will have different attributes.
Then for a given Event look-up you would run 2 queries, one against each Event Type. For the static events table you would defiantly need (at least) one datetime field so the lookup for a given month would simply use that feild in the conditions (where event_date > FirstDayOfTheMonth and event_date < LastDayOfTheMonth ). Same logic for a weekly/yearly view.
This result set would be combined with a second result set from the recurring events table. Possible attributes could be similar to crontab entries, using day of week/day of month as the 2 main variables. If you're looking at a monthly view,
select * from recurring_events where DayOfWeek in (1,2,3,4,5,6,7) or (DayOfMonth > 0 and DayOfMonth < #NumberOfDaysInThisMonth )
Again similar if for a weekly/yearly view. To make this even simpler to interface, use stored procedures with all the logic for determining 'which days of the week are found between date A and date B'.
Once you have both result sets, you could aggregate them together in the client then display them together. The adavantage to this is there will be no need for "mock/empty records" nor async cronjobs which pre-fill, the queries could easily happen on the fly and if performance actually degrades, add a caching layer, especially for a system of this nature a cache makes perfect sense.
I'm actually looking for something similar to this and my solution so far (on paper I didn't start to structure or code yet) stores in 2 tables:
the "events" would get the date of the first occurrence, the title and description (plus the auto-increment ID).
the "events_recursion" table would cross reference to the previous table (with an event_id field for instance) and could work in 2 possible ways:
2.A: store all the occurrences by date (i.e. one entry for every occurrence so 4 if yo want to save "every friday of this month" or 12 for "the 1st of every month in 2012")
2.B: or saving the interval (I would save it in seconds) from the date of the first event in a field + the date of the last occurrence (or end of recursion) in another field such as
ID: 2
EVENT_ID: 1
INTERVAL: 604800 (a week if I'm not mistaken)
END: 1356912000 (should be the end of this year)
Then when you open the php that shows the schedule it would check for the event still active in that month with a joint between the two tables.
The reason why I would use 2 tables cross-referenced instead of saving all in one tables just comes from the facts that my projects sees very crazy events such as "every fridays AND the 3rd monday of every month" (that in this case would be 1 entry in the events tables and 2 with same "event_id" field in the second table. BTW my projects is for music teachers that here got small work on strict schedules decided 3 or 6 months at a time and are a real mess).
But as I have said i haven't started yet so I'm looking forward to seeing your solution.
PS: please forgive (and forget) my english, first isn't my language and second it is pretty late night and I'm sleepy
Maybe check out some great ideas from MySQL Events
and some more:
http://phpmaster.com/working-with-mysql-events/?utm_source=rss&utm_medium=rss&utm_campaign=phpmaster-working-with-mysql-events
http://dev.mysql.com/doc/refman/5.5/en/create-event.html
the best solution depends on whether you want to favor standard compliance (RFC5545) or working exclusively within MySQL.
depend on on flexible your recurrence rule engine needs to be. If you want simple rules (every 1st of month or every January, ...) then the solutions offered above have been detailed at length.
However should you want your application to offer compatibility with existing standards (RFC5545) which involves much more complex rules you should have a look at this SO post when building a calendar app, should i store dates or recurrence rules in my database?
I would do it as I explained here. It will create an infinite calander:
PHP/MySQL: Model repeating events in a database but query for date ranges
The downside is that there will be some calculation during the query. If you need a high performance website, preloading the data will be the way to go. You dont even have to preload all the events in the calendar, to make it possible for easy changing the values in a single event. But it would be wise to store all dates from now till ....
Now using cached values does make it less infinite, but it will increase speed.
Copy of awnser for easy access:
I would create a tally table with just one col called id and fill that table with numbers from 0 to 500. Now we easily use that to make selections instead of using a while loop.
Id
-------------------------------------
0
1
2
etc...
Then i'd store the events in a table with Name as varchar, startdate as datetime and repeats as int
Name | StartDate | Repeats
-------------------------------------
Meeting | 2012-12-10 00:00:00 | 7
Lunch | 2012-12-10 00:00:00 | 1
Now we can use the tally table to select all dates between two dates by using:
SELECT DATE_ADD('2012-12-09 00:00:00',INTERVAL Id DAY) as showdate
FROM `tally`
WHERE (DATE_ADD('2012-12-09 00:00:00',INTERVAL Id DAY)<='2012-12-20 00:00:00')
ORDER BY Id ASC
ShowDate
-------------------------------------
2012-12-09 00:00:00
2012-12-10 00:00:00
2012-12-11 00:00:00
2012-12-12 00:00:00
2012-12-13 00:00:00
2012-12-14 00:00:00
2012-12-15 00:00:00
2012-12-16 00:00:00
2012-12-17 00:00:00
2012-12-18 00:00:00
2012-12-19 00:00:00
2012-12-20 00:00:00
Then we join this on the events table to calculate the difference between the startdate and the showdate. We devided the results of this by the repeats column and if the remainder is 0, we have match.
All combined becomes:
SELECT E.Id, E.Name, E.StartDate, E.Repeats, A.ShowDate, DATEDIFF(E.StartDate, A.ShowDate) AS diff
FROM events AS E, (
SELECT DATE_ADD('2012-12-09 00:00:00',INTERVAL Id DAY) as showdate
FROM `tally`
WHERE (DATE_ADD('2012-12-09 00:00:00',INTERVAL Id DAY)<='2012-12-20 00:00:00')
ORDER BY Id ASC
) a
WHERE MOD(DATEDIFF(E.StartDate, A.ShowDate), E.Repeats)=0
AND A.ShowDate>=E.StartDate
Which results in
Id | Name |StartDate | Repeats | ShowDate | diff
---------------------------------------------------------------------------------
1 | Meeting | 2012-12-10 00:00:00 | 7 | 2012-12-10 00:00:00 | 0
2 | Lunch | 2012-12-10 00:00:00 | 1 | 2012-12-10 00:00:00 | 0
2 | Lunch | 2012-12-10 00:00:00 | 1 | 2012-12-11 00:00:00 | -1
2 | Lunch | 2012-12-10 00:00:00 | 1 | 2012-12-12 00:00:00 | -2
2 | Lunch | 2012-12-10 00:00:00 | 1 | 2012-12-13 00:00:00 | -3
2 | Lunch | 2012-12-10 00:00:00 | 1 | 2012-12-14 00:00:00 | -4
2 | Lunch | 2012-12-10 00:00:00 | 1 | 2012-12-15 00:00:00 | -5
2 | Lunch | 2012-12-10 00:00:00 | 1 | 2012-12-16 00:00:00 | -6
1 | Meeting | 2012-12-10 00:00:00 | 7 | 2012-12-17 00:00:00 | -7
2 | Lunch | 2012-12-10 00:00:00 | 1 | 2012-12-17 00:00:00 | -7
2 | Lunch | 2012-12-10 00:00:00 | 1 | 2012-12-18 00:00:00 | -8
2 | Lunch | 2012-12-10 00:00:00 | 1 | 2012-12-19 00:00:00 | -9
2 | Lunch | 2012-12-10 00:00:00 | 1 | 2012-12-20 00:00:00 | -10
Now you could (and should!) speed things up. For instance by directly storing dates in a table so you can just select all dates directly instead of using a tally table with dateadd. Every thing you can cache and dont have to calculate again is good.

Categories