Best way to query calendar events? - php

I'm creating a calendar that displays a timetable of events for a month. Each day has several parameters that determine if more events can be scheduled for this day (how many staff are available, how many times are available etc).
My database is set up using three tables:
Regular Schedule - this is used to create an array for each day of the week that outlines how many staff are available, what hours they are available etc
Schedule Variations - If there are variations for a date, this overrides the information from the regular schedule array.
Events - Existing events, referenced by the date.
At this stage, the code loops through the days in the month and checks two to three things for each day.
Are there any variations in the schedule (public holiday, shorter hours etc)?
What hours/number of staff are available for this day?
(If staff are available) How many events have already been scheduled for this day?
Step 1 and step 3 require a database query - assuming 30 days a month, that's 60 queries per page view.
I'm worried about how this could scale, for a few users I don't imagine that it would be much of a problem, but if 20 people try and load the page at the same time, then it jumps to 1200 queries...
Any ideas or suggestions on how to do this more efficiently would be greatly appreciated!
Thanks!

I can't think of a good reason you'd need to limit each query to one day. Surely you can just select all the values between a pair of dates.
Similarly, you could use a join to get the number of events scheduled events for a given day.
Then do the loop (for each day) on the array returned by the database query.

Create a table:
t_month (day INT)
INSERT
INTO t_month
VALUES
(1),
(2),
...
(31)
Then query:
SELECT *
FROM t_month, t_schedule
WHERE schedule_date = '2009-03-01' + INTERVAL t_month.day DAY
AND schedule_date < '2009-03-01' + INTERVAL 1 MONTH
AND ...
Instead of 30 queries you get just one with a JOIN.
Other RDBMS's allow you to generate rowsets on the fly, but MySQL doesn't.
You, though, can replace t_month with ugly
SELECT 1 AS month_day
UNION ALL
SELECT 2
UNION ALL
...
SELECT 31

I faced the same sort of issue with http://rosterus.com and we just load most of the data into arrays at the top of the page, and then query the array for the relevant data. Pages loaded 10x faster after that.
So run one or two wide queries that gather all the data you need, choose appropriate keys and store each result into an array. Then access the array instead of the database. PHP is very flexible with array indexing, you can using all sorts of things as keys... or several indexes.

Related

Building a scheduler with sql, php. Most efficient way

I am creating a system that requires a schedular for a particular task. Users may pick from times 24 hours a day, 7 days a week.
I came up with a few options for the database storage, but I don't think either one is the most efficient design, so I'm hoping for some possible alternatives that may be more efficient.
On the user side I created a grid of buttons with 2 loops to create the days, and the times, and I set each a unique value of $timeValue = "d".$j."-t".$i;
So d1-t0 will be Saturday at Midnight d3-t12= Tuesday at Noon, and so forth.
So, in the database I was first going to simply have a ID, day, time set up, but that would result in a possible 168 rows per event
Then I tried an ID, day, and time 0-23 (a column for each hour of the day) And I was simply going to have a boolean set up. 0 if not selected, 1 if it is.
This would result in 7 rows per event, but I think querying that data might be a pain.
I need to perform a few functions on this data. On each day, list the number of selected times into an array. But I don't believe having a select statement of SELECT * from schedule where time0, =1 or time1= 1 .... ect will work, nor will it produce the desired array. (times=(0,3,5,6,7...)
So, this isnt going to work well.
My overall system will need to also know every event that has each time selected for a mass posting.
"Select * from table where time = $time (0-23) and day= $day (1-7)
Do action with data...
So with this requirement, I'm going to assume that storing the times as an array within the database is likely not the most efficient way either.
So am I stuck with needing up to 168 rows of data per event, or is there a better way I am missing? Thanks
Update:
To give a little more clarity on what I need to accomplish:
Users will be creating event campaigns in which other users can bid on various time slots for something to happen. There will likely be 10-100 thousand of these campaigns at any one time and they are ongoing until the creator stops them. The campaign creators can define the time slots available for their campaign.
At the designated time each day the system will find every campaign that has an event scheduled and perform the event.
So the first requirement is to know which time slots are available for the campaign, and then I need the system to quickly identify campaigns that have an event on each hour and day and perform it automatically.

Mysql (+php) Right approach to store, retrieve and show date ranges

I'm working on a calendar application where you can set events which can last multiple days. On a given date there can be multiple events, already started or starting that day. The user will view everything as a calendar.
Now, my question is: which of the following is the right approach? Is there an even better approach?
1) use 2 tables, events and events_days and store the days of every event in the event_days table
2) use just one table with the events stored with a date_from field and a date_to field and generate every time with mysql and php a list of days for the desired range with the events
Obviously the first option will require much more db storage, while the second one will require a bigger work from the server to generate the list (every single time the user asks for it).
The db storage shouldn't be a problem for now, but i don't know if will be the same in the future. And i fear the second option will need too many resources.
I have used both approaches. Here is a list of pros and cons that I have noticed:
Two tables: events(id) and events_dates(eventid, date)
Pros:
Query to check if there are events on a given date or between given dates is trivial:
SELECT eventid FROM events_dates WHERE date BETWEEN '2015-01-01' AND '2015-01-10'
Query to select the list of dates for an event is trivial
SELECT date FROM events_dates WHERE eventid = 1
Cons:
While selecting the list of dates is trivial, inserting the list of dates for an event requires scripting (or a table-of-dates)
Additional measures required to make sure data remains consistent, for example, when inserting an event spanning three days you need four insert queries
This structure not suitable in situations where time is involved, for example, meetings schedule
One table: events(id, start, end)
Cons:
Query to check if there is are events on a given date or between given dates is tricky.
Query to select the list of dates for an event is tricky.
Pros:
Inserting an event is trivial
This structure suitable in situations where time is involved

Getting temperature difference between intervals

my question is more "theoretical" than practical - in other words, Im not really looking for a particular code for how to do something, but more like an advice about how to do it. Ive been thinking about it for some time but cannot come up with some feasible solution.
So basically, I have a MySQL database that saves weather information from my weather station.
Column one contains date and time of measurement (Datetime format field), then there is a whole range of various columns like temp, humidity etc. The one I am interested in now is the one with the temperature. The data is sorted by date and time ascending, meaning the most recent value is always inserted to the end.
Now, what I want to do is using a PHP script, connect to the db and find temperature changes within a certain interval and then find the maximum. In other words, for example lets say I choose interval 3h. Then I would like to find the time, from all the values, where there was the most significant temperature change in those 3 h (or 5h, 1 day etc.).
The problem is that I dont really know how to do this. If I just get the values from the db, Im getting the values one by one, but I cant think of a way of getting a value that is lets say 3h from the current in the past. Then it would be easy, just subtracting them and get the date from the datetime field at that time, but how to get the values that are for example those 3 h apart (also, the problem is that it cannot just simply be a particular number of rows to the past as the intervals of data save are not regular and range between 5-10mins, so 3 h in the past could be various number of rows).
Any ideas how this could be done?
Thx alot
Not terribly hard actually. So I would assume it's a two column table with time and temp fields, where time is a DATETIME field
SELECT MAX(temp) FROM records
WHERE time >= "2013-10-14 12:00:00" and time <= "2013-10-14 15:00:00"
SELECT t1.*, ABS(t1.temperature - t2.temperature) as change
FROM tablename t1
JOIN tablename t2
ON t2.timecolumn <= (t1.timecolumn - INTERVAL 3 HOUR)
LEFT JOIN tablename t3
ON t3.timecolumn <= (t1.timecolumn - INTERVAL 3 HOUR)
AND t2.timecolumn > t3.timecolumn
WHERE
t3.some_non_nullable_column IS NULL
ORDER BY ABS(t1.temperature - t2.temperature) DESC
LIMIT 1;
1 table joined 2 times on itself, t2 is the quaranteed direct predecessor of t1 t2 is the closest record with offset 3h before or more. This could with the proper indexes, and a limited amount of data (where limited is in the eye of the beholder) be quite performant. However, if you need a lot of those queries in a big dataset, this is a prime candidate for denormalization, were you create a table which also stores the calculated offsets compared to the previous entry.

Is it good or bad practise to alter start dates in a database to the next occurrence of an event?

I am trying to create an event calendar which whilst initially quite small could turn out to be quite large. To that end, when trying to future proof it as much as possible, all events that occur in the past will be deleted from the database. However, is it bad practise to alter the start date of recurring events once they have happened to indicate when the next event will start? This makes it easier to perform search queries because theoretically no events will start more than say a week in the past, depending on how often the database is updated.
Is there a better way to do this?
My current intention is to have a table listing the event details along with a column for whether it is a yearly, monthly, weekly or daily recurrence. When somebody then searches for events between 2 dates, I simply look at each row and check if (EVENT START <= SEARCH FINISH && EVENT FINISH >= SEARCH START). This then gets all the possible events, and the recurring ones then need to be checked to see if they occur during the time period given. This is where I come a little unstuck, as to how to achieve this specifically. My thoughts are as follows:
Yearly: if EVENT START + 1 YEAR <= SEARCH FINISH || EVENT FINISH + 1 Year >= SEARCH START; repeat for +2 YEARS etc until EVENT START + NO YEARS > SEARCH FINISH.
Monthly: As above but + 1 month each time.
Weekly: As above but EVENT START and EVENT FINISH will be plus 7 DAYS BETWEEN RECURRENCE each iteration until EVENT START + 7 DAYS REPEATED > SEARCH FINISH.
Daily: As above but NO OF DAYS DIFFERENCE instead of 7 days for a week. This could be used to specify things like every 14 days (fortnight), every 10 days. Even every week could use this method.
However, when I think about the query that would have to be built to achieve this, I cannot help think that it will be very cumbersome and probably slow. Is there a better way to achieve the results I want? I have still not found a way to do things like occurs on the first Monday of a month or the last Friday of a month, or the second Saturday of April each year. Are these latter options even possible?
-- Edit: added below:
It might help a bit if I explain a bit more about what I am creating. That way guidance can be given with respect to that.
I am creating a website which allows organisations to add events, whether they are a one-off or recurring (daily, weekly, monthly, first Tuesday of a month etc.). The user of the site will then be able to search for events within a chosen distance (arbitrary 10, 25, 50, 100miles, all of country) on a set date or between 2 given dates which could be from 1 day apart up to a couple of years apart (obviously events that far into the future will be minimal or non-existant depending on the dates used).
The EVENTS table itself currently holds a lot of information about the event, such as location, cost, age group etc. Would it be better to have this in a separate table which is looked up once it has been determined if the event is within the specified search parameters? Clearly not all of this information is needed until the detailed page view, maybe just a name, location, cost and brief description.
I appreciate there are many ways to skin a cat but I am unsure how to skin this one. The biggest thing I am struggling with is how to structure my data so that a query will know if the recursion is within the specified date. Also, given that the mathematics to calculate distance between 2 lat/longs is relatively complex, I need to be able to build this calculation into my query, otherwise I will be doing the calculation in PHP anyway. Granted, there will be less results to process this way, but it still needs to be done.
Any further advice is greatly appreciated.
Creating events for each recurrence is unnecessary. It is much better to store the details that define how the event recurs. This question has been answered many times on SO.
One way to do this is to use a structure like this -
tblEvent
--------
id
name
description
date
tblEventRecurring
-----------------
event_id
date_part
end_date
Then you could use a query like this to retrieve events -
SELECT *
FROM `tblEvent`
LEFT JOIN `tblEventRecurring`
ON `tblEvent`.`id` = `tblEventRecurring`.`event_id`
WHERE (`tblEvent`.`date` = CURRENT_DATE AND `tblEventRecurring`.`event_id` IS NULL)
OR (
CURRENT_DATE BETWEEN `tblEvent`.`date` AND `tblEventRecurring`.`end_date`
AND (
(`tblEventRecurring`.`date_part` = 'D') OR
(`tblEventRecurring`.`date_part` = 'W' AND DAYOFWEEK(`tblEvent`.`date`) = DAYOFWEEK(CURRENT_DATE)) OR
(`tblEventRecurring`.`date_part` = 'M' AND DAYOFMONTH(`tblEvent`.`date`) = DAYOFMONTH(CURRENT_DATE))
)
)
UPDATE Added the following example of returning events for a given date range.
When returning dates for a given date range you can join the above query to a table representing the date range -
SET #start_date = '2012-03-26';
SET #end_date = '2012-04-01';
SELECT *
FROM (
SELECT #start_date + INTERVAL num DAY AS `date`
FROM dummy
WHERE num < (DATEDIFF(#end_date, #start_date) + 1)
) AS `date_list`
INNER JOIN (
SELECT `tblEvent`.`id`, `tblEvent`.`date`, `tblEvent`.`name`, `tblEventRecurring`.`date_part`, `tblEventRecurring`.`end_date`
FROM `tblEvent`
LEFT JOIN `tblEventRecurring`
ON `tblEvent`.`id` = `tblEventRecurring`.`event_id`
WHERE `tblEvent`.`date` BETWEEN #start_date AND #end_date
OR (`tblEvent`.`date` < #end_date AND `tblEventRecurring`.`end_date` > #start_date)
) AS `events`
ON `events`.`date` = `date_list`.`date`
OR (
`date_list`.`date` BETWEEN `events`.`date` AND `events`.`end_date`
AND (
(`events`.`date_part` = 'D') OR
(`events`.`date_part` = 'W' AND DAYOFWEEK(`events`.`date`) = DAYOFWEEK(`date_list`.`date`)) OR
(`events`.`date_part` = 'M' AND DAYOFMONTH(`events`.`date`) = DAYOFMONTH(`date_list`.`date`))
)
)
WHERE `date_list`.`date` BETWEEN #start_date AND #end_date
ORDER BY `date_list`.`date`;
You can replace the SQL variables with PHP vars if you would prefer. To display days without any events you can change the INNER JOIN between the two derived tables, date_list and events, to a LEFT JOIN.
The table dummy consists of a single column with numbers from 0 to whatever you anticipate needing. This example creates the dummy table with enough data to cover one month. You could easily populate it using an INSERT... SELECT... on the AI PK of another table -
CREATE TABLE `dummy` (
`num` SMALLINT UNSIGNED NOT NULL PRIMARY KEY
);
INSERT INTO `dummy` VALUES
(00), (01), (02), (03), (04), (05), (06), (07), (08), (09),
(10), (11), (12), (13), (14), (15), (16), (17), (18), (19),
(20), (21), (22), (23), (24), (25), (26), (27), (28), (29),
(30), (31);
Break it up
Have one table for vents that haven't happened yet with a reccurring event ID. So you can just poke one offs in there with recurring veent id of null. Get rid /archive past ones etc.
Have another for the data about recurring events.
When an event marked as recurring happens, go back to recurring table, check to see if it's enabled (you might want to add a range to them ie do this every wek for three months), and if all is okay, add a new record for the next time it occurs.
One way to do it anyway, and it gets rid of the problem of using event start for two different things which is why your code is getting complicated.
If you want future jobs from this. ie everything needed to do in the next month.
The it would be a union query. One to get all teh "current jobs", unioned with one to get all the jobs that will recur in the next month.
Can't stress this enough, get the data design right the code "just happens". If you data is messed up as in one field "start date" serving two different needs, then every time you go near it, you have to deal with that dual use. Forget it once and you get anything from a painful mess to a disaster.
Adding a Recurring_Start_Date column would be better than your current plan, wouldn't it. You wouldn't be asking this question, beacseu your data would fit your needs.
I assume you'll be searching through events much more frequently than you will be creating new ones. During event creation, I would create records for each occurrence of the event up to so reasonable amount of time (maybe for the next year or two).
It would also make things like "The third thursday of each month" a little easier. If you tried to do any of the calculations in a query it would be difficult and probably slow.

Schedules and the database

So, I've previously developed an employee scheduling system in php. It was VERY inefficient. When I created a new schedule, I generated a row in a table called 'schedules' and, for every employee affected by that schedule, I generated a row in a table called 'schedule_days' that gave there start and stop time for that specific date. Also, editing the schedules was a wreck too. On the editing page, I pulled every user from the database from the specific schedule and printed it out on the page. It was very logical, but it was very slow.
You can imagine how long it takes to load around 15 employees for a week long schedule. That would be 1 query for the schedule, 1 query for each user, and 7 queries for each day for every user.. If I have 15 users thats too many queries. So I'm simply asking, whats someone else's view on the best way to do this?
For rotation based schedules, you want to use an exclusion based system. If you know that employee x works in rotation y within date range z, then you can calculate the individual days for that employee on the fly. If they're off sick/on course/etc., add an exclusion to the employee for that day. This will make the database a lot smaller than tracking each day for each employee.
table employee {EmployeeID}
table employeeRotations {EmployeeRotationID, EmployeeID, RotationID, StartDate, EndDate}
table rotation {RotationID, NumberOfDays, StartDate}
table rotationDay {RotationDayID, RotationID, ScheduledDay, StartTime, EndTime}
table employeeExceptions {EmployeeExceptionID, ExceptionDate, ExceptionTypeID (or whatever you want here)}
From there, you can write a function that returns On/Off/Exception for any given date or any given week.
Sounds like you need to learn how to do a JOIN rather than doing many round trips to the server for each item.

Categories