MySQL Min Max deduction - mysql

I have a simple table that used to store fuel issuing details for different vehicles as follows:
+----+------------+-----+---------------+-------------+
| id | vehicle_no | qty | meter_reading | date_issued |
+----+------------+-----+---------------+-------------+
| 1 | 366 | 50 | 10500 | 2019-09-01 |
| 2 | 366 | 50 | 11020 | 2019-09-02 |
| 3 | 367 | 25 | 25000 | 2019-09-03 |
| 4 | 366 | 50 | 11450 | 2019-09-04 |
| 5 | 368 | 50 | 6000 | 2019-09-05 |
+----+------------+-----+---------------+-------------+
02) Then I need to find no of Kilometers run against issued sum of fuel quantities.
03) I used the following query
select f1.vehicle_no, (select f1.meter_reading-f2.meter_reading)/sum(qty) from fuel f1) from fuel f2 group by vehicle_no
04) I want to get the desired output as follows :
As an example :
the meter reading of id=4 - meter reading of id=2 is 430 Kilometers
the meter reading of id=4 - meter reading of id=1 is 950 Kilometers
the meter reading of id=2 - meter reading of id=1 is 520 Kilometers
But I did not get the expected result. Can anyone help me ?

With a self join:
select
f.id,
ff.id,
f.vehicle_no,
f.date_issued,
ff.date_issued,
f.meter_reading - ff.meter_reading as dif
from fuel f inner join fuel ff
on ff.vehicle_no = f.vehicle_no and ff.date_issued < f.date_issued
See the demo.
Results:
> id | id | vehicle_no | date_issued | date_issued | dif
> -: | -: | ---------: | :------------------ | :------------------ | --:
> 2 | 1 | 366 | 2019-09-02 00:00:00 | 2019-09-01 00:00:00 | 520
> 4 | 1 | 366 | 2019-09-04 00:00:00 | 2019-09-01 00:00:00 | 950
> 4 | 2 | 366 | 2019-09-04 00:00:00 | 2019-09-02 00:00:00 | 430

Related

Getting sets of data from MySQL table with OFFSET and LIMIT when one set of data can come from 2 or more rows

Let's suppose I have a MySQL table 'orders' with the following data:
| id | order_no | item_id | amount | datetime |
| 1 | 123 | 901 | 1 | 2020-08-05 00:00:01 |
| 2 | 324 | 902 | 2 | 2020-08-06 00:00:01 |
| 3 | 324 | 905 | 1 | 2020-08-06 00:00:01 |
| 4 | 511 | 902 | 1 | 2020-08-07 00:00:01 |
| 5 | 400 | 904 | 3 | 2020-08-08 00:00:01 |
| 6 | 195 | 903 | 1 | 2020-08-09 00:00:01 |
| 7 | 195 | 905 | 2 | 2020-08-09 00:00:01 |
| 8 | 250 | 908 | 1 | 2020-08-10 00:00:01 |
| 9 | 222 | 901 | 3 | 2020-08-11 00:00:01 |
| 10 | 315 | 903 | 1 | 2020-08-12 00:00:01 |
| 11 | 315 | 905 | 2 | 2020-08-12 00:00:01 |
| 12 | 198 | 903 | 1 | 2020-08-13 00:00:01 |
| 13 | 651 | 902 | 2 | 2020-08-14 00:00:01 |
| 14 | 651 | 907 | 2 | 2020-08-14 00:00:01 |
| 15 | 405 | 902 | 1 | 2020-08-15 00:00:01 |
| 16 | 112 | 905 | 1 | 2020-08-16 00:00:01 |
and in my website I want to display the orders according to user's settings like: orders per page/ page number. The data need to be ordered by 'datetime' in ascending order, so if the page number is 2 with orders per page = 5, I would need data of id-s 8-14 (as rows of id 1-7 make the first 5 orders and 8-14: the second one). Note that some orders (in bold) have 2 rows (and can have more) with the same order_no but different item_id.
The simple LIMIT and OFFSET clauses are of no use here unless I combine them with some subqueries but so far I have not found the solution.
I have come to a solution that I think will work best for me: one table 'orders' with all the order 'header' data + first item of the order (each piece of item data in a separate column, like item_id, amount etc.) then, another column: 'items' of type JSON to store 2nd and further items if there are 2 or more; this way I'll be able to use LIMIT and OFFSET and will need only one query especially for inserting a new order, which had worried me the most because with 2 tables I would have to use a transaction.
A select query, in most cases will be simple, only with 2 or more items per order I will need to handle the items from the JSON column, it will not harm performance at all with, as I mentioned in a comment, most of orders containing only one item.
Thank you #Shadow for your comments, they have really helped me to find the solution as I think I'd been going in a wrong direction.

How to get the opposite of a join?

I am trying to get the rows that don't exist in one table where one table called schedules (match_week, player_home_id, player_away_id) and the other table called match (match_week, Winner_id, Defeated_id) are joined. The players look at their schedule and play a match. I am trying to get a list of the scheduled matches that do not exist in the match table. The IDs in the match table can be in either column Winner_id or Defeated_id.
I have reviewed a number of Stack Exchange examples, but most use "IS NULL" and I don't have null values. I have used a Join that does give the output of the matches played. I would like the matches that have not been played.
CSV - wp_schedule_test
+----+------------+--------------+--------------+-----------------+-----------------+
| ID | match_week | home_player1 | away_player1 | player1_home_id | player1_away_id |
+----+------------+--------------+--------------+-----------------+-----------------+
| 1 | WEEK 1 | James Rives | Dale Hemme | 164 | 169 |
| 2 | WEEK 1 | John Head | David Foster | 81 | 175 |
| 3 | WEEK 1 | John Dalton | Eric Simmons | 82 | 23 |
| 4 | WEEK 2 | John Head | James Rives | 81 | 164 |
| 5 | WEEK 2 | Dale Hemme | John Dalton | 169 | 82 |
| 6 | WEEK 2 | David Foster | Eric Simmons | 175 | 23 |
| 7 | WEEK 3 | John Dalton | James Rives | 82 | 164 |
| 8 | WEEK 3 | John Head | Eric Simmons | 81 | 23 |
| 9 | WEEK 3 | Dale Hemme | David Foster | 169 | 175 |
| 10 | WEEK 4 | Eric Simmons | James Rives | 23 | 164 |
| 11 | WEEK 4 | David Foster | John Dalton | 175 | 82 |
| 12 | WEEK 4 | Dale Hemme | John Head | 169 | 81 |
+----+------------+--------------+--------------+-----------------+-----------------+
CSV - wp_match_scores_test
+----+------------+------------+------------+
| ID | match_week | player1_id | player2_id |
+----+------------+------------+------------+
| 5 | WEEK 1 | 82 | 23 |
| 20 | WEEK 1 | 164 | 169 |
| 21 | WEEK 2 | 164 | 81 |
| 25 | WEEK 2 | 82 | 169 |
| 61 | WEEK 3 | 175 | 169 |
| 62 | WEEK 4 | 175 | 82 |
| 69 | WEEK 2 | 175 | 23 |
| 85 | WEEK 3 | 164 | 82 |
| 86 | WEEK 4 | 164 | 23 |
+----+------------+------------+------------+
The output from the mysql query are the matches that have been played. I am trying to figure out how to list the matches that have not been played from the table Schedule.
CSV - MySQL Output
+------------+------------+------------+
| match_week | player1_id | player2_id |
+------------+------------+------------+
| WEEK 1 | 164 | 169 |
| WEEK 1 | 82 | 23 |
| WEEK 2 | 164 | 81 |
| WEEK 2 | 82 | 169 |
| WEEK 2 | 175 | 23 |
| WEEK 3 | 175 | 169 |
| WEEK 3 | 164 | 82 |
| WEEK 4 | 175 | 82 |
| WEEK 4 | 164 | 23 |
+------------+------------+------------+
MYSQL
select DISTINCT ms.match_week, ms.player1_id , ms.player2_id FROM
wp_match_scores_test ms
JOIN wp_schedules_test s
ON (s.player1_home_id = ms.player1_id or s.player1_away_id =
ms.player2_id)
Order by ms.match_week
The expected output is:
CSV - Desired Output
+------------+----------------+----------------+
| match_week | player_home_id | player_away_id |
+------------+----------------+----------------+
| WEEK 1 | 81 | 175 |
| WEEK 3 | 81 | 23 |
| WEEK 4 | 169 | 81 |
+------------+----------------+----------------+
The added code I would like to use is
SELECT s.*
FROM wp_schedules_test s
WHERE NOT EXISTS
(select DISTINCT ms.match_week, ms.player1_id , ms.player2_id FROM
wp_match_scores_test ms
JOIN wp_schedules_test s
ON (s.player1_home_id = ms.player1_id or s.player1_away_id =
ms.player2_id)
Order by ms.match_week)
Unfortunately, the output yields "No Rows"
You can use a LEFT JOIN to achieve the desired results, joining the two tables on matching player ids (noting that player id values in wp_match_scores_test can correspond to either player1_home_id or player1_away_id in wp_schedules_test). If there is no match, the result table will have NULL values from the wp_match_scores_test table values, and you can use that to select the matches which have not been played:
SELECT sch.*
FROM wp_schedule_test sch
LEFT JOIN wp_match_scores_test ms
ON (ms.player1_id = sch.player1_home_id
OR ms.player2_id = sch.player1_home_id)
AND (ms.player1_id = sch.player1_away_id
OR ms.player2_id = sch.player1_away_id)
WHERE ms.ID IS NULL
Output:
ID match_week home_player1 away_player1 player1_home_id player1_away_id
2 Week 1 John Head David Foster 81 175
8 Week 3 John Head Eric Simmons 81 23
12 Week 4 Dale Hemme John Head 169 81
Note that you can also use a NOT EXISTS query, using the same condition as I used in the JOIN:
SELECT sch.*
FROM wp_schedule_test sch
WHERE NOT EXISTS (SELECT *
FROM wp_match_scores_test ms
WHERE (ms.player1_id = sch.player1_home_id
OR ms.player2_id = sch.player1_home_id)
AND (ms.player1_id = sch.player1_away_id
OR ms.player2_id = sch.player1_away_id))
The output of this query is the same. Note though that conditions in the WHERE clause have to be evaluated for every row in the result set and that will generally make this query less efficient than the LEFT JOIN equivalent.
Demo on dbfiddle

MySql Average and Group By Usage

Report Table
+----+-------------------+--------+---------------------+
| id | mac_address |quantity| zaman |
+----+-------------------+--------+---------------------+
| 12 | 26-C0-B7-1E-7A-8C | 001 | 2017-12-07 10:22:09 |
| 14 | 26-C0-B7-1E-7A-8C | 001 | 2017-12-07 10:28:44 |
| 15 | 26-C0-B7-1E-7A-8D | 001 | 2017-12-07 10:44:54 |
| 19 | 26-C0-B7-1E-7A-AC | 001 | 2017-12-08 10:11:00 |
| 20 | 26-C0-B7-1E-7A-AD | 002 | 2017-12-08 10:32:12 |
| 24 | 26-C0-B7-1E-7A-8D | 001 | 2017-12-09 10:29:54 |
| 25 | 26-C0-B7-1E-7A-8E | 002 | 2017-12-09 10:39:11 |
I want to find average of quantity between days according to this equation:
Sum of quantity(9)/Number of day(3) = 3
So far I have this query:
SELECT ROUND(AVG(quantity),2) AS quantity, DATE(zaman) as DateOnly
FROM report WHERE DATE(zaman) BETWEEN ? AND ?
GROUP BY DateOnly
How can I achieve this?
try using sum of the quantity divided by number of days
select sum(quantity) /( datediff(max(zaman), min(zaman))) as quantity
FROM report WHERE DATE(zaman) BETWEEN ? AND ?
Try this:
SELECT ROUND((SUM(Quantity)*1.0)/datediff(MAX(Zaman), MIN(Zaman)),0)
FROM report
WHERE zaman BETWEEN ? AND ?

Return multi columns result from single table with zero values

I have a single table like :
mysql> select RefID,State,StartTime,EndTime from execReports limit 5;
+--------------------------------------+-----------+---------------------+---------------------+
| RefID | State | StartTime | EndTime |
+--------------------------------------+-----------+---------------------+---------------------+
| 00019a52-8480-4431-9ad2-3767c3933627 | Completed | 2016-04-18 13:45:00 | 2016-04-18 13:45:01 |
| 00038a8a-995e-4cb2-a335-cb05d5b3e92d | Aborted | 2016-05-03 04:00:00 | 2016-05-03 04:00:02 |
| 001013f8-0b86-456f-bd59-a7ef066e565f | Completed | 2016-04-14 03:30:00 | 2016-04-14 03:30:11 |
| 001f8d23-3022-4271-bba0-200494de678a | Failed | 2016-04-30 05:00:00 | 2016-04-30 05:00:02 |
| 0027ba42-1c37-4e50-a7d6-a4e24056e080 | Completed | 2016-04-18 03:45:00 | 2016-04-18 03:45:02 |
+--------------------------------------+-----------+---------------------+---------------------+
I can extract the count of exec for each state with :
mysql> select distinct State,count(StartTime) as nbExec from execReports group by State;
+-----------+--------+
| State | nbExec |
+-----------+--------+
| Aborted | 3 |
| Completed | 14148 |
| Failed | 49 |
+-----------+--------+
4 rows in set (0.02 sec)
I can extract the count of exec for each week with :
mysql> select distinct extract(week from StartTime) as Week, count(StartTime) as nbExec from execReports group by Week;
+------+--------+
| Week | nbExec |
+------+--------+
| 14 | 1317 |
| 15 | 3051 |
| 16 | 3066 |
| 17 | 3059 |
| 18 | 3059 |
| 19 | 652 |
+------+--------+
6 rows in set (0.01 sec)
But I would like to extract a crossing table like :
+------+---------+-----------+--------+---------+---------+
| Week | nbExec | Completed | Failed | Running | Aborted |
+------+---------+-----------+--------+---------+---------+
| 14 | 1317 | 1312 | 3 | 1 | 1 |
| 15 | 3051 | 3050 | 1 | 0 | 0 |
| 16 | 3066 | 3060 | 3 | 2 | 1 |
| 17 | 3059 | 3058 | 0 | 1 | 0 |
| 18 | 3059 | 3057 | 1 | 0 | 1 |
| 19 | 652 | 652 | 0 | 0 | 0 |
+------+---------+-----------+--------+---------+---------+
I'm stuck on this for a few days. Any help appreciated.
Best regards
select extract(week from StartTime) as Week, count(StartTime) as nbExec,
sum(if(state="Completed",1,0)) Completed,
sum(if(state="Failed",1,0)) Failed,
sum(if(state="Aborted",1,0)) Aborted
from execReports group by Week;
demo
You can join multi tables for this. If you want for dynamic row to column, check this: MySQL pivot row into dynamic number of columns
SELECT
a.week,
count(a.StartTime) as nbExec,
count(b1.StartTime) as Completed,
count(b2.StartTime) as Failed,
count(b3.StartTime) as Running,
count(b4.StartTime) as Aborted,
FROM execReports a
LEFT JOIN execReports b1 ON a.refID = b1.refID and b1.state ='Completed'
LEFT JOIN execReports b2 ON a.refID = b2.refID and b2.state ='Failed'
LEFT JOIN execReports b3 ON a.refID = b3.refID and b3.state ='Running'
LEFT JOIN execReports b4 ON a.refID = b4.refID and b4.state ='Aborted'
GROUP BY 1

Displaying records from database by quarter in mysql

I have a table named nca_totals.
Table: nca_totals
+----------+-----------+------------+--------------+
| total_id | nca_total | nca_date | account_type |
+----------+-----------+------------+--------------+
| 13 | 10450 | 2015-01-21 | DBP-TRUST |
| 14 | 5000 | 2015-02-05 | DBP-TRUST |
| 15 | 7000 | 2015-04-02 | DBP-TRUST |
| 16 | 4000 | 2015-05-02 | DBP-TRUST |
+----------+-----------+------------+--------------+
Now I want to display all the data by quarter base on its date. Let's say I want to display all the records who belong to 1st Quarter like this:
+----------+-----------+------------+--------------+
| total_id | nca_total | nca_date | account_type |
+----------+-----------+------------+--------------+
| 13 | 10450 | 2015-01-21 | DBP-TRUST |
| 14 | 5000 | 2015-02-05 | DBP-TRUST |
+----------+-----------+------------+--------------+
This date belongs to the 1st quarter of the year (Jan, Feb, March). I only have this query to select the date and return its quarter number as:
SELECT QUARTER('2015-01-11'); /* returns 1 */
How can I combine that query to display all the records by quarter ? Can anyone help ? Thanks.
select *
from nca_totals
where QUARTER(nca_date) = 1
SELECT
CEIL(MONTH(`nca_date`) / 3) AS `quarter`
FROM `nca_totals`;