Project: I am working on a project which is about some rooms and equipments using in the rooms. The software is about scheduling the equipments in the rooms. In other words, it is a reservation software that reserves selected equipments in separate rooms for needed dates and times ranges. I have many tables in MYsSQL database working with Php but I will mention the tables my question is about. The tables I will relate my questions are equipment table (Table A), schedule table (Table B) and equipments using in the related schedule (Table C).
Table A: equipment list table
eqid | eqName | available|
1 | book | 90 |
2 | pen | 82 |
3 | computer | 25 |
In table A; eqid represents unique id of an equipment, eqName represents name of an equipment, available represents total available equipments existing.
Table B: schedule table
scheduleid | startDate | endDate | startTime | endTime | office |
1 | 2012-08-27 | 2012-08-27 | 08:30:00 | 10:00:00 | room1 |
2 | 2012-08-27 | 2012-08-27 | 09:30:00 | 11:00:00 | room3 |
3 | 2012-08-28 | 2012-08-30 | 08:30:00 | 12:00:00 | room2 |
4 | 2012-08-29 | 2012-08-31 | 11:30:00 | 14:00:00 | room1 |
5 | 2012-08-28 | 2012-08-28 | 10:30:00 | 14:00:00 | room3 |
6 | 2012-08-27 | 2012-08-30 | 08:30:00 | 10:00:00 | room4 |
7 | 2012-08-27 | 2012-08-27 | 10:30:00 | 12:00:00 | room4 |
8 | 2012-08-27 | 2012-08-30 | 08:30:00 | 11:00:00 | room6 |
9 | 2012-08-27 | 2012-08-27 | 10:30:00 | 12:00:00 | room5 |
In table B; scheduleid represents unique id for a schedule, startDate and endDate are date range for a schedule, startTime and endTime time range for a schedule, office means that where the schedule will take place. Let me give an example here. Scheduleid 1 means there is a reservation on 27th of august 2012, Monday and it is from 08.30 to 10:00. As it start and end on same day this is just one day reservation in room1. However, Scheduleid 3 means there is a reservation starts on 28th of august 2012, Tuesday and goes on until 30th of august 2012, Thursday at 08:30-12:00... in other words, it lasts for 3 days and everyday from 08:30 to 12:00... So there is a reservation from Tuesday to Thursday at 08:30 to 12:00 in room2... I hope this is clear.
Table C: equipments using in the related schedule
Autoid | scheduleid | eqid | amountInSch|
1 | 1 | 1 | 2 |
2 | 1 | 2 | 3 |
3 | 1 | 3 | 1 |
4 | 2 | 1 | 1 |
5 | 2 | 2 | 1 |
6 | 2 | 3 | 2 |
7 | 3 | 2 | 1 |
8 | 3 | 3 | 3 |
9 | 4 | 2 | 1 |
10 | 4 | 3 | 1 |
11 | 5 | 1 | 1 |
12 | 6 | 1 | 1 |
13 | 6 | 3 | 2 |
14 | 6 | 2 | 4 |
15 | 7 | 1 | 5 |
16 | 7 | 2 | 6 |
17 | 8 | 2 | 1 |
18 | 9 | 1 | 8 |
19 | 9 | 2 | 5 |
20 | 9 | 3 | 6 |
In table C: Autoid represents unique automatic id generated by auto-increment, scheduleid comes from Table B, eqid comes from Table A, amountInSch represents how many (amount) equipment will use in the related schedule. I want to give an example here. Scheduleid 1 in Table C, there are 3 rows. This means that scheduleid 1 related in TAble B will use 2 books (eqid 1), 3 pens (eqid 2) and 1 computer (eqid 3) in room1 specified dates and times in table B . Another example is that scheduleid 3 in Table C is related 2 rows. It means that 1 pen (eqId 2) and 3 computers (eqId 3) will be using in room2 from 27th to 30th of august 2012 everyday from 08:30 to 12:00.
The above is the explanation and give some information about the project. The table rows are not permanent. When you make a reservation, there will be a new row in Table B and if it is selected an equipment, there will be new rows in table C...
The Question:
I want to calculate left amount of a specific equipment when I supply eqId, startDate, endDate, startTime and endTime...
An example:
eqId: 1 (book)
startDate: 2012-08-27
endDate: 2012-08-27
startTime: 08:30:00
endTime: 12:00:00
Result should be: 14 books used in schedule and 76 left available books
Because: if you look scheduleIds and related eqIds, you will only see 1, 2, 6, 7, 9 scheduleIds related to my query(dates and eqId). If you sum the all amount of related in Table C, you will get the wrong result. In other words, related amounts for eqId(1-book) and for 1, 2, 6, 7, 9 scheduleIds are 2, 1, 1, 5, 8 respectively. So if you sum them you will get 17 which is wrong. Because, 1 and 9 schedule don't intersect each other in terms of start and end Times, and 6 and 7 don't intersect each other either. as a result of them 2 stays lonely and can be count separately. We must consider 1 and 9 as summed 8 because 8 is bigger than 2. it is same for 6 and 7, considered as 5 because of 5 is bigger than 1...
So folks! I am not sure how I can sum/ this in programming algorithm. Is there a way to do in SQL or do I have to use PHP and Mysql together? and How?
Cheers!
SQLFiddle Records
I started with the following SQL to gather all date ranges that intersect with the given range:
SELECT MAX(available) - IFNULL(SUM(amountInSch), 0)
FROM Table1
LEFT JOIN Table3 USING (eqid)
LEFT JOIN Table2 USING (scheduleid)
WHERE DATE(startDate) <= '2012-08-27' AND DATE(endDate) >= '2012-08-27'
AND endTime > '08:30' AND startTime < '12:00'
AND eqid = 1
Fiddle
This is only the first part. Next up you have to work out the possible overlaps; this wouldn't be practical to do with SQL, so I would suggest to do this in PHP.
The generic algorithm I would pick is unfortunately O(n**2), it goes like this:
create a timeline (demarcated by each day) with time as the horizontal axis
iterate over every date/time range and mark the time of its left and right edge to create time segments of every possible permutation.
using the segments, you sum vertically for overlaps and you take the daily maximum across.
Hope that helps.
Related
I am calculating for the total payments that are made by each class and by the student in a class he or she has ever been enrolled in, depending on the Academic Year.
Now the Student Class Table below has two students, the student with Student ID=0001 has been enrolled in two classes in two different years Class ID = 1, Year=2013 and Class ID=2, Year=2014.
However the student with Student ID=0002 only enrolled in one class and he left the school. Another student was enrolled in class with
Class ID = 1 in Year=2014
Student Class Table
| Student ID | Class ID | Year |
| 0001 | 1 | 2013 |
| 0002 | 1 | 2013 |
| 0003 | 1 | 2014 |
| 0001 | 2 | 2014 |
Below is the amount assigned to each student for each class in a particular period.
Designated Fees
| Class ID | Amount | Year |
| 1 | 100 | 2013 |
| 1 | 120 | 2014 |
| 2 | 210 | 2014 |
Now, I want to find the total amount each 'Class' is suppose to pay in the current year (thus in 2014), with the summation of what each class was suppose to pay in the previous year ( in this case, the 2013 is the previous Year) and the Previous Year (in this case, the 2014 is the current Year).
Using the below query will give you the amount each Student is suppose to pay from the previous year and current year as far is he/she enrolled in there.
This query gives a good results without any problems
SELECT
a.`Student ID`,
a.`Class ID`,
b.`Amount`,
a.`Year
FROM
`Student Class Table` a
JOIN
`Designated Fees` b
ON
a.`Class ID`=b.`Class ID`
and
a.Year=b.Year
QUERY RESULTS
| Student ID | Class ID | Amount | Year |
| 0001 | 1 | 100 | 2013 |
| 0002 | 1 | 100 | 2013 |
| 0003 | 1 | 120 | 2014 |
| 0001 | 2 | 210 | 2014 |
I want find the total class fees for all the years based on the students enrolled, using the current year as the base year, in this case year 2014 is the current year.
For instance
| Class ID | Total Fees | Year |
| 1 | 120 | 2014 |
| 2 | 410 | 2014 |
The Total Fees for the class with Class ID=1 is 120 because only a student was enrolled in that class within the Year 2013 and 2014 and thier allocated fees for the year 2014 is 120, Also the Total Fees for the
class with Class ID=2 is 410 because two students were enrolled in class with Class ID = 1 in Year 2013 and they were expected to pay an amount of 200, and in the Year 2014 Class 2, only one student enrolled and was expected to pay an amount of 210.
Well, my thoughts on solving this has been complicated, however I added a group by to the query afforementioned, this is wrong that I know. I wanted an Idea as to how to resolve this using either mysql or php, I can't seem to find my way around it.
This will be possible if you have batch numbers for the class from the time, the class was registered. This students in such a class will bear the batch No till they complete the school.
For instance, students in class with Class ID= 1 in Year=2013 will bear the same batch No till they complete. You can choose to create another table and reference it in the Student Class Table depending on how you promote students through the classes over the years.
Student Class Table
| Student ID | Class ID | Year | BatchNo |
| 0001 | 1 | 2013 | 1 |
| 0002 | 1 | 2013 | 1 |
| 0003 | 1 | 2014 | 2 |
| 0001 | 2 | 2014 | 1 |
After Adding the Batch No, you can simply group by the batchNo and sum the amount:
SELECT
a.`Student ID`,
a.`Class ID`,
sum(b.`Amount`),
a.`Year
FROM
`Student Class Table` a
JOIN
`Designated Fees` b
ON
a.`Class ID`=b.`Class ID`
and
a.Year=b.Year
GROUP BY batchNo
This will be your Results
| Class ID | Total Fees | Year |
| 1 | 120 | 2014 |
| 2 | 410 | 2014 |
You can go by this idea and I think, it can work well.
I have a mysql database table which has 4 columns: ID, Date, SessionID, UserID(irrelevant here). Let's call them A, B, C,
What I can't figure out is how to count how many sessions has been booked to specific date.
For example something like this
A | B | C
---|------------|------
1 | 2016-02-12 | 1 |
2 | 2016-02-12 | 1 |
3 | 2016-02-13 | 1 |
4 | 2016-02-12 | 5 |
5 | 2016-02-12 | 5 |
6 | 2016-02-13 | 2 |
7 | 2016-02-18 | 2 |
8 | 2016-02-19 | 3 |
So I want that my php code would output
that for date 2016-02-12 I have 2 entries with value 1 and 2 entries with value 5.
For date 2016-02-13 I have one entrie with value 1 and one with value 2.
This should work as you need
SELECT B AS date,COUNT(C) AS sessions FROM table GROUP BY B,C
It's a simple GROUP BY.
SELECT Date, SessionID, COUNT(*)
FROM yourTable
GROUP BY Date, SessionID
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))
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'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?