Selecting entries by date and by time seperately with MySQL - php

I have this query that selects every appointment with status Pending. Works well. The problem with this query it will also select appointments that are pending in the past. I only want to display those that are either today at a later hour than current_time or simply at a later date. Time and date are in a different column. In the example below only the second and third row should be returned. I'm giving you the full query as it is used and working in my app right now. How can this be achieved?
user_schedule table
id | customer_id | date | time | cleaning_status
1 | 345 | 2020-06-09 | 08:00:00 | Pending
2 | 768 | 2020-06-09 | 19:00:00 | Pending
3 | 913 | 2020-06-11 | 07:00:00 | Pending
PHP
if(!empty($_POST)){
//variables
$current_time ='16:00:00';
$current_date ='2020-06-09';
$my_city ='Miami';
$sstatus_o = 'Pending';
//query
$data = $conn->prepare("select *,us.id as orderid,us.customer_id
as ownerId from user_schedule us
left join users u
on us.customer_id=u.id
LEFT JOIN user_avatar ua
ON us.customer_id=ua.user_id
and ua.last_update = (select
max(last_update)
from user_avatar ua1 where
ua.user_id=ua1.user_id)
left join user_address uad
on us.customer_id=uad.user_id
where (uad.city LIKE ?) AND
us.cleaning_status=? ORDER BY us.id DESC");
$data->bind_param('ss',$my_city,$sstatus_o);
$data->execute();
$result_data = $data->get_result();
}

You can add another constraint to your query that checks whether the timestamp formed from your date and time values is greater than your $current_date and $current_time values i.e.
WHERE uad.city LIKE ?
AND us.cleaning_status = ?
AND TIMESTAMP(us.date, us.time) > TIMESTAMP(?, ?)
and then add the $current_date and $current_time variables to the bind_param i.e.
$data->bind_param('ssss', $my_city, $sstatus_o, $current_date, $current_time);

Related

Joining a calendar table to a sales table to get total sales for every day in a specified range

I have a 'sales' table called phpbb_sold which records each 'sale' as a row.
I am able to use a WHERE clause with the uitemid field to select one particular item in the sales records, as seen below:
SELECT uitemid, locktime, migrated_sold FROM phpbb_sold WHERE uitemid=342;
+---------+------------+---------------+
| uitemid | locktime | migrated_sold |
+---------+------------+---------------+
| 342 | 1632523854 | 1 |
| 342 | 1634239244 | 1 |
| 342 | 1634240072 | 1 |
| 342 | 1636367271 | 1 |
+---------+------------+---------------+
uitemid = number that identifies this as a sale of X item. locktime = UNIX timestamp that shows the datetime that the item was sold. migrated_sold = the quantity of the item sold. So this is nice, I have a table that keeps a record of each sale as it happens.
What I want to achieve though, is a record of the total number of sales of this item type, for each day in a 6 month period spanning back from the current date, and including each day regardless of whether a sale was made or not. So the desired output of my query would be:
SELECT (the query I want goes here) and returns the following rows...;
+------------+------------+
| caldate | sold_total |
+------------+------------+
| 2021-09-23 | 2 |
| 2021-09-24 | 0 |
| 2021-09-25 | 1 |
| 2021-09-26 | 0 |
| 2021-09-27 | 0 |
| 2021-09-28 | 1 |
+------------+------------+
Note that each day is included as a row in the results, even where the sales total for that day is 0. I read that to do this, I would be required to create a calendar table with one column and all the days I want as rows, so I went ahead and did that:
SELECT caldate FROM phpbb_calendar;
+------------+
| caldate |
+------------+
| 2021-09-23 |
| 2021-09-24 |
| 2021-09-25 |
| 2021-09-26 |
| 2021-09-27 |
| 2021-09-28 |
+------------+
Now all that remains is for me to make the query. I need to somehow return all the rows from the phpbb_calendar table, joining the data from sum() (?) of the total migrated_sold for those days where exists, and a 0 where no sales took place.
I anticipated some issues with the UNIX timestamp, but it's okay because I am able to get caldate and locktime fields to be the same format by using from_unixtime(locktime, '%Y-%m-%d'), so both dates will be in the YYYY-MM-DD format for comparison.
Please could someone help me with this. I've gotten so close every time but it seems that everyone else's request is only slightly different from mine, so existing questions and answers have not been able to satisfy my requirements.
End goal is to use a JS chart library (AnyChart) to show a line graph of the number of sales of the item over time. But to get there, I first need to provide it with the query necessary for it to display that data.
Thanks
Update
Using this query:
SELECT c.caldate, u.uitemid, sum(v.migrated_sold) as total_sales
from phpbb_calendar c cross join
(select distinct uitemid from phpbb_sold) u left join
phpbb_sold v
on c.caldate = from_unixtime(v.locktime, '%Y-%m-%d') WHERE u.uitemid = 39 and c.caldate <= curdate() GROUP BY c.caldate ORDER BY c.caldate;
Returns:
But as you can see, it's just tallying up the total number of sales ever made or something - its clearly incrementing in a way I don't understand.
I don't want it to do that - I want it to count the number of total sales on each day individually. The results should look like this:
So that what is returned is basically a 'histogram' of sales, if any occurred, including 'empty' days where there were no sales (so these empty days must still be returned as rows).
SELECT c.caldate, u.uitemid, COALESCE(SUM(v.migrated_sold), 0) AS total_sales
FROM phpbb_calendar c
CROSS JOIN (SELECT DISTINCT uitemid FROM phpbb_sold WHERE uitemid = 37) u
LEFT JOIN phpbb_sold v
ON v.locktime BETWEEN UNIX_TIMESTAMP(TIMESTAMP(c.caldate)) AND UNIX_TIMESTAMP(TIMESTAMP(c.caldate, '23:59:59'))
AND u.uitemid = v.uitemid
WHERE c.caldate BETWEEN CURDATE() - INTERVAL 6 MONTH AND CURDATE()
GROUP BY c.caldate, u.uitemid
ORDER BY c.caldate;
N.B. I have changed your join to use the unix_timestamp as it should be more efficient and it can use any existing index on locktime
check this out:
select id, d, sum(s) from (
select U.id, d, 0 s from (
select adddate(current_date(),-rows.r) d from (
select (#row_number := #row_number + 1) r
from information_schema.columns,
(SELECT #row_number := 0) AS x
limit 200
) rows
) dates,
(SELECT distinct uitemid id FROM `phpbb_sold`) U
where d > adddate(current_date(), interval -6 month)
union
select uitemid, date(from_unixtime(locktime)),sum(migrated_sold)
from `phpbb_sold`
group by uitemid, date(from_unixtime(locktime))
) sales_union
group by id, d
order by id, d;
see dbfiddle
no need for calendar table

MySQL Specific condition for each row in same query +PHP

Please consider my tables
store - table with information of all my stores
zone - An alias table with zone_id and the name of the zone.
store_zone - Table that contains the information when a store was changed to a different zone.
cashdesk - table with local_id that contains information about the
income for each day and each store
store_zone table:
+----+----------+---------+-------------+
| id | store_id | zone_id | modified_at |
+----+----------+---------+-------------+
| 1 | 1 | 2 | 2019-01-01 |
| 2 | 1 | 1 | 2019-01-04 |
| 3 | 2 | 4 | 2019-01-03 |
+----+----------+---------+-------------+
store_id is the foreign key for my local table id
store table also contains a fk for zone_id
zone_id is the foreign key for my zone table id
Now consider my problem:
I need to select all stores
get the sum(income) per row (stores)
filter by zone
consider only incomes that correspond to the local_zone table between
dates. (if I select the whole month and a zone was changed the day
15.. I only care about the 15 first days). I can use php to help build it.
something like:
$zone_modified_dates = //result of
"SELECT modified_at
FROM store_zone
WHERE zone_id = 1 AND store_id = 1"
$zone_modified_dates = '2019-01-04'
SELECT sum(income) from cashdesk c
inner join store s ON s.id = c.store_id
inner join store_zone sz ON (sz.store_id = s.id AND sz.zone_id = z.id
WHERE zone_id = 1
AND cashdesk created_at >= '2019-01-01'
AND cashdesk created_at >= '2019-01-10'
AND //IF ZONE WAS CHANGED IN THIS CREATED_AT BETWEEN DATES ONLY CONSIDER THE DAYS THAT THE SPECIFIC ROW STORE WAS CONSIDERED ZONE 1
GROUP BY s.id
Thanks in advance.
#edited for better explanation of the problem.

How to check given date period is between two dates in MySQL and PHP

I have a travel history of the employee. I want to check, for the particular month, whether he is in outstation (traveled outside) or in the office, to calculate number of hours in travel. Just we are maintaining the travel database, in that we entered employee name with client place traveled with travel date and returned date.
One employee have the following data:
Traveled date: '2015-08-29' (29th Aug 2015)
returned date: '2015-11-06' (6th Nov 2015)
So here, I want to check in the month of October, all employees that are out of the office. Obviously this guy should come in that category, but I could not get him.
I also tried directly in MySQL workbench, but I didn't get the result.
My original PHP code:
// $req['date_start'] = '2015-10-01'
// $req['date_end'] = '2015-10-31'
$employeeTravel = new EmployeeTravelRecord();
$TravelEntryList = $employeeTravel->Find("(travel_date between ? and ? or return_date between ? and ? )",array($req['date_start'], $req['date_end'],$req['date_start'], $req['date_end']));
$startdate = $req['date_start'];
$enddate = $req['date_end'];
foreach($TravelEntryList as $Travelentry){
$key = $Travelentry->employee;
if($startdate >= $Travelentry->travel_date)
{
$firstdate = $startdate;
}
else
$firstdate = $Travelentry->travel_date;
if($enddate <= $Travelentry->return_date )
{
$lastdate = $enddate;
}
else
$lastdate = $Travelentry->return_date;
$holidays = $this->getholidays($firstdate,$lastdate);
$totalhours = $this->getWorkingDays($firstdate,$lastdate,$holidays); //It returns in total time of outstation in hours excluding company holidays
$amount = $totalhours;
if(isset($TravelTimeArray[$key])){
$TravelTimeArray[$key] += $amount;
}else{
$TravelTimeArray[$key] = $amount;
}
}
But my input data doesn't retrieve that particular employee record, because both traveled date and returned date don't fall in my input dates.
MySQL Workbench:
SELECT * FROM employeetravelrecords where travel_date between '2015-10-01' and '2015-10-31' or return_date between '2015-10-01' and '2015-10-31';
I got only the following result:
+----+----------+---------------+----------------+----------+------------------+------------------+----------------+
| id | employee | type | project | place | travel date | return date | details |
+----+----------+---------------+----------------+----------+------------------+------------------+----------------+
| 13 | 38 | International | PVMTC Training | Hongkong | 11/10/2015 13:33 | 28/11/2015 13:33 | PVMTC Training |
| 14 | 48 | International | PVMT | VIETNAM | 10/10/2015 9:28 | 1/1/2016 9:28 | PETRO |
| 17 | 57 | International | PVMT | Saudi | 10/10/2015 11:39 | 2/1/2016 11:39 | |
+----+----------+---------------+----------------+----------+------------------+------------------+----------------+
The following record didn't get retrieved by this query:
+---+----+---------------+------+-----+-----------------+-----------------+-----------------+
| 7 | 22 | International | MOHO | XYZ | 29/8/2015 18:00 | 6/11/2015 18:00 | FOR DDS review |
+---+----+---------------+------+-----+-----------------+-----------------+-----------------+
SELECT * FROM employeetravelrecords
WHERE
return_date >= '2015-10-01' /* start parameter */
and travel_date <= '2015-10-31' /* end parameter */
The logic seems a little mind-bending at first and I've even reordered the comparisons a little from my original comment above. Think of it this way: you need to return after the start of the range and leave before the end of the range in order to have an overlap.
For a longer explanation and discussion you might find this page useful: TestIfDateRangesOverlap
SELECT '2015-08-29' < '2015-10-31' AND '2015-11-06' >= '2015-10-01' on_leave;
+----------+
| on_leave |
+----------+
| 1 |
+----------+
You can use between for get data between two dates. Here is the working example.
Here is my table structure :
Table User
user_id user_name created_date modified_date
1 lalji nakum 2016-01-28 17:07:06 2016-03-31 00:00:00
2 admin 2016-01-28 17:25:38 2016-02-29 00:00:00
And here is my mysql query :
Query
SELECT * FROM `user` WHERE created_date between '2015-12-01' and '2016-12-31' or modified_date between '2015-12-01' and '2016-12-31'
Result of above query :
Result
user_id user_name created_date modified_date
1 lalji nakum 2016-01-28 17:07:06 2016-03-31 00:00:00
2 admin 2016-01-28 17:25:38 2016-02-29 00:00:00
You want to find all those people who are not available for the complete duration say between T1 & T2 (and T2 > T1). The query you used will only give users whose start date and return date both lie between the given interval which is not the required output.
So to get the desired output you need to check for all employees who have started their journey on or before T1 and return date is on or after T2 (thus unavailable for complete interval [T1, T2]). So the query you can use is:
SELECT * FROM employeetravelrecords where travel_date <= '2015-10-01' and return_date >= '2015-10-31';
If you want employees who are even partially not available between the given duration then we need employees who started their travel before T1 and have any return date later than T1 (thus making them atleast unavailable for a part of the given interval):
SELECT * FROM employeetravelrecords where travel_date <= '2015-10-01' and return_date > '2015-10-01';

SQL count distinct visits of a customer with opening hours from 10am to 6am

I am writing a customer loyalty software for a club that opens from 10am to 6am everyday. The data is store in MYSQL and I'd like to count the customer's total visits for the month.
I am using count(distinct(date)) but if the player came at 5pm and stayed till 3am with 2 transactions at 10pm and 2am. It will be counted as 2 visits instead of 1.
I have a transaction table with the columns listed below:
ps: anything in the brackets () is not real data. I get about 2000 transactions a day. I am also able to change the table structure
Transaction_ID | Date(not Date/Time) | Customer_ID | Item | price | timestamp
1 | 11-06-2015 (6pm) | Jane | drink| 2.00 | 156165166
2 | 09-06-2015 (2pm) | Jane | drink| 2.00 | 1433858493
3 | 10-06-2015 (3am) | Jane | drink| 2.00 | 1433906073
4 | 06-06-2015 (6pm) | Jane | drink| 2.00 | 156165166
Current code returns {4, Jane}. The answer I'm looking for is {3,Jane}. Transaction {2,3} should be considered as one visit
SELECT count(distinct(Date)) as visit, Customer_ID
FROM transaction
GROUP BY Customer_ID
WHERE timestamp BETWEEN $timestamp1 AND $timestamp2
$timestamp1 = strtotime("first day of february +10am");
$timestamp2 = strtotime("first day of march +6am");
How do you suggest to accurately count the total visits below? I am able to change the table structure from Date to Date/time.
The easiest answer with least changes to my codes.
SELECT count(DISTINCT(DATE(DATE_SUB(from_unixtime(timestamp),INTERVAL 6 HOUR))) as visit, Customer_ID
FROM transaction
GROUP BY Customer_ID
WHERE timestamp BETWEEN $timestamp1 AND $timestamp2
The easiest way is to shift your datetime (date,timestamp?) field back for 6 hours in a SQL statement and then you will get an interval in one day from 4AM to 12PM:
DISTINCT(DATE(DATE_SUB(dt,INTERVAL 6 HOUR)))
SQLFiddle demo
Here is the code you need:
SELECT
Customer_ID 'Customer ID'
, COUNT(DISTINCT visit) as 'Visits per month'
, MONTH(visit) 'Month'
, YEAR(visit) 'Year'
FROM
(SELECT
*
, CASE
WHEN (t_timestamp > Date_StartDate AND t_timestamp < Date_EndDate)
THEN d_date
WHEN (t_timestamp < Date_StartDate)
THEN date_add(d_date, INTERVAL -1 DAY)
END 'visit'
FROM
(SELECT *
, DATE_ADD(CAST(d_date AS DATETIME), INTERVAL 10 HOUR) Date_StartDate
, DATE_ADD(DATE_ADD(cast(d_date AS DATETIME), INTERVAL 6 HOUR), INTERVAL 1 DAY) Date_EndDate
FROM transactions) Results
) Results
GROUP BY customer_id, month(visit), year(visit)
Also, here is a SQLFiddle with the results of the code.
I haven't used the exact format for your Customer_ID (I've used INTEGER instead of VARCHAR) and didn't use the exact dates you used in your example, but obviously it should work for anything.
Consider adjusting the name of the columns used in my query to the appropriate column names and you should be fine.

MYSQL Query most recent entry

Here is the problem. I need to transform this query to get the right information and Im not sure how.
Database info
Id | Client_Code | Time_Date | Employee_Name | Date_Time | Time_Stamp
1 | 000010 | 2010-11-17 07:45:00| Jerry | 2010-11-17| 07:45 AM
2 | 000022 | 2010-11-17 07:30:00| Jerry | 2010-11-17| 07:30 AM
3 | 000010 | 2010-11-17 16:00:00| Bill | 2010-11-17| 04:00 PM
4 | 000022 | 2010-11-17 16:00:00| Bill | 2010-11-17| 04:00 PM
Here is the query
$sql = "SELECT Client_Code, MAX(TIME(Time_Date)), Employee_Name, Date_Time, Time_Stamp FROM Transaction WHERE Date_Time = CURdate()
AND Time_Stamp != '' GROUP BY Client_Code";
Here is what i get with this query and phpcode
echo " $row[Employee_Name], $row[Client_Code], ".$row['MAX(TIME(Time_Date))']."<br>";
Jerry, 000010, 16:00:00
Bill, 000022, 16:00:00
For some reason yes its giving me the right 16:00:00 Time but it is not giving me the right employee name with that time. It has something to do with the grouping I think but the group has to be by client_code first because I want the most recent entry for each Client_Code. Also I can not use ID for grouping because inputs are not always in order. Here is what is should look like.
Bill, 000010, 16:00:00
Bill, 000022, 16:00:00
Can anyone tell me how to fix this query to get the correct information please. Also once the mysql query is corrected Ill need it to count each employee up and display the results like so.
Bill, 2
Use a self join:
SELECT x.client_code,
TIME(x.time_date)
x.employee_name,
x.date_time,
x.time_stamp
FROM TRANSACTION x
JOIN (SELECT t.client_code,
MAX(t.time_date) AS max_time_date
FROM TRANSACTION t
WHERE t.date_time = CURRENT_DATE
AND t.time_stamp != ''
GROUP BY t.client_code) y ON y.client_code = x.client_code
AND y.max_time_date = x.time_date
WHERE x.date_time = CURRENT_DATE
AND x.time_stamp != ''
You have to add "order by id desc limit 0,1" to your query or you can try to mysql_last_insert_id

Categories