How can I calculate the difference between different times - mysql

hope you are well today.
I need some help in this case, need for each day, for each taxiID, the sum of the hours worked with passengers ( ent_pickup_time, ent_dropoff_time) and without passengers, ( ent_requested time, ent_dropofftime).
For example the taxi 003001 in the day march 3 of 2015 worked 3 hours with a passenger and 3 hours and a half without a passenger.
I have tried a million queries, and no one of them worked so far :(
My last query :
SELECT substring(hex(ent_id), 1, 3) AS fleetId, substring(hex(ent_id), 4, 16) AS taxiId,
(ent_requested_time), (ent_pickup_time), (ent_dropoff_time) , (ent_fare), (ent_distance),
SEC_TO_TIME(SUM(TIME_TO_SEC(ent_dropoff_time) - TIME_TO_SEC(ent_pickup_time))) AS Con_Pasajero,
SEC_TO_TIME(SUM(TIME_TO_SEC(ent_pickup_time) - TIME_TO_SEC(ent_requested_time))) AS Sin_pasajero
FROM tf_entities WHERE DATE(`ent_requested_time`) = '2015-03-03'
GROUP BY 'fleetId', taxiId ASC
order by taxiId ASC
In this Query I have to manually sum the time differences in a day, but I want to automatize the date-hour thing.
My wished result would be something like this, ordered by date and taxiId, for example:
Id|Taxi_Id| DATE |time_wihtout_passenger|time_with_passenger| total_time |
03|003001|2015-3-3| 00:30:00 | 01:02:00 | 01:32:00
ent_fare_total | ent_distance_total |
40,000 | 20,000
The time without passenger would be the difference between ent_requested_time and ent_pickup_time and the time with the passenger would be ent_pickup_time and ent_dropoff_time. Total time would be the SUM of the two of them.

Related

How do I select row with the most recent reduction in a column value from same columns value in previous row?

I have a set of inventory data where the amount increases at a given rate. For example, the inventory increases by ten units every day. However, from time to time there will be an inventory reduction that could be any amount. I need a query that can find me the most recent inventory reduction and return to me the sum of that deduction.
My table holds date and amount for numerous item id's. In theory what I am trying to do is select all amounts and dates for a given item ID, and then find the difference between the most recent reduction between two days inventory. Due to the fact that multiple items are tracked, there is no guarantee that the id column will be consecutive for a set of items.
Researching to find a solution to this has been completely overwhelming. It seems like window functions might be a good route to try, but I have never used them and don't even really have a concept of where to start.
While I could easily return the amounts and do the calculation in PHP, I feel the right thing to do here is harness SQL but my experience with more complex queries is limited.
ID | ItemID | Date | Amount
1 2 2019-05-05 25
7 2 2019-05-06 26
34 2 2019-05-07 14
35 2 2019-05-08 15
67 2 2019-05-09 16
89 2 2019-05-10 5
105 2 2019-05-11 6
Given the data above, it would be nice to see a result like:
item id | date | reduction
2 2019-05-10 11
This is because the most recent inventory reduction is between id 67 and 89 and the amount of the reduction is 11 on May 10th 2019.
In MySQL 8+, you can use lag():
select t.*, (prev_amount - amount) as reduction
from (select t.*,
lag(amount) over (partition by itemid order by date) as prev_amount
from t
) t
where prev_amount > amount
order by date desc
limit 1;

MYSQL How to perform custom month difference between two dates in MYSQL?

My requirement is to compute the total months and then broken months separately between 2 dates (ie first date from table and second date is current date). If broken months total count is > 15 then account it as one month experience and if its les than 15 don't account that as 1 month experience.
Assume I have a date on table as 25/11/2018 and current date is 06/01/2019;
the full month in between is December, so 1 month experience; and broken months are November and January, so now I have to count the dates which is 6 days in Nov and 6 days in Jan, so 12 days and is <= (lte) 15 so total experience will be rounded to 1 month experience
I referred multiple questions related to calculating date difference in MYSQL from stackoverflow, but couldn't find any possible options. The inbuilt functions in MYSQL TIMESTAMPDIFF, TIMEDIFF, PERIOD_DIFF, DATE_DIFF are not giving my required result as their alogrithms are different from my calculation requirement.
Any clue on how to perform this calculation in MYSQL and arrive its result as part of the SQL statement will be helpful to me. Once this value is arrived, in the same SQL, that value will be validated to be within a given value range.
Including sample table structure & value:
table_name = "user"
id | name | join_date
---------------------
1| Sam | 25-11-2017
2| Moe | 03-04-2017
3| Tim | 04-07-2018
4| Sal | 30-01-2017
5| Joe | 13-08-2018
I wanted to find out the users from above table whose experience is calculated in months based on the aforementioned logic. If those months are between either of following ranges, then those users are fetched for further processing.
table_name: "allowed_exp_range"
starting_exp_months | end_exp_months
-------------------------------------
0 | 6
9 | 24
For ex: Sam's experience till date (10-12-2018) based on my calculation is 12+1 month = 13 months. Since 13 is between 9 & 24, Sam's record is one of the expected output.
I think this query will do what you want. It uses
(YEAR(CURDATE())*12+MONTH(CURDATE()))
- (YEAR(STR_TO_DATE(join_date, '%d-%m-%Y'))*12+MONTH(STR_TO_DATE(join_date, '%d-%m-%Y'))) -
- 1
to get the number of whole months of experience for the user,
DAY(LAST_DAY(STR_TO_DATE(join_date, '%d-%m-%Y')))
- DAY(STR_TO_DATE(join_date, '%d-%m-%Y'))
+ 1
to get the number of days in the first month, and
DAY(CURDATE())
to get the number of days in the current month. The two day counts are summed and if the total is > 15, 1 is added to the number of whole months e.g.
SELECT id
, name
, (YEAR(CURDATE())*12+MONTH(CURDATE())) - (YEAR(STR_TO_DATE(join_date, '%d-%m-%Y'))*12+MONTH(STR_TO_DATE(join_date, '%d-%m-%Y'))) - 1 -- whole months
+ CASE WHEN DAY(LAST_DAY(STR_TO_DATE(join_date, '%d-%m-%Y'))) - DAY(STR_TO_DATE(join_date, '%d-%m-%Y')) + 1 + DAY(CURDATE()) > 15 THEN 1 ELSE 0 END -- broken month
AS months
FROM user
We can use this expression as a JOIN condition between user and allowed_exp_range to find all users who have experience within a given range:
SELECT u.id
, u.name
, a.starting_exp_months
, a.end_exp_months
FROM user u
JOIN allowed_exp_range a
ON (YEAR(CURDATE())*12+MONTH(CURDATE())) - (YEAR(STR_TO_DATE(u.join_date, '%d-%m-%Y'))*12+MONTH(STR_TO_DATE(u.join_date, '%d-%m-%Y'))) - 1
+ CASE WHEN DAY(LAST_DAY(STR_TO_DATE(u.join_date, '%d-%m-%Y'))) - DAY(STR_TO_DATE(u.join_date, '%d-%m-%Y')) + 1 + DAY(CURDATE()) > 15 THEN 1 ELSE 0 END
BETWEEN a.starting_exp_months AND a.end_exp_months
Output (for your sample data, includes all users as they all fit into one of the experience ranges):
id name starting_exp_months end_exp_months
1 Sam 9 24
2 Moe 9 24
3 Tim 0 6
4 Sal 9 24
5 Joe 0 6
I've created a small demo on dbfiddle which demonstrates the steps in arriving at the result.

Calculated field in access with a loop

I want to use a calculated field in access, however, the tricky part for me is when I have to run it through the dates. I have a database with multiple registers for the same day, but with different dates. Let's take this one for example:
Date | Report | Onblock Time
-----|--------|-------------
27/5 | 5:45 | 8:52
-----|--------|-------------
27/5 | 9:35 | 10:57
-----|--------|-------------
27/5 | 11:52 | 12:59
So, what I want to do is add 45 minutes to the first time that shows (in this case 5:45) and add 30 minutes to the last one (in this case 12:59). Once those two things are done, I want to calculate the difference between them.
I've tried [(Onblock Time + 0:30) - (Report - 0:45)] in the expression generator, and it seems to work. The problem I have is when I have to make it for a table that has 1000's of registers, with 4-6 a day. Is there any sort of automated loop, like a for each of anything like that?
Thanks in advance,
Jonathan
If I understood you right, you need a query, which returns for each day number of minutes between minimum of ReportTime + 0:45 and maximum of OnblockTime + 0:30. If so, the SQL for query should be like this:
SELECT ReportDate
,DateDiff("n", DateAdd("n", 45, Min([ReportTime])), DateAdd("n", 30, Max([OnblockTime]))) AS Diff
FROM TimeTable
GROUP BY ReportDate;

Optimizing query response time

I have a live, filterable report in my web app which is querying a list of loans and loan payments in MySQL. The goal is to display each loan in a table row and then a list of its loan payments in table columns that each represent a sum of loan payments for that day. We also allow the user to select a date range and aggregation level (daily / weekly / monthly). If the user chose Sept 1-3 with daily aggregation, the results would look like this:
Loan ID | sept 1 | sept 2 | sept 3
---------------------------------------
0001 | $350 | $239.45 | $112
0002 | $100 | $0 | $75
The 2 database tables are Loan and Payment where Payment stores the Loan ID, date, and amount of each payment.
When we run this query on a 60 day range, the result is ~45sec response time. We then tried to create our own pre-aggregated table which was 366 columns per year (Loan ID + daily date columns representing the sum of payments on that day). This increased the response time to > 60sec. That is not even including weekly or monthly aggregation which is even slower.
How can we speed this up? We're ideally looking for 10-15 sec response time, and I have tried every caching / indexing technique I can find without success.
You should discuss with the business what are business requirements or practical application of the table with 60 columns?
The result table looks fine for Sep1-3 example, but for 60 days date range? Who would look at this table? Would it better to group by weeks or months?
If the number of loans is limited

MySQL - Select row with column + X > column

We have a database for patients that shows the details of their various visits to our office, such as their weight during that visit. I want to generate a report that returns the visit (a row from the table) based on the difference between the date of that visit and the patient's first visit being the largest value possible but not exceeding X number of days.
That's confusing, so let me try an example. Let's say I have the following table called patient_visits:
visit_id | created | patient_id | weight
---------+---------------------+------------+-------
1 | 2006-08-08 09:00:05 | 10 | 180
2 | 2006-08-15 09:01:03 | 10 | 178
3 | 2006-08-22 09:05:43 | 10 | 177
4 | 2006-08-29 08:54:38 | 10 | 176
5 | 2006-09-05 08:57:41 | 10 | 174
6 | 2006-09-12 09:02:15 | 10 | 173
In my query, if I were wanting to run this report for "30 days", I would want to return the row where visit_id = 5, because it's 28 days into the future, and the next row is 35 days into the future, which is too much.
I've tried a variety of things, such as joining the table to itself, or creating a subquery in the WHERE clause to try to return the max value of created WHERE it is equal to or less than created + 30 days, but I seem to be at a loss at this point. As a last resort, I can just pull all of the data into a PHP array and build some logic there, but I'd really rather not.
The bigger picture is this: The database has about 5,000 patients, each with any number of office visits. I want to build the report to tell me what the average wait loss has been for all patients combined when going from their first visit to X days out (that is, X days from each individual patient's first visit, not an arbitrary X-day period). I'm hoping that if I can get the above resolved, I'll be able to work the rest out.
You can get the date of the first and next visit using query like this (Note that this doesn't has correct syntax for date comparing and it is just an schema of the query):
select
first_visits.patient_id,
first_visits.date first_date,
max(next_visit.created) next_date
from (
select patient_id, min(created) as "date"
from patient_visits
group by patient_id
) as first_visits
inner join patient_visits next_visit
on (next_visit.patient_id = first_visits.patient_id
and next_visit.created between first_visits.created and first_visits.created + 30 days)
group by first_visits.patient_id, first_visits.date
So basically you need to find start date using grouping by patient_id and then join patient_visits and find max date that is within the 30 days window.
Then you can join the result to patient_visits to get start and end weights and calculate the loss.