This is a rather obscure question. It's more about approach than syntax.
I have a MySQL table filled with 'notifications' (id,user_id,date,etc). I have to send an alert (via email, facebook, twitter, whatever... not the issue) when each of those entries pings as 'true'. Here's the thing, how should I go about pinging them as true in the most efficient way possible when the conditions that determine true/false have to be calculated?
Sending an email of a bday is easy. Just search a date field for today's date. Suppose you have to send en email every 20th day starting from a date entered in the field? I have to calculate each row to see if it's true today.
How should I do that? I've considered the following: 1. a complex MySQL query 2. a PHP page cron job run through each row and mark them as done 1 by 1 every x seconds/min 3. pulling my hair out and running out of the room screaming like a little girl. I'm leaning toward 3 at the moment.
My main concern is that I'm on a shared server and I don't want to do anything too intensive. Thanks for spending your brain on this. I appreciate it.
You should have a look at the strtotime() examples and see if it can accomodate the types of alerts you are sending. This could allow you to represent things like annual reminders (birthdays), alerts every 20 days, monthly alerts (first Monday/last Friday of each month) in a table like so:
| id | user_id | status | send_on | next_occurrence |
|------|---------|---------|---------------------|--------------------|
| 1001 | 123 | pending | 2010-03-04 12:00:00 | Next March 4 noon |
| 1002 | 123 | pending | 2010-02-05 00:00:00 | +20 days midnight |
| 1003 | 123 | pending | 2010-02-01 08:00:00 | First Monday 8am |
You then set up a CRON job (or poor man's CRON on a shared host) that fires every ten minutes or so with some fairly simple code:
# get pending alerts
$alerts = $this->Alert->find('all', array(
'conditions' => array(
'send_on <=' => date('Y-m-d H:i:s'),
'status' => 'pending',
),
));
# send alerts
foreach ($alerts as $alert) {
# send alert and update status
$status = $this->Something->send($alert);
$status = ($status) ? 'sent' : 'failed';
$this->Alert->id = $alert['Alert']['id'];
$this->Alert->saveField('status', $status);
# generate and save next pending occurrence
$this->Alert->create();
$this->Alert->save(array('Alert' => array(
'user_id' => $alert['Alert']['user_id'],
'status' => 'pending',
'send_on' => strtotime($alert['Alert']['next_occurrence']),
'next_occurrence' => $alert['Alert']['next_occurrence'],
)));
}
Fast forward to March 5th this year and that same table now looks like this:
| id | user_id | status | send_on | next_occurrence |
|------|---------|---------|---------------------|--------------------|
| 1001 | 123 | sent | 2010-03-04 12:00:00 | Next March 4 noon |
| 1002 | 123 | sent | 2010-02-05 00:00:00 | +20 days midnight |
| 1003 | 123 | sent | 2010-02-01 08:00:00 | First Monday 8am |
| 1004 | 123 | sent | 2010-03-01 08:00:00 | First Monday 8am |
| 1005 | 123 | sent | 2010-02-25 00:00:00 | +20 days midnight |
| 1006 | 123 | pending | 2010-03-17 00:00:00 | +20 days midnight |
| 1007 | 123 | pending | 2010-04-05 08:00:00 | First Monday 8am |
| 1008 | 123 | pending | 2011-03-04 12:00:00 | Next March 4 noon |
Below is somewhat simplified explanation of how I approached a similar situation where I needed to periodically send sms and emails based on varying complex conditions:
I created 2 new tables:
scenarios (id; name; frequency)
processes (id; scenario_id; process_type; execution_type; process)
with:
scenario.frequency: hourly, daily, weekly or monthly
processes.process_type: filter or action
processes.execution_type: sql or function
Then I set up a cron according to these frequencies to go trough the scenarios table and take the scenarios of the appropriate frequency and collect the associated filter (which can be an sql statement or a php function). If the filter returns any results then perform the associated actions with the results. I also extended this system to perform setup, test and teardown so I could safely test my scenarios before activating them.
Hope this helps - Cheers
You might want to look into a queue service ala beanstalk etc.
I know with some of them you can post actions / events into your queue and set them to be periodic, conditional etc.
Queue servers / services is a big topic, but maybe just having thrown this out there will give you some more options and some alternative trains of thought.
Related
We have developed a website for pharmacies in which we show the opened pharmacies in a specific city.
I handle this with a field named "timestamps" in wich it is stored all timestamps of opening hour every 15 minutes for a period of about 3 months.
For example if every days the farmacy is open from 8:00 to 19:00 there is a range of timestamps from one time to another, with an interval of 15 minutes.
While in the frontend we have a list of opened pharmacies and I could show opened ones by querying the database with for example:
"WHERE timestamps LIKE('%1449820800%')" where the timestamp is the current time rounded to the nearest quarter hour.
The question is: considering the time ranges are different from week to week, is there a better way to handle this situation? Also because we have 25.000 users and the website is slow if we have large amount of timestamps.
Thank you in advance!
You could just have a database with each day of openning for every store :
-----------------------------------------
| StoreId | Day | HourOpen | HourClose |
=========================================
| 1 | 1 | 8:30 | 21:15 |
-----------------------------------------
| 1 | 2 | 9:00 | 17:00 |
-----------------------------------------
| 2 | 1 | 10:00 | 12:30 |
-----------------------------------------
| 2 | 1 | 14:00 | 19:00 |
=========================================
In this table, the day represent the day of the week (1 for monday, 2 for tuesday for example) and then you just have to parameter the openniong hours for each store only once.
You can then query this table to see if a store is open for a day of the week at the very moment.
If a pharmacy has an exceptionnal closure or openning hours for a day, i suggest an ovveride table like this one
----------------------------------------------------------
| StoreId | Date | isOpen | HourOpen | HourClose |
==========================================================
| 1 | 2015-12-20 | true | 10:00 | 16:00 |
----------------------------------------------------------
| 2 | 2015-12-20 | false | null | null |
==========================================================
This way, you can check first if an pharmacy has any record in this table for the current day (not depending of the time) if it does, you check if there is an opening. If there is not any entry in the override table, you check with the first table.
You also can ahve a hour model table with opening and closing time, a day table, and an associative table that creates relations between stores, hours and days.
I have a table called jobs with fields: id, status. And another table called jobs_history with fields: id, jobid, status, added_time.
Every time a job is edited through php, I insert the updated data into jobs_history table with jobid, user selected status, and added_time.
A job maybe edited many times so there could be many rows in jobs_history table for one job. And the status field may contains different status like, 1st status would be "Under review", 2nd could be "In progress", 3rd could be "E-mail sent", 4th "Completed".
Now, I need to count the total number of jobs at a specific datetime that had status "In progress" on that date. Here is some example data in jobs_history table:
id | jobid | status | added_time
--------------------------------------
1 | 10 | Under review | 2014-05-20 01:00:00
2 | 10 | In progress | 2014-05-21 02:30:00
3 | 10 | E-mail sent | 2014-05-23 10:00:00
4 | 10 | Completed | 2014-05-23 04:00:00
5 | 11 | Under review | 2014-05-19 05:00:00
6 | 11 | In progress | 2014-05-20 06:00:00
7 | 11 | E-mail sent | 2014-05-20 07:00:00
8 | 11 | Completed | 2014-05-22 08:00:00
9 | 12 | Under review | 2014-05-23 09:00:00
10 | 12 | In progress | 2014-05-24 02:00:00
11 | 13 | Under review | 2014-05-20 10:00:00
12 | 13 | In progress | 2014-05-20 12:00:00
I want to count total number of jobs that had status "In progress" on the 2014-05-21 06:00:00. The query should produce result 2, that is jobid 10 and 13, because those are the only jobs that had last status "In progress" on 2014-05-21 06:00:00.
I have about 400,000 data in the jobs_history table so the query should be written in the fastest way possible.
Thanks and I very much appreciate your help on this.
If your date format is different you can use date_format function. Check this link
http://sqlfiddle.com/#!2/4a6e8b/17
I am currently working on a project where i was required to develop a timeslot system where employee can occupy a time slot within a specific day. I am using military time as my time format 00:00:00 - 23:00:00 and the days are from sunday - saturday. I just want them to book times for each day.
what is the correct mysql schema for this?
How do i go about handling validation, making sure that they only book for the available timeslots.
----------------------------------------------------
USER_ID | DAY | STARTS_AT | ENDS_AT
----------------------------------------------------
| | |
| | |
| | |
| | |
| | |
------------------------------------------------------
Well I think the logic of which slot is available should be in your code, and then you simply keep record of which employee booked what time slot, using mysql datetime, don't think you need an extra day field, maybe something like id, user_id, start_time, end_time
The system is as such. Tutors provide their availability (Monday - Sunday) and the time frame they are available on that day (0700 - 1400) (ie: 7am - 2pm).
I am trying to figure out the best way to store and search through this information to find available tutors. Searching only needs to be done on a daily system (ie: day of the week - mon, tues, wed, etc).
My planned infrastructure:
//Tutor Availability
---------------------------------------------------------------------------
tutorID | monday | tuesday | wednesday | thursday | friday |
---------------------------------------------------------------------------
27 | 0700-1200 | NULL | 1400-1800 | NULL | NULL |
---------------------------------------------------------------------------
35 | NULL | 1400-1600 | NULL | NULL | 1100-1900 |
//Scheduled tutor sessions
------------------------------------
tutorID | day | time |
------------------------------------
27 | monday | 0700-0900 |
------------------------------------
35 | friday | 1300-1500 |
Query: SELECT tutorid FROM tutoravailability WHERE 'monday'=... is available between 0900-1100 and is not in scheduled tutor session.
I have been searching forever about how I can search through (and store) these time intervals in MySQL. Is this the best way to store the time intervals of a 24 hours day? Will it even be possible to search between these intervals? Am I approaching this from the wrong way? Any insight is appreciated.
Updated Infrastructure
//Tutor Availability
-----------------------------------------------------
tutorID | day | start_time | end_time | PK |
-----------------------------------------------------
27 | mon | 0700 | 1200 | 1 |
-----------------------------------------------------
27 | fri | 1400 | 1800 | 2 |
-----------------------------------------------------
35 | tue | 1100 | 1600 | 3 |
//Scheduled tutor sessions
--------------------------------------------------------
tutorID | day | start_time | end_time | PK |
--------------------------------------------------------
27 | mon | 0800 | 1000 | 1 |
--------------------------------------------------------
27 | fri | 1600 | 1800 | 2 |
So with this system it will be much simpler to search for available times. However I am still at a loss as to how to compare the availability against the scheduled lessons to ensure no overlap.
SELECT tutorID
FROM tutoravailability WHERE day = 'fri'
AND start_time <= '1400'
AND end_time >= '1530'
Now I don't understand how I would compare this query against the Scheduled tutor sessions table to avoid duplicate bookings.
Final Update
To ensure their are no overlapping of the Scheduled Tutors sessions I will use the MySQL BETWEEN clause to search for the start and end time.
If you store the time interval using two columns it will be much easier for you to perform a search using sql query.
i.e. tutorID, day, startTime, endTime
You can use a bit flag to indicate the availability (24 bit) and scheduled time (24 bit). Then you can use 24 bit to represent the available hours and scheduled hours for each day.
In the Tutor Availability table, let's say '1' stands for Available in and '0' stands for unavailable. In the Scheduled table, '0' stands for Scheduled, '1' stands for Unscheduled.
So the available interval 0900-1100 can be stored as POW(2,9) | POW(2,10) | POW(2,11); the scheduled 1000-1200 can be stored as ^(POW(2,10) | POW(2,12))
Then the following query can give your the availability of on tutor - available on Monday between 09 am to 11 am:
SELECT ta.tutorid FROM tutoravailability ta, tutorscheduled ts
WHERE ta.tutorid = ts.tutorid AND ts.day = 'monday'
AND (ta.monday & ts.time & (POW(2,9) | POW(2,10) | POW(2,11))) = (POW(2,9) | POW(2,10) | POW(2,11))
Please forgive me if this unclear, but I am having a tough time trying to get what is in my mind down on paper.
Scenario:
We store a variety of jobs in a table. These are all timestamped using DATETIME set to UTC time zone.
Our users may have their timezones set in their preferences, so that we can transcribe system times to the users local times.
Now, assume that a user goes to select all jobs that were entered on October 25, 2012 (local user time):
+--------------------------------------+------------+---------+---------------------+
| id | project_id | amount | created |
+--------------------------------------+------------+---------+---------------------+
| 50889ba5-77b4-41e1-a942-1dea0ab761f6 | 15076850 | 50.00 | 2012-10-25 01:53:41 |
| 5088b9a3-8110-446e-81c8-75da341f3f95 | 15076850 | 2000.00 | 2012-10-25 04:01:39 |
| 5088c852-d434-41e6-ba5d-27560ab761f6 | 15076850 | 100.00 | 2012-10-25 05:04:18 |
| 50892a3b-ad9c-4a32-aebf-384c0ab761f6 | 15076850 | 500.00 | 2012-10-25 12:02:03 |
| 50893098-6b9c-4028-9a87-3eb20ab761f6 | 15076850 | 25.00 | 2012-10-25 12:29:12 |
| 50894b10-d260-4f61-8eb9-1d190ab761f6 | 15076850 | 25.00 | 2012-10-25 14:22:08 |
| 50895129-48c8-4bb4-928f-483b341f3f95 | 15076850 | 25.00 | 2012-10-25 14:48:09 |
| 50896019-7144-4e74-8037-4160341f3f95 | 15076850 | 50.00 | 2012-10-25 15:51:53 |
+--------------------------------------+------------+---------+---------------------+
8 rows in set (0.00 sec)
If this user is in the Eastern United States (EST), and the times in this table are UTC, the results are going to come across as October 24th for the first two rows in the table results. Basically, I want to exclude those first two rows if the user is EST.
I have been experimenting with the use of DateTime(), however - I am stuck because this spans DST end (November 4, 2012). When I select the date on October 25th, DST is active, so it shows the users local time as UTC -0400. When the job ends, it is after (UTC -0500).
I am completely stuck on how to make this work.
Get the UTC timestamp for what is the user's midnight (which is 4am UTC in the below example):
$start = new DateTime('2012-10-25 00:00:00', new DateTimeZone('America/New_York'));
$start->setTimeZone(new DateTimeZone('UTC'));
$start = $start->format('Y-m-d H:i:s');
// do the same with $end
SELECT ... WHERE `created` BETWEEN $start AND $end
The final query should select WHERE BETWEEN 2012-10-25 04:00:00 AND 2012-10-26 03:59:59, which is the day of October 25th in America/New York.
This should also work during DST changes. On November 4th, the query would be BETWEEN 2012-11-04 04:00:00 AND 2012-11-05 04:59:59, encompassing one more hour.