Select events that take place in given month - php

I am trying to select events from my database that are taking place in a given month.
I can't seem to figure out how to select those events that started before the given month and end after it.
I know I should probably get the date range between the two dates and see if the given month is inside this range. I would also like to create a column range which has three values based on the range of the event. When the event starts and ends inside the given month this value will be 'fits'. When the event starts in the given month, but extends past it the value is 'extends'. When it starts before the given month, but end in it, it's 'past'. And finally when it started before and goes further the value is 'full'
What I don't know is how to approach the implementation of this. Can all this be done with MySQL, or does the second part need to be implemented in PHP?
Here's an example of the table structure:
| Event_id | Date_from | Date_to |
+----------+----------------------+--------------------+
| 1 | 2014-03-05 19:28:00 | 2014-03-06 09:00:00 |
| 2 | 2014-02-05 00:00:00 | 2014-03-07 10:00:00 |
| 3 | 2014-03-28 15:00:00 | 2014-05-06 00:00:00 |
| 4 | 2014-02-05 00:00:00 | 2014-05-06 00:00:00 |
| 5 | 2014-05-06 19:28:00 | 2014-06-03 19:28:00 |
And the result of the select statement I am trying to achieve should look like this:
| Event_id | Date_from | Date_to | Range |
+----------+----------------------+----------------------+---------+
| 1 | 2014-03-05 19:28:00 | 2014-03-06 09:00:00 | 'fits' |
| 2 | 2014-02-05 00:00:00 | 2014-03-07 10:00:00 | 'past' |
| 3 | 2014-03-28 15:00:00 | 2014-05-06 00:00:00 | 'extends' |
| 4 | 2014-02-05 00:00:00 | 2014-05-06 00:00:00 | 'full' |
I am completely clueless at this point, therefore I will really appreciate any advice and tips on how this could be done. Thank you very much for reading this and for your help.
PS:
Before I realized I need to take in account the events that match the 'full' range criteria I had this:
SELECT * FROM `eventstesting`
WHERE `e_to` < '2014-04-01' OR
`e_from` > '2014-03-01'
Which successfully selects the events that match the 'past', 'fits' and 'extends' criteria, but still doesn't generate the range column.
I have also checked this topic MySQL - Get a list of months on which events are taking place, but it didn't help me

?
SELECT *
, CASE WHEN date_from >= '2014-03-01' AND date_to < '2014-04-01' THEN 'fits'
WHEN date_from < '2014-03-01' AND date_to BETWEEN '2014-03-01' AND '2014-04-01' THEN 'past'
WHEN date_from BETWEEN '2014-03-01' AND '2014-04-01' AND date_to >= '2014-04-01' THEN 'extends'
WHEN date_from <= '2014-03-01' AND date_to >= '2014-04-01' THEN 'full'
END `range`
FROM eventstesting
HAVING `range` IS NOT NULL;

date range selection trick - I think this is what you're looking for:
start < month_end_date
and
end > month_start_date
Basically you select any event that starts before the month ends and ends after the months' start - i.e. it's either partially or fully overlapping with the month.
I'm not sure if this is exactly what you need, but this is how you usually compare date ranges efficiently.
Ah, there's one more thing: if dates can be 'open' (i.e. an event can still be on-going, and the end date is still NULL, that has to be taken care of separately)

Related

MySQL: Get record where TIME('xx:yy:zz') between start and end time

This is the table:
+----+-----------+------------+----------+
| id | id_sensor | start_time | end_time |
+----+-----------+------------+----------+
| 1 | 12 | 21:15:00 | 02:45:00 |
| 2 | 7 | 00:00:00 | 23:15:00 |
| 3 | 5 | 04:30:00 | 16:30:00 |
+----+-----------+------------+----------+
I need to get record(s) where a specific time (e.g. 01:00:00) passed by PHP is between. start_time and end_time are TIME fields in UTC, I'm passing to the query hour via php, note, converted in php from user_timezone to UTC.
SELECT * FROM test WHERE TIME('01:00:00') BETWEEN start_time AND end_time;
Query returns only record id 2, not the 1. I need both, in this case (for id 1, end time ofcourse is next day).
Of course, if we looking for TIME('01:00:00'), we don't need the id 3.
Thank you.
I think this is the logic you want:
SELECT *
FROM test
WHERE (start_time < end_time AND TIME('01:00:00') BETWEEN start_time AND end_time) OR
(start_time > end_time AND TIME('01:00:00') NOT BETWEEN end_time AND start_time);
now() returns both, current time and date. You can also work with curtime(), which returns only the current time.
BTW, i think that working with SELECT * should be avoided (maybe you used it just for this example), it is IMHO always better to list the fields needed explicitly.

MySQL - selecting the correct data within the correct week using week beginnings

I am trying to select data, when inserting the data it has an auto insert of the date when submitting. So when data is inserted it inserts the current date.
However, in my table I have week beginnings, so I am trying to select the data inside of that week:
mysql> select * from week;
+---------+------+------------+
| week_id | week | date |
+---------+------+------------+
| 1 | 1 | 2014-12-29 |
| 2 | 2 | 2015-01-05 |
| 3 | 3 | 2015-01-12 |
| 4 | 4 | 2015-01-19 |
| 5 | 5 | 2015-01-26 |
| 6 | 6 | 2015-02-02 |
| 7 | 7 | 2015-02-09 |
| 8 | 8 | 2015-02-16 |
| 9 | 9 | 2015-02-23 |
| 10 | 10 | 2015-03-02 |
| 11 | 11 | 2015-03-09 |
| 12 | 12 | 2015-03-16 |
| 13 | 13 | 2015-03-23 |
| 14 | 14 | 2015-03-30 |
| 15 | 15 | 2015-04-06 |
| 16 | 16 | 2015-04-13 |
| 17 | 17 | 2015-04-20 |
e.g.
select * from table where date='2015-04-06';
However the data will not be selected and presented because the inserted date was 2015-04-10. The only way to retrieve that data is by doing this:
select * from table where date='2015-04-10'; < when the data was inserted
So my question is, is it possible to select that data from that week beginning?
So if I select data from 2015-04-06 it should show data from the range of 2015-04-06 to 2015-04-12, is that possible?
Hopefully I have explained correctly, been a bit tricky to explain let alone try to implement. I can add any more info if needed.
NOTE: I am trying to use this inside of PHP so where the date is I would just use a variable, just thought I would say.
As the week will always end 6 days from the beginning you can use the between operator and the date_add function like this:
(for your specific example):
select *
from table
where date between '2015-04-06' and date_add('2015-04-06', interval 6 day)
And using a php variable:
select *
from table
where date between '$name_of_dt_var' and date_add('$name_of_dt_var', interval 6 day)
You could also compare the week of the date the data was entered with the weeks in the week table using WEEK() function.
Assuming that week is the same value as week(), the:
select t.*
from table t
where week = week('2015-04-10');
Even if the numbers do not match, then presumably you have some base date (such as 2015-01-01 and simple arithmetic would accomplish something very similar).
I have found that the most robust way to do this sort of week processing is to truncate each date in the table (in your example 2015-04-10) to the preceding Monday at midnight. That way you can compute the week of each item by assigning it to the first day of that week.
This little formula returns the preceding Monday given any DATE or DATETIME value.
FROM_DAYS(TO_DAYS(datestamp) -MOD(TO_DAYS(datestamp) -2, 7))
For example,
set #datestamp := '2015-04-10'
SELECT FROM_DAYS(TO_DAYS(#datestamp) -MOD(TO_DAYS(#datestamp) -2, 7))
yields the value 2015-04-06.
So, if you have a table called sale you can add up sales by week like this:
SELECT SUM(amount) weekly_amount,
FROM_DAYS(TO_DAYS(datestamp) -MOD(TO_DAYS(datestamp) -2, 7)) week_beginning
FROM sale
GROUP BY FROM_DAYS(TO_DAYS(datestamp) -MOD(TO_DAYS(datestamp) -2, 7))
This is a very convenient way to handle things, because it's robust over end-of-year transitions. The WEEK() function doesn't work quite as well.
If your business rules say that your weeks begin on Sunday rather than Monday, use -1 rather than -2, as follows.
FROM_DAYS(TO_DAYS(datestamp) -MOD(TO_DAYS(datestamp) -1, 7))

Date between 2 other dates, disregarding year

I'm looking for something like the person here was lookin for, only I'd like to use MySQL. The table below is something you'd find in my database (simplified).
+------+------+------+------+
| id | name | first| last |
+------+------+------+------+
| 1 | John | 1020 | 0814 |
| 2 | Ram | 0827 | 0420 |
| 3 | Jack | 0506 | 0120 |
| 4 | Jill | 0405 | 0220 |
| 5 | Zara | 1201 | 1219 |
+------+------+------+------+
First of all the entry has to be random, not id 4 and I only want 1 entry. I worked that out: SELECT * FROM test WHERE id <> 4 ORDER BY rand() LIMIT 1.
In this table the columns 'first' and 'last' are dates formatted as mmdd (both integers). So John is available for most of the year; from October 20th to August 14th. Zara on the other hand is only available for a small period of time; December 1st till December 19th.
My question: how do I change my query to only select the available people? I can't use 'between' since, in John's case, there's nothing between 1020 and 0814.
I just can't figure it out, there's must be other people that have a similar problem... Does anyone have a solution?
Kind regards
You need to distinguish two cases.
When first < last, the dates are in the same year. You can then use between to match dates.
When first > last, it means last is in the next year. In this case, the dates that match are date >= first OR date <= last.
So your WHERE clause should be:
WHERE IF(first < last, #date BETWEEN first AND last,
#date >= first OR date <= last)

MySQL searching through time interval

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))

PHP/MySQL: Model repeating events in a database but query for date ranges

I'm working on a (what I was intending to be) simple PHP/MySQL app. As part of it I'd like to be able to model repeating events, however I need to be able to query all the events that happened between two dates (including repeated events). The events only have a date, the time of day doesn't matter.
I've been researching this and have looked in to various approaches including Calendar Recurring/Repeating Events - Best Storage Method and Repeating calendar events and some final maths.
However, any example of a database schema supporting this that I find online, only seems to support querying for events that happened on a certain day. There is no support for events that happened between a range of dates.
As an abstract example
Events table (with some sort of repeat representation):
Event | Start Date | Repeats
-------------------------------------
Meeting | 10/Dec/2012 | Every 7 days
Lunch | 10/Dec/2012 | Every 1 days
Target result of abstract query SELECT Events BETWEEN 09/Dec/2012 AND 20/Dec/2012
Event | Date | Repeats
-------------------------------------
Meeting | 10/Dec/2012 | Every 7 days
Meeting | 17/Dec/2012 | Every 7 days
Lunch | 10/Dec/2012 | Every 1 days
Lunch | 11/Dec/2012 | Every 1 days
Lunch | 12/Dec/2012 | Every 1 days
Lunch | 13/Dec/2012 | Every 1 days
etc...
Lunch | 20/Dec/2012 | Every 1 days
Is there a database schema that will support these kind of queries? How would I go around making a query on that schema for any event (including repeating events) that happened between two days?
Or perhaps a design pattern that is used for repeating events?
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.
I didn't quite understand your goal... Maybe you were looking for UNIQUE function in SQL?

Categories