Non-overlapping minutes per day - php

I have been cracking my head trying to resolve this problem.
I need to know how many minutes of the day are being worked by a staff member alone in the shop.
Here is the data for daynumber = 0 (monday):
For this day, the staff member with staffid = 32 is alone from 11:00 to 11:05 in the shop.
What I have so far, is just adding all starting times, but basically what I'm thinking is, if I have any way of knowing a staff member is alone, I can calculate time between the index and the next.
for($i=0; $i<count($results); $i++){
if(isset($results[$i+1])){
if($results[$i]->starttime < $results[$i+1]->starttime)
$start = strtotime($results[$i]->starttime);
$end = strtotime($results[$i+1]->endtime);
$minutes += idate('i', $end - $start);
}
}
}
Any thoughts?
UPDATE 1:
I get to this but still no luck;
for($i=0; $i<count($results); $i++){
if(isset($results[$i+1])){
$StartDate1 = strtotime($results[$i]->starttime);
$EndDate1 = strtotime($results[$i]->endtime);
$StartDate2 = strtotime($results[$i+1]->starttime);
$EndDate2 = strtotime($results[$i+1]->endtime);
if(($StartDate1 <= $EndDate2) && ($EndDate1 >= $StartDate2)){
$StartDate1 = idate('i', $StartDate1);
$EndDate1 = idate('i', $EndDate1);
$StartDate2 = idate('i', $StartDate2);
$EndDate2 = idate('i', $EndDate2);
$a = abs($EndDate1 - $StartDate1);
$b= abs($EndDate1 - $StartDate2);
$c = abs($EndDate2 - $StartDate2);
$d = abs($EndDate2 - $StartDate1);
$minutes += min([$a,$b,$c,$d]);
}
}
}
What am I doing wrong?

Here's one idea, using a utility table - in this case a table of integers from 0-9.
Utility tables are frowned on by some, but I like them because they mean less typing.
You can always replace the table with a string of UNIONs.
This is for all days. I might modify it later to show how you could filter for a specific day.
SELECT * FROM ints;
+---+
| i |
+---+
| 0 |
| 1 |
| 2 |
| 3 |
| 4 |
| 5 |
| 6 |
| 7 |
| 8 |
| 9 |
+---+
SELECT SEC_TO_TIME((i4.i*1000+i3.i*100+i2.i*10+i1.i)*60) n
FROM ints i1
JOIN ints i2
JOIN ints i3
JOIN ints i4
JOIN
( SELECT daynumber
, MIN(starttime) starttime
, MAX(CASE WHEN endtime < starttime THEN SEC_TO_TIME(TIME_TO_SEC('24:00:00')+TIME_TO_SEC(endtime)) ELSE endtime END) endtime
FROM my_table
GROUP
BY daynumber
) x
ON SEC_TO_TIME((i4.i*1000+i3.i*100+i2.i*10+i1.i)*60) BETWEEN x.starttime AND x.endtime
JOIN my_table y
ON SEC_TO_TIME((i4.i*1000+i3.i*100+i2.i*10+i1.i)*60) BETWEEN y.starttime AND CASE WHEN y.endtime < y.starttime THEN SEC_TO_TIME(TIME_TO_SEC('24:00:00')+TIME_TO_SEC(y.endtime)) ELSE y.endtime END
GROUP
BY n HAVING COUNT(*) = 1;
The number of lone minutes is equal to the number of rows in this result.

Related

How to check the id is booked of for specific days?

I have a table and records are like below table.
id | list_id |venue_id |name | start_date | end_date |start_time |end_time | days
1 | 1 | 1 |asdf | 2019-02-02 14:05:54| 2019-02-28 14:05:54|05:30 |10:00 | 1,2,3
2 | 7 | 2 |awed | 2019-02-10 15:02:24| 2019-02-20 15:02:24|07:30 |14:00 | 2,5
3 | 7 | 1 |mjgd | 2019-02-04 09:05:54| 2019-02-13 09:05:54|09:30 |18:00 | 4
Now What I am doing is I have to check the start_time and end_time range if found in the range then check start_date and end_date are in the range if found then display the records.
So below query is working for the above scenario
SELECT * FROM batch_list
WHERE venue_id=1 AND (start_date <= '2019-03-01') AND (start_time <= '13:00:00') AND (end_date >= '2019-02-04') AND (end_time >= '10:00:00')";
Now I have one more column which is days. So what I am doing is venue_id=1 is booked for a day which is 1,2,3,4 then display the records.
So how do i check the days which is already booked for id 1?
So what query I have to the user it to check the days are already in the table or not of the venue id=1?
function fetchBatches($venue_id,$new_batch_start_date,$new_batch_end_date,$new_batch_start_time,$new_batch_end_time,$days)
{
$where="venue_id=$venue_id AND (start_date <= '$new_batch_end_date') AND (start_time <= '$new_batch_end_time') AND (end_date >= '$new_batch_start_date') AND (end_time >= '$new_batch_start_time')";
$result =$this->db->select('*')
->from('batch_list')
->where($where)
->get()
->result();
if ($result) {
return $result;
}
else{
return 0;
}
}
Would you help me out on this issue?
A query to check whether one range overlaps another might look like this:
SELECT x.*
FROM my_table x
WHERE x.start_datetime < :end_datetime
AND x.end\datetime >= :start_datetime;
But really your question is broader than this

Get month difference only if the days are the same

How can I get the difference of months between two dates in MySQL only if the days are the same? Here is what i have so far but it doesn't work because when the days are not the same, it still works:
TIMESTAMPDIFF(MONTH, '2019-05-05', '2019-06-05')
Output: 1
TIMESTAMPDIFF(MONTH, '2019-05-05', '2019-06-22')
Output: 1 // should NOT work because days are 05 and 22. Needs to be the same dates
I can obviously do:
DAY('2019-05-05') = DAY('2019-06-05') AND TIMESTAMPDIFF(MONTH, '2019-05-05', '2019-06-05')
but how can I do it in one function?
Thanks!
SUBDATE()/ADDDATE/DATE_SUB/DATE_ADD will handle this well:
SELECT DATE_SUB('2019-06-22', INTERVAL 1 MONTH) = '2019-05-05'
| DATE_SUB('2019-06-22', INTERVAL 1 MONTH) = '2019-05-05' |
| ------------------------------------------------------: |
| 0 |
SELECT DATE_SUB('2019-06-22', INTERVAL 1 MONTH) = '2019-05-22'
| DATE_SUB('2019-06-22', INTERVAL 1 MONTH) = '2019-05-22' |
| ------------------------------------------------------: |
| 1 |
See the DBfiddle
In MySQL, you can get it by using the below query...
select DATEDIFF('2019-06-22', '2019-05-05') as Diff from table_name where
day('2019-06-22') == day('2019-05-05');
using column name...
select DATEDIFF(dateColum2, dateColum1) as Diff from table_name where
day(dateColum2) == day(dateColum1);
From PHP you can get it like below...
$dateValue1 = '2019-05-05';
$dateValue2 = '2019-06-22';
$time1=strtotime($dateValue1);
$day1=date("D",$time1);
$time2=strtotime($dateValue2);
$day2=date("D",$time2);
if($day1 == $day2 ) {
$seconds_diff = $time2 - $time;
}

Group Query Results by Start of Week

I have a few different tables that I'm trying to join aggregate results out of and group them based on different time intervals.
calendar table
+------------+
| adate |
+------------+
| 2015-09-24 |
| 2015-09-25 |
| 2015-09-26 |
| 2015-09-27 |
| 2015-09-28 |
....etc.......
plays table
+----+------------+----------+---------------------+---------------------+
| id | session_id | video_id | created_at | updated_at |
+----+------------+----------+---------------------+---------------------+
| 1 | 1 | 1 | 2015-09-25 15:49:50 | 2015-09-25 15:49:50 |
+----+------------+----------+---------------------+---------------------+
And the PHP/SQL:
switch ( TRUE ) {
case ($interval->days <= 7):
default:
$group_by = 'DATE(dates.adate)';
break;
case in_array( $interval->days, range(8, 31) ):
$group_by = 'CONCAT(YEAR(dates.adate), "/", WEEK(dates.adate))';
break;
case ($interval->days > 31):
$group_by = 'YEAR(dates.adate), MONTH(dates.adate)';
break;
}
$sql = '
SELECT calendar.adate,
IFNULL(g.event_count, 0) event_count
FROM calendar
LEFT JOIN (
SELECT DATE(events.created_at) created_at,
COUNT(*) event_count
FROM plays events
WHERE events.video_id = %d
AND events.created_at >= %s
AND events.created_at < date_add(%s, INTERVAL 1 DAY)
GROUP BY DATE(events.created_at)
) g
ON g.created_at = calendar.adate
WHERE calendar.adate BETWEEN %s AND %s
GROUP BY ' . $group_by . '
ORDER BY adate;
';
I'd like to change the grouping based on the interval length, ie. if it's a week's worth of results then group by each day, a months worth of results should group by "week of 9/28/15", a years worth of results should group by mm/yyyy
Can you show how to group the result set accordingly? I feel like I am close but missing something here.

Displaying daily totals between now and x days ago using MYSQL/PHP

I have a table called 'orders' and it contains; id, order_total and time fields. 'time' is an integer and stores a unix timestamp...
orders
| id | order_total | time |
-------------------------------------
| 1 | 42.00 | 1443355834 |
| 2 | 13.00 | 1443460326 |
| 3 | 51.00 | 1443468094 |
| 4 | 16.00 | 1443477442 |
| 5 | 10.00 | 1443606966 |
| 6 | 53.00 | 1443608256 |
I want to able to display in a table using php the sum, of 'order_total' for each day for the previous 'x' amount of days (or weeks or months) so it will look something like this:
| Date | Order Total |
---------------------------
| 27/09/15 | 42.00 |
| 28/09/15 | 80.00 |
| 30/09/15 | 63.00 |
I have made a MYSQL query and a php loop that kind of works but being new to MYSQL I am probably over-complicating things and there must be an easier way to do this ? When I say kind of works, it will correctly sum and show the order_totals up until the current day but for some reason will combine the current day with the previous day.
Here is what I currently have:
$x = $interval;
$y = $x - 1;
while ($x > 0) {
$sql10 = "
SELECT id,
time,
SUM(order_total) as sum,
date_format(DATE_SUB(FROM_UNIXTIME($now_time), INTERVAL $x DAY), '%Y-%m-%d') as thedate
FROM $ordersTABLE
WHERE FROM_UNIXTIME(time) BETWEEN date_format(DATE_SUB(FROM_UNIXTIME($now_time), INTERVAL $x DAY),'%Y-%m-%d')
AND date_format(DATE_SUB(FROM_UNIXTIME($now_time), INTERVAL $y DAY),'%Y-%m-%d')";
$result10 = mysql_query ( $sql10, $cid ) or die ( "Couldn't execute query." );
while ( $row = mysql_fetch_array ( $result10) ) {
$order_total = $row ["order_total"];
$thedate = $row ["thedate"];
$sum = $row ["sum"];
$sum = number_format($sum,2);
$thedate = strtotime($thedate);
$thedate = date("d/m/y",$thedate);
print "<tr><td width=\"120\">$thedate</td><td>\$$sum</td></tr>";
}
$x--;
$y--;
}
(The string $now_time contains the current time as a Unix Timestamp hence the converting as the system time can not be changed and this contains the correct local time for the user)
Is there better way to do this ?
You can convert the timestamps into YYYY MM DD using FROM_UNIXTIME function and then select only the ones which are older enough thanks to the DATEDIFF function. Today's date is provided by CURDATE function.
First of all, the query which retrieves the totals for the orders older then the interval and reformats the date fields:
$q1 = "SELECT " . $ordersTABLE . ".order_total AS total, FROM_UNIXTIME(" . $ordersTABLE . ".time, '%Y-%m-%d') AS short_date FROM " . $ordersTABLE . " WHERE DATEDIFF(CURDATE(), short_date) > " . $intervalInDAYS;
Then, the one that sums up the totals of the day:
$q2 = "SELECT short_date AS day, SUM(total) AS total FROM (" . $q1 . ") GROUP BY short_date";
And then you perform your query stored in $q2 and all other operations you need to display the result.
Result from the query should be in form:
| day | total |
===========================
| 25/09/15 | 34.00 |
| 16/09/15 | 100.00 |
| 31/07/14 | 3.20 |
| ... | ... |

Query: Calculate average price of stay depending on dates

Hello all,
I need to create a query for house search, that would match in database user entered data: date when they want to move in and leave, number of people they have in group and price per night.
Lets say user searched for house:
dates: from 2011-01-15 to 2011-03-01 (see on picture period A1C1), for 3 people, and he is willing to spend from $90 to $125 dollars per night.
This is my manual calculations for this search:
dates available in database
total number of dates user wants to stay is: 44 days
price for the first period 2011-01-15 to 2011-01-25 is 10 days * $100 = $1000
price for the second period 2011-01-25 to 2011-02-14 is 20 days * $120 = $2400
price for the third period 2011-02-14 to 2011-03-01 is 14 days * $140 = $1960
total average price per night = 1000 + 2400 + 1960 / 44 = $121.8
price and number of people matches user input, so we display this house
If you merge dates and calculate average price per night for the given period, search script should match array of data provided above.
My question is this: How my query should look like to calculate quickly if user data matches records in database.
I was thinking about using SQL DATEDIFF function and then multiply by price ... etc but it looks to me pretty complex.
I will appreciate any advice.
Thank you
UPDATE
Here is my database schema:
Table "apt_search_periods" which stores all merged dates (continuous dates from availability table)
+-----------+------------+------------+-----------+--------------+--------+
| period_id | start_date | end_date | rental_id | nb_of_people | merged |
+-----------+------------+------------+-----------+--------------+--------+
| 21 | 2011-03-31 | 2012-03-31 | 548 | 4 | y |
+-----------+------------+------------+-----------+--------------+--------+
Table "apt_search_periods_avail" linking merged dates with availability table
+----+-----------+-----------------+
| id | period_id | availability_id |
+----+-----------+-----------------+
| 21 | 21 | 20953 |
| 22 | 21 | 20952 |
| 23 | 21 | 4033 |
+----+-----------+-----------------+
Table "availability" with expanded dates and prices
+-------+-----------+------------+------------+--------------+--------------+
| id | rental_id | start_date | end_date | nb_of_people | rent_per_day |
+-------+-----------+------------+------------+--------------+--------------+
| 20952 | 548 | 2011-03-31 | 2011-07-01 | 4 | 575 |
| 4033 | 548 | 2011-07-01 | 2011-09-01 | 4 | 680 |
| 20953 | 548 | 2011-09-01 | 2012-03-31 | 4 | 575 |
+-------+-----------+------------+------------+--------------+--------------+
Following should get you started.
Note that the only difference is that the third period comprises 15 days io 14 according to DATEDIFF.
SQL Statement
;WITH q AS (
/* Kick of with the record where startdate < input < enddate */
SELECT date_start
, date_end
FROM #HouseSearch
WHERE date_start <= #date_start
AND date_end >= #date_start
AND nb_people >= #nb_people -- Only when number of people is adequate
UNION ALL
SELECT q.date_start
, hs.date_end
FROM q
INNER JOIN #HouseSearch hs ON hs.date_start = q.date_end
WHERE nb_people >= #nb_people -- Only when number of people is adequate
)
SELECT *
FROM (
-- Only return result if sequence exists between date range
SELECT date_start = MIN(date_start)
, date_end = MAX(date_end)
FROM q
WHERE date_end >= #date_end
) datetimerange
-- Calculate the average price
CROSS APPLY (
SELECT [AveragePrice] = SUM(price / DATEDIFF(dd, #date_start, #date_end))
FROM (
-- Price for all records where date_end <= #date_end
SELECT [price] =
CASE WHEN #date_start < date_start
THEN DATEDIFF(dd, date_start, date_end) * price
ELSE DATEDIFF(dd, #date_start, date_end) * price
END
FROM #HouseSearch
WHERE #date_end > date_end
UNION ALL
-- Price of remaining records where date_end >= #date_end
SELECT DATEDIFF(dd, date_start, #date_end) * price
FROM #HouseSearch
WHERE #date_end between date_start AND date_end
) prices
) price
WHERE date_start IS NOT NULL
Test data
DECLARE #HouseSearch TABLE (
date_start DATE
, date_end DATE
, nb_people INTEGER
, price FLOAT
)
INSERT INTO #HouseSearch VALUES
('2011-01-01', '2011-01-25', 4, 100)
, ('2011-01-25', '2011-02-14', 3, 120)
, ('2011-02-14', '2011-03-12', 3, 140)
, ('2011-03-12', '2011-04-10', 3, 100)
DECLARE #date_start DATE = '2011-01-15'
DECLARE #date_end DATE = '2011-03-01'
DECLARE #nb_people INTEGER = 3
DECLARE #price_low FLOAT = 90
DECLARE #price_high FLOAT = 15

Categories