Calculate total in mysql - mysql

I want to add total sum of former broken and former crack for line 1 - 20 in December 2021. but now I only know this code
SELECT `line`, SUM(`FormerBroken`), SUM(`FormerCrack`) FROM `line_check` WHERE `Month` = '2021-12' AND `Line` = '1'
So is there any way that i can add line='2' , line='3',line='4'
my table for reference
Date
Line
Former Broken
Former Crack
1/12
1
3
2
2/12
2
5
4
3/12
3
7
6
4/12
4
9
8
5/12
5
10
10
6/12
1
3
2
7/12
2
5
4
8/12
3
7
6
9/12
4
9
8

You can use IN(...).
Example:
SELECT * FROM table WHERE someValue IN (1, 2, 3, 4);
Tailored to your existing query:
SELECT `line`, SUM(`FormerBroken`), SUM(`FormerCrack`) FROM `line_check` WHERE `Month` = '2021-12' AND `Line` IN ('1', '2', '3', '4');

Related

Get the top value row of a table without id

So i have this table:
req_num
number
status
order
1254
5
7
1
1254
4
7
2
1254
6
7
3
1246
7
8
1
1246
5
8
2
1246
3
8
3
1253
1
9
1
1253
4
7
2
1253
7
4
3
1321
7
4
1
1321
8
4
2
1321
7
4
3
1321
8
4
4
and i need to know if theres a way to generate a query that get a column like this based on the highest value in the "order" column.
req_num
number
status
order
last_req
1254
5
7
1
not_last
1254
4
7
2
not_last
1254
6
7
3
last
1246
7
8
1
not_last
1246
5
8
2
not_last
1246
3
8
3
last
1253
1
9
1
not_last
1253
4
7
2
not_last
1253
7
4
3
last
1321
7
4
1
not_last
1321
8
4
2
not_last
1321
7
4
3
not_last
1321
8
4
4
last
this table doesnt have any ids and i can't do inserts on it, any ideas?
You can use window functions to accomplish this - you can partition your data by req_num and then take the item in each partition with the highest order.
The docs: https://dev.mysql.com/doc/refman/8.0/en/window-functions-frames.html
The result will be something like (untested):
SELECT rec_num, number, status, LAST_VAL(order) OVER w AS 'last',
FROM my_table
WINDOW w AS (PARTITION BY req_num ORDER BY order);
That won't give you exactly what you want as all the rows will now include the highest order num for that rec_num. But you can then easily compare the order and last columns to create your last and not_last identifiers.
SELECT rec_num, number, status, LAST_VAL(order) OVER w AS 'last',
IF(`order` = `last`, 'last', 'not_last') AS last_req
FROM my_table
WINDOW w AS (PARTITION BY req_num ORDER BY order);
The window functions are remarkably powerful for situations like this.
In order to solve this problem, you need to look for the maximum value in the order field, grouped by req_num.
If your version is MySQL 8.0, you can use the ROW_NUMBER window function to assign an ordinal value to that column in a descendent way, so that your max value has ranking = 1. Afterwards you check with an IF statement if your column has the value 1 or not, and replace with 'last' and 'not_last' accordingly.
SELECT *,
IF(ROW_NUMBER() OVER(PARTITION BY req_num_ ORDER BY order_ DESC) = 1,
'last',
'not_last') AS last_req
FROM tab
Demo here.
If your version is MySQL 5.X, you can first use the MAX aggregation function in a subquery containing only req_num and order, by grouping on the former field and aggregating on the latter one. Then you can LEFT JOIN the original table with this result set, and the order values who will have assigned NULL will be the ones that are 'not_last'.
SELECT tab.req_num,
tab.status,
tab.order,
IF(max_values.max_order, 'last', 'not_last') AS last_req
FROM tab
LEFT JOIN (SELECT req_num_,
MAX(order) AS max_order
FROM tab
GROUP BY req_num ) max_values
ON tab.req_num = max_values.req_num
AND tab.order = max_values.max_order
Demo here.

How to get the average number of cities crossed [duplicate]

This question already has an answer here:
How to write these complex MySQL queries?
(1 answer)
Closed 1 year ago.
I am a beginner at databases. I need to write some SQL queries.
The tables are:
Expedition(id, number, id_captain, id_ship, id_heros)
City(id, name)
Heros(id, family_name, first_name)
Step(id, index, id_expedition, id_city)
sample data :
'Table expedition'
id
number
id_captain
id_ship
id_hero
1
1
1
10
8
2
2
2
1
5
3
3
1
8
3
4
4
10
9
6
5
5
5
7
4
6
6
6
5
4
7
7
7
3
7
8
8
8
2
8
9
9
9
1
3
10
10
1
4
2
11
11
6
3
1
12
12
8
6
1
13
13
5
8
6
14
14
4
9
9
15
15
3
10
4
16
16
10
2
2
17
17
9
3
3
18
18
8
7
7
19
19
9
8
10
20
20
7
2
2
table 'heros'
id
family_name
first_name
1
familyname1
firstname1
2
familyname2
firstname2
3
familyname3
firstname3
4
familyname4
firstname4
5
familyname5
firstname5
6
familyname6
firstname6
7
familyname7
firstname7
8
familyname8
firstname8
9
familyname9
firstname9
10
familyname10
firstname10
query1: The family (based on the family name) with the least travelling (the fewest cities different crossings).
i have done this for the first query:
select expedition.id, id_hero, heros.family_name as Famille_expedition, count(distinct id_city) as city_count
from expedition, step, heros
where expedition.id=step.id and expedition.id_hero=heros.id
group by id_hero
having city_count =
(select count(distinct id_city) as min_city_count
from expedition, step
where expedition.id=step.id
group by id_hero
order by min_city_count asc
limit 1);
query2: The average of cities crossed by an expedition
I have no idea how to answer the second one.
Well, first ask yourself what information do you need to answer your question?
From your question, I'd say the average number of crossings is just the sum of all entries in the steps table, divided by the number of expeditions, since in each step, one city is visited and the average of all visits is what you are looking for:
SELECT (
(SELECT COUNT(s.id_city)
FROM step AS s) /
(SELECT COUNT(e.id)
FROM expedition AS e) ) AS total_average__cities
That being said, it depends on how exactly you define number of cities and crossing. Imagine the following example data for the table step:
id
idx
id_expedition
id_city
1
1
1
1
2
2
1
5
3
3
1
3
4
1
2
5
5
2
2
9
6
1
3
8
7
2
3
5
8
3
3
9
9
4
3
5
10
5
3
8
The table lists the steps for three expeditions. Expedition 1 goes from one city via another to a third. Expedition 2 goes directly from one city to another. And expedition 3 goes through several cities and visits one city twice along the way and also returns to the city that it started in.
The average number of cities over all these steps is (3 + 2 + 5 [cities in all steps]) / 3 [expeditions] = 3.3333. That is the result of the above query.
Now, if you define number of cities as meaning unique cities for each expedition, expedition 3 only visits 3 cities instead of 5. Then your average calculates as (3 + 2 + 3 [unique cities/expedition in all steps]) / 3 [expeditions] = 2.6666. The according query needs to count the distinct cities within each expedition before building the average:
SELECT (
(SELECT SUM(cnt) FROM (SELECT COUNT(DISTINCT s.id_city) AS cnt
FROM step AS s
GROUP BY s.id_expedition) t) /
(SELECT COUNT(e.id)
FROM expedition AS e) ) AS total_average__cities
Now, if you define crossing as only covering cities along the way, expedition 1 only crosses 1 city and expedition 2 crosses none at all.
Then your query also needs to look differently. You need to filter the all cities to exclude the first and the last for each expedition. The subquery could look like this:
SELECT s.* FROM step s
JOIN ( SELECT id_expedition,
MAX(idx) AS max_idx,
MIN(idx) AS min_idx
FROM step s
GROUP BY id_expedition) minmax
ON s.id_expedition = minmax.id_expedition
AND s.idx > minmax.min_idx
AND s.idx < minmax.max_idx
So for the case that you want the number of cities crossed excluding start and stop, your average would be computed as (1 + 0 + 3 [intermediate cities in all steps]) / 3 [expeditions] = 1.3333. The according query would be
SELECT (
(SELECT COUNT(s.id_city)
FROM step s
JOIN ( SELECT id_expedition,
MAX(idx) as max_idx,
MIN(idx) as min_idx
FROM step s
GROUP BY id_expedition) minmax
ON s.id_expedition = minmax.id_expedition
AND s.idx > minmax.min_idx
AND s.idx < minmax.max_idx) /
(SELECT COUNT(e.id)
FROM expedition AS e) ) AS total_average__cities
Finally, in case you want to both exclude start and stop and only want to count unique cities, your average would be computed as (1 + 0 + 2 [unique intermediate cities in all steps]) / 3 [expeditions] = 1. The following query combines the two approaches from above:
SELECT (
(SELECT SUM(cnt) FROM (SELECT COUNT(DISTINCT id_city) AS cnt
FROM step s
JOIN ( SELECT id_expedition,
MAX(idx) AS max_idx,
MIN(idx) AS min_idx
FROM step s
GROUP BY id_expedition) minmax
ON s.id_expedition = minmax.id_expedition
AND s.idx > minmax.min_idx
AND s.idx < minmax.max_idx
GROUP BY s.id_expedition) t) /
(SELECT COUNT(e.id)
FROM expedition AS e) ) AS total_average_cities
You can test all these queries in this db<>fiddle.

Group by last occurrence

i am trying to get the last rows where rec_p_id = 4 SORTED by the timestamp. Since i do not want to have all the results WHERE rec_p_id = 4, i am using GROUP BY to group it by send_p_id.
My SQL query looks like this:
SELECT *
FROM
( SELECT *
FROM chat
WHERE rec_p_id= "4"
ORDER
BY timestamp DESC) as sub
GROUP
BY send_p_id
My table looks like this:
Table chat
c_id
send_p_id
rec_p_id
timestamp
1
3
4
2020-05-01 14:46:00
2
3
4
2020-05-01 14:49:00
3
3
4
2020-05-01 14:50:00
4
7
4
2020-05-01 12:00:00
5
4
7
2020-05-01 12:10:00
6
7
4
2020-05-01 12:20:00
7
9
4
2020-05-01 16:50:00
8
9
4
2020-05-01 17:00:00
I want to get the last occurrences:
c_id
send_p_id
rec_p_id
timestamp
3
3
4
2020-05-01 14:50:00
6
7
4
2020-05-01 12:20:00
8
9
4
2020-05-01 17:00:00
But instead i get all the first ones:
c_id
send_p_id
rec_p_id
timestamp
1
3
4
2020-05-01 14:46:00
4
7
4
2020-05-01 12:00:00
7
9
4
2020-05-01 16:50:00
I saw the query i am using in this question: ORDER BY date and time BEFORE GROUP BY name in mysql
it seems to work for all of them. What am i doing wrong with my query?
Thanks in advance.
Looking to your expected result seems you are looking for
select max(c_id) c_id, send_p_id, min(timestamp) timestamp
from chat WHERE rec_p_id= "4"
group by send_p_id
ORDER BY c_id
Group by is for aggregated result ..
an use without aggregation function can produce unpredicatble result and in version > 5.6 can produce error
I used this answer and built this setup for you.
The code below is the copy of it, so that you can run it later yourself.
For the solution the example from the official manual.
CREATE TABLE chat
(
c_id INT PRIMARY KEY
, send_p_id INT
, rec_p_id INT
, timestamp DATETIME
);
INSERT INTO chat VALUES
(1, 3, 4, '2020-05-01 14:46:00')
, (2, 3, 4, '2020-05-01 14:49:00')
, (3, 3, 4, '2020-05-01 14:50:00')
, (4, 7, 4, '2020-05-01 12:00:00')
, (5, 4, 7, '2020-05-01 12:10:00')
, (6, 7, 4, '2020-05-01 12:20:00')
, (7, 9, 4, '2020-05-01 16:50:00')
, (8, 9, 4, '2020-05-01 17:00:00');
Solution:
SELECT c_id,
send_p_id,
rec_p_id,
timestamp
FROM chat AS c
WHERE timestamp=(SELECT MAX(c1.timestamp)
FROM chat AS c1
WHERE c.send_p_id = c1.send_p_id)
AND send_p_id != 4
ORDER BY timestamp;

group by month returns only April for two tables

Currently I am honestly at loss what I am doing wrong. It is a rather simple query I think.
Tables:
operations:
id processedon clientid
1 2018-01-01 9
2 2018-03-16 9
3 2018-04-21 9
4 2018-04-20 9
5 2018-05-09 9
items:
id operation_id quantity unitprice
1 1 10 2
2 1 5 3
3 2 20 4
4 3 10 2
5 4 8 4
6 4 10 4
7 5 2 2
The expected result of the operation/query is:
month total_value
1 35
3 80
4 92
5 4
That is quantity * unitprice based. For some reason, it only returns month=4
SELECT
month(`operations`.`processedon`) AS `month`,
SUM((`items`.`quantity` * `items`.`unitprice`)) AS `total_value`
FROM `items`
INNER JOIN `operations` ON (`items`.`operation_id` = `operations`.`id`)
GROUP BY 'month'
ORDER BY 'month'
According to the info provided the join should be
INNER JOIN operations ON items.operation_id = operations.id
Eg
SELECT
month(`operations`.`processedon`) AS `month`,
SUM((`items`.`quantity` * `items`.`unitprice`)) AS `total_value`
FROM `items`
INNER JOIN `operations` ON `items`.`operation_id` = `operations`.`id`
GROUP BY month(`operations`.`processedon`)
ORDER BY `month`
There is no efficiency gain by using a column alias in the group by clause, I prefer to avoid using them except perhaps in the order by clause.
The following query will give you the required answer
SELECT
month(`operations`.`processedon`) AS `month`,
SUM((`items`.`quantity` * `items`.`unitprice`)) AS `total_value`
FROM items
INNER JOIN operations ON (items.operation_id = operations.id)
GROUP BY month(operations.processedon)
ORDER BY month(operations.processedon)
You need to specify month correctly since it is not an existing column.
You'll get the following result
month total_value
1 35
3 80
4 92
5 4

Get the column order of a query

I have a table with two columns [id, value] both numeric.
In this example:
[ id, value ]
[ 1, 6 ]
[ 2, 4 ]
[ 3, 10 ]
[ 4, 2 ]
[ 5, 7 ]
[ 6, 3 ]
For a given id I'd like to retrieve the top 3 id's (those with highest value), their top position and if the given id is not in the top 3, also get its position, id and value:
Example 1: ask_id = 5 Return:
[ position, id, value ]
[ 1, 3, 10 ]
[ 2, 5, 7 ]
[ 3, 1, 6 ]
Example 2: ask_id = 4. Return:
[ position, id, value ]
[ 1, 3, 10 ]
[ 2, 5, 7 ]
[ 3, 1, 6 ]
[ 6, 4, 2 ]
So the important points are:
How to get for the position column?
How to get the additional row if possible (anyway there's no problem if I need two queries)?
select t2.pos, t1.id, t1.value
from test as t1
inner join
(select id, value, #pos:=if(#pos is null, 0, #pos)+1 as pos
from test order by value desc) as t2
on t1.id=t2.id
where t2.pos<=3 or t2.id={$ask_id}
order by t2.pos;
Basically, the idea is like this:
Rank the rows by value.
Retrieve rows where at least one of the following is true:
position BETWEEN 1 AND 3
id = #given_id
These posts give examples of how you could substitute ranking functions (at least the most fundamental of them, ROW_NUMBER()) in MySQL:
ROW_NUMBER() in MySQL
MSSQL Row_Number() over(order by) in MySql
This method should be used with caution, though, as this article explains.
That said, one possible implementation of the above steps might look like this:
SET #pos = 0;
SELECT
position,
id,
value
FROM (
SELECT
id,
value,
#pos := #pos + 1 AS position
FROM atable
ORDER BY value DESC
) s
WHERE position BETWEEN 1 AND 3
OR id = #given_id
ORDER BY position
Tested in MySQL
to retrieve the top 3 id's (those with highest value) with position in ascending order.
set #num = 0;
SELECT #num := #num + 1 as position_sequence,id,value FROM tablename
ORDER BY value desc
limit 3;
I've not (yet) tested the selected answer in MySQL on the interesting cases where there are ties in the top three places, but I have tested this code in Informix on those cases, and it produces the answer I think should be produced.
Assuming that the table is called leader_board:
CREATE TABLE leader_board(id INTEGER NOT NULL PRIMARY KEY, value INTEGER NOT NULL);
INSERT INTO leader_board(id, value) VALUES(1, 6);
INSERT INTO leader_board(id, value) VALUES(2, 4);
INSERT INTO leader_board(id, value) VALUES(3, 10);
INSERT INTO leader_board(id, value) VALUES(4, 2);
INSERT INTO leader_board(id, value) VALUES(5, 7);
INSERT INTO leader_board(id, value) VALUES(6, 3);
This query works on the data shown, assuming that the special ID is 4:
SELECT b.position - c.tied + 1 AS standing, a.id, a.value
FROM leader_board AS a
JOIN (SELECT COUNT(*) AS position, d.id
FROM leader_board AS d
JOIN leader_board AS e ON (d.value <= e.value)
GROUP BY d.id
) AS b
ON a.id = b.id
JOIN (SELECT COUNT(*) AS tied, f.id
FROM leader_board AS f
JOIN leader_board AS g ON (f.value = g.value)
GROUP BY f.id
) AS c
ON a.id = c.id
WHERE (a.id = 4 OR (b.position - c.tied + 1) <= 3) -- Special ID = 4; Top N = 3
ORDER BY position, a.id;
Output on original data:
standing id value
1 3 10
2 5 7
3 1 6
6 4 2
Explanation
The two sub-queries are closely related, but they produce different answers. At one time, I used two temporary tables to hold those results. In particular, the first sub-query (AS b) produces a position, but when there are ties, the position is the lowest rather than the highest of the tied positions. That is, given:
ID Value
1 10
2 7
3 7
4 7
The outputs will be:
Position ID
1 1
4 2
4 3
4 4
However, we would like to count them as:
Position ID
1 1
2 2
2 3
2 4
So, the corrected position is the original position minus the number of tied values (3 for ID ∈ { 2, 3, 4 }, 1 for ID 1) plus 1. The second sub-query returns the number of tied values for each ID. There might be a neater way to do that calculation, but I'm not sure what it is at the moment.
Special cases
However, the code should demonstrate that it handles the cases where:
There are 2 or more ID values with the same top value.
There are 2 or more ID values with the same second highest top score (but the top one is unique).
There are 2 or more ID values with the same third highest top score (but the top two are unique).
To save rewriting the query each time, I converted it into an Informix-style stored procedure which take both the Special ID and the Top N (defaulting to 3) values that should be displayed and made them into parameters of the procedure. (Yes, the notation in the RETURNING clause is weird.)
CREATE PROCEDURE leader_board_standings(extra_id INTEGER, top_n INTEGER DEFAULT 3)
RETURNING INTEGER AS standing, INTEGER AS id, INTEGER AS value;
DEFINE standing, id, value INTEGER;
FOREACH SELECT b.position - c.tied + 1 AS standing, a.id, a.value
INTO standing, id, value
FROM leader_board AS a
JOIN (SELECT COUNT(*) AS position, d.id
FROM leader_board AS d
JOIN leader_board AS e ON (d.value <= e.value)
GROUP BY d.id
) AS b
ON a.id = b.id
JOIN (SELECT COUNT(*) AS tied, f.id
FROM leader_board AS f
JOIN leader_board AS g ON (f.value = g.value)
GROUP BY f.id
) AS c
ON a.id = c.id
WHERE (a.id = extra_id OR (b.position - c.tied + 1) <= top_n)
ORDER BY position, a.id
RETURN standing, id, value WITH RESUME;
END FOREACH;
END PROCEDURE;
This can be invoked to produce the same result as before:
EXECUTE PROCEDURE leader_board_standings(4);
To illustrate the various cases outlined above, add and remove extra rows:
EXECUTE PROCEDURE leader_board_standings(4);
1 3 10
2 5 7
3 1 6
6 4 2
INSERT INTO leader_board(id, value) VALUES(10, 10);
EXECUTE PROCEDURE leader_board_standings(4);
1 3 10
1 10 10
3 5 7
7 4 2
INSERT INTO leader_board(id, value) VALUES(11, 10);
EXECUTE PROCEDURE leader_board_standings(4);
1 3 10
1 10 10
1 11 10
8 4 2
INSERT INTO leader_board(id, value) VALUES(12, 10);
EXECUTE PROCEDURE leader_board_standings(4);
1 3 10
1 10 10
1 11 10
1 12 10
9 4 2
DELETE FROM leader_board WHERE id IN (10, 11, 12);
EXECUTE PROCEDURE leader_board_standings(6, 4); -- Special ID 6; Top 4
1 3 10
2 5 7
3 1 6
4 2 4
5 6 3
INSERT INTO leader_board(id, value) VALUES(7, 7);
EXECUTE PROCEDURE leader_board_standings(4);
1 3 10
2 5 7
2 7 7
7 4 2
INSERT INTO leader_board(id, value) VALUES(13, 7);
EXECUTE PROCEDURE leader_board_standings(4);
1 3 10
2 5 7
2 7 7
2 13 7
8 4 2
INSERT INTO leader_board(id, value) VALUES(14, 7);
EXECUTE PROCEDURE leader_board_standings(4);
1 3 10
2 5 7
2 7 7
2 13 7
2 14 7
9 4 2
DELETE FROM leader_board WHERE id IN(7, 13, 14);
INSERT INTO leader_board(id, value) VALUES(8, 6);
EXECUTE PROCEDURE leader_board_standings(4);
1 3 10
2 5 7
3 1 6
3 8 6
7 4 2
INSERT INTO leader_board(id, value) VALUES(9, 6);
EXECUTE PROCEDURE leader_board_standings(4);
1 3 10
2 5 7
3 1 6
3 8 6
3 9 6
8 4 2
INSERT INTO leader_board(id, value) VALUES(15, 6);
EXECUTE PROCEDURE leader_board_standings(4);
1 3 10
2 5 7
3 1 6
3 8 6
3 9 6
3 15 6
9 4 2
EXECUTE PROCEDURE leader_board_standings(3); -- Special ID 3 appears in top 3
1 3 10
2 5 7
3 1 6
That all looks correct to me.