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

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

Related

MYSQL Optimzation: Fetching Data w.r.t DAY OF THE WEEK

I have to work on CRON which will be sending email to subscriber weekly on the day they get subscribed. For example if user A subscribed on Thursday and user B subscribed on Wednesday then user A will get mail on every Thursday and user B on every Wednesday.
Now my approach will be following:
1- First get the day of the week of current(TODAY) date and assign in a variable
2- Running the SELECT query and fetch all subscriber IDs who's subscription day's is similar to the day of Today's Date. I am planning to use MYSQL's dayofweek() to extract day from Week,
3- Once getting all IDs then send last 7 day activities to those subscribers via email.
Thing thing which is making me a bit puzzled is DAYOFWEEK() function which column based and looks costly. What alternative would you suggest?(Assuming the table would have lots of data)
Per-row functions rarely scale well as the database table grows.
The first thing you should do is make sure there's actually a performance problem to solve. Always start with third normal form and regress only if you find such a problem, otherwise your effort is wasted. It may be that the speed is not that bad in which case stick with 3NF.
If it turns out there is a performance problem, one way to solve it is to add and indexed column called weekday that will hold the day of the week the user subscribed.
This is technically breaking 3NF since that attribute is dependent on the date of subscription which is unlikely to be part of the key. It may also come to disagree with that subscription date if you update one or the other independently.
But you can mitigate the problem by having an insert/update trigger which forces the weekday column to agree with the subscription date, ensuring that they never disagree.
Then your query simply becomes something like:
dow = Now.dayOfWeek()
rowSet = executeQuery ("select sub_id from subscribers where weekday = ?", dow)
and then processing each of those subscribers (or as one big honkin' query if you wish).
The fact that you're not having to retrieve every row to do a getWeekDay (subscription_date) and filter the rows should massively improve the query speed.
The vast majority of databases are read far more often than written and, by shifting the cost of the calculation to the insert/update, you effectively amortise that cost over all selects.
Assuming your subscribers subscribe for more than a week (since you send out their stuff once a week), that will be more efficient than calculating on the select.
And, although this takes up more space in your table (due to the extra column and index), have a look at the ratio of "My query isn't fast enough" questions compared to "My database is too big" questions. The former far outweigh the latter.

Save multiple dates of an event in MySQL

I'm building a system that shows "events for this month", listed by day and hour.
When I create the event, I set a start date, an end date and a hour.
Let's say that one event starts at 07-10-2011 and ends 07-12-2011. The problem is that some days in this date range will not feature the event. As an example, this event may happen all days and at the same hour, except some few days where it will not happen or has a different hour (think about a show with an opening date different than the rest of the days).
I'm using PHP, MySQL and Codeigniter and my doubt is about the right way to save those dates in the database. Another table with all the dates and the event ID, or save them all in a field inside the event row? Or something else?
Thanks
I'd create two tables. The first table is an events table, and the other is an events_dates table. This way you can create a single event and have as many dates linked to it as you want.
The events_dates table can be as detailed or simple as you want. If it were me, I'd probably have a start_time and end_time column, as well as an event_id and any other data you want.
I would store the range date in a table and then create an "exception" table where you can store your exceptions.

Filtering popular items by using day / week / month

I am developing a website which will have 200.000 pages. There is also a browse section, which shows most popular, highest rated etc. documents. However this section will become almost static couple of weeks later, after launch. So I also would like to implement a filtering system which will show today's, this week's, this month's most popular items, just like youtube.
Just like this:
http://www.youtube.com/videos?c=2
How should I implement this function? Do I need another table, which will have a new entry for every document each day?
docid, date, view_count, rating
So I will get today's row for filtering by using a day, or calculate a week (7 rows) for filtering by using week? It seems not efficient. Do you have any suggestions?
I am using LAMP stack by the way.
Thanks,
Assuming you timestamp the records in your table, you should be able to put a where clause that limits the timestamp to whatever timeframe you want.
You can cache the result, especially the longer ones, for long enough to make the request inconsequential.
EDIT
But perhaps you mean most popular today, not most popular that was added today?
In which case, I don't have an answer.
The most direct approach is to save the timestamp and the resource id each time the resource is shown in recent_views(what, when). Daily/weekly/monthly charts can be created with appropriate WHERE clauses like WHERE when > $beginOfPeriod AND when < $endOfPeriod.
For performance reasons you can aggregate the values each night, save the sums in separate tables like daily_views(what, sum) and truncate the source table.
I guess I would calculate the date's in code and then pass them as arguments, to the SQL you are using.
I would do it using a compiler. Youtube probably does that too, considering the amount of traffic and the response times.
The principle is easy to understand. You log every every view or rating in a page_view table. You define periods at which the compilation occurs (hourly, daily, weekly, monthly). Every time you hit the good time (e.g.: end of the day), you execute the compiler, which essentially execute a query à-la...
SELECT * FROM page_view WHERE date > $from_date AND date < $to_date
... and store the result. This probably works better in a cron job.
The next time you need to display the information, you can just fetch the stored result and display it without re-computation. There are a variety of storage methods you can use: a MySQL table (e.g.: page_view_compiled), memcached, etc.

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.

Best way to query calendar events?

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.

Categories