Get MAX row for GROUP in MySQL - mysql

I have the following data:
+---------+----------+----------+--------+
| id | someId | number | data |
+---------+----------+----------+--------+
| 27 | 123 | 1 | abcde1 |
| 28 | 123 | 3 | abcde2 |
| 29 | 123 | 1 | abcde3 |
| 30 | 123 | 5 | abcde4 |
| 31 | 124 | 4 | abcde1 |
| 32 | 124 | 8 | abcde2 |
| 33 | 124 | 1 | abcde3 |
| 34 | 124 | 2 | abcde4 |
| 35 | 123 | 16 | abcde1 |
| 245 | 123 | 3 | abcde2 |
| 250 | 125 | 0 | abcde3 |
| 251 | 125 | 1 | abcde4 |
| 252 | 125 | 7 | abcde1 |
| 264 | 125 | 0 | abcde2 |
| 294 | 123 | 0 | abcde3 |
| 295 | 126 | 0 | abcde4 |
| 296 | 126 | 0 | abcde1 |
| 376 | 126 | 0 | abcde2 |
+---------+----------+----------+--------+
And I want to get a MySQL query that gets me the data of the row with the highest number for each someId. Note that id is unique, but number isn't

SELECT someid, highest_number, data
FROM test_1
INNER JOIN (SELECT someid sid, max(number) highest_number
FROM test_1
GROUP BY someid) t
ON (someid=sid and number=highest_number)
Unfortunately it is not look quite efficient. In Oracle it could be possible to user OVER clause without subqueries, but MySQL…
Update 1
If there are several instances of highest number this will returs also several data for each pair of someid and number.
To get the only row per each someid we should preaggregate the source table to make someid and number pairs unique (see t1 subquery)
SELECT someid, highest_number, data
FROM
(SELECT someid, number, MIN(data) data
FROM test_1
GROUP BY
someid, number) t1
INNER JOIN
(SELECT someid sid, max(number) highest_number
FROM test_1
GROUP BY someid) t2
ON (someid=sid and number=highest_number)
Update 2
It is possible to simplify previous solution
SELECT someid,highest_nuimber,
(select min(data)
from test_1
where someid=t1.someid and number=highest_nuimber)
FROM
(SELECT someid, max(number) highest_nuimber
FROM test_1
GROUP BY someid) t1
If we materialize unique pairs of someid and number than it is possible to use correlated subquery. Unlike a JOIN it would not produce additional rows if highest value of number is repeated several times.

Slight tweak to Naeel's answer but to return just a single data result for any someId even if there's a tie you should add a GROUP BY:
SELECT t1.someid, t1.number, t1.data
FROM Table1 t1
INNER JOIN (SELECT someId sid, max(number) max_number
FROM Table1
GROUP BY someId) t2
ON (someId = sid AND number = max_number)
GROUP BY t1.someId
SQL Fiddle here

Related

Mysql delete similar rows according to specific columns except the ones with highest id

my table has duplicate row values in specific columns. i would like to remove those rows and keep the row with the latest id.
the columns i want to check and compare are:
sub_id, spec_id, ex_time
so, for this table
+----+--------+---------+---------+-------+
| id | sub_id | spec_id | ex_time | count |
+----+--------+---------+---------+-------+
| 1 | 100 | 444 | 09:29 | 2 |
| 2 | 101 | 555 | 10:01 | 10 |
| 3 | 100 | 444 | 09:29 | 23 |
| 4 | 200 | 321 | 05:15 | 5 |
| 5 | 100 | 444 | 09:29 | 8 |
| 6 | 101 | 555 | 10:01 | 1 |
+----+--------+---------+---------+-------+
i would like to get this result
+----+--------+---------+---------+-------+
| id | sub_id | spec_id | ex_time | count |
+----+--------+---------+---------+-------+
| 5 | 100 | 444 | 09:29 | 8 |
| 6 | 101 | 555 | 10:01 | 1 |
+----+--------+---------+---------+-------+
i was able to build this query to select all duplicate rows from multiple columns, according to this question
select t.*
from mytable t join
(select id, sub_id, spec_id, ex_time, count(*) as NumDuplicates
from mytable
group by sub_id, spec_id, ex_time
having NumDuplicates > 1
) tsum
on t.sub_id = tsum.sub_id and t.spec_id = tsum.spec_id and t.ex_time = tsum.ex_time
but now im not sure how to wrap this select with a delete query to delete the rows except for the ones with highest id.
as shown here
You can modify your sub-select query, to get maximum value of id for each duplication combination.
Now, while joining to the main table, simply put a condition that id value will not be equal to the maximum id value.
You can now Delete from this result-set.
Try the following:
DELETE t
FROM mytable AS t
JOIN
(SELECT MAX(id) as max_id,
sub_id,
spec_id,
ex_time,
COUNT(*) as NumDuplicates
FROM mytable
GROUP BY sub_id, spec_id, ex_time
HAVING NumDuplicates > 1
) AS tsum
ON t.sub_id = tsum.sub_id AND
t.spec_id = tsum.spec_id AND
t.ex_time = tsum.ex_time AND
t.id <> tsum.max_id

How to get max value with various conditions from a single MySQL table

I have table with a bunch of (machine id) mid's and (sensor id) sid's, and their corresponding (values) v's. Needless to say the id column is a unique row number. (NB: There are other columns in the table, and not all mid's have the same sid's)
Current Table:
+------+-------+-------+-----+---------------------+
| id | mid | sid | v | timestamp |
+------+-------+-------+-----+---------------------+
| 51 | 10 | 1 | 40 | 2015/5/1 11:56:01 |
| 52 | 10 | 2 | 39 | 2015/5/1 11:56:25 |
| 53 | 10 | 2 | 40 | 2015/5/1 11:56:42 |
| 54 | 11 | 1 | 50 | 2015/5/1 11:57:52 |
| 55 | 11 | 2 | 18 | 2015/5/1 11:58:41 |
| 56 | 11 | 2 | 19 | 2015/5/1 11:58:59 |
| 57 | 11 | 3 | 58 | 2015/5/1 11:59:01 |
| 58 | 11 | 3 | 65 | 2015/5/1 11:59:29 |
+------+-------+-------+-----+---------------------+
Q: How would I get the MAX(v)for each sid for each mid?
Expected Output:
+------+-------+-------+-----+---------------------+
| id | mid | sid | v | timestamp |
+------+-------+-------+-----+---------------------+
| 51 | 10 | 1 | 40 | 2015/5/1 11:56:01 |
| 53 | 10 | 2 | 40 | 2015/5/1 11:56:42 |
| 54 | 11 | 1 | 50 | 2015/5/1 11:57:52 |
| 56 | 11 | 2 | 19 | 2015/5/1 11:58:59 |
| 58 | 11 | 3 | 65 | 2015/5/1 11:59:29 |
+------+-------+-------+-----+---------------------+
The expected output is to obtain the whole row with all the (single) max value for all the sids in all the mids.
Addendum:
Due to a very big table, I need to place boundaries with dates. For the sample above the two boundary dates should be 2015/05/01 00:00:00 (1st of May'15) till 2015/05/02 00:00:00 (2nd of May'15). Q: How could I add this date boundary?
Find the max v in subquery for each combination of mid, sid and then join it with your original table to get the desired result.
select *
from your_table t
join (
select mid, sid, max(v) as v
from your_table
group by mid, sid
) t2 using (mid, sid, v);
Note here that if there are multiple rows with same sid, mid and v, it will return all of them.
As mentioned in the comments, since you have an id column, you can include that in limited correlated query like this:
select *
from your_table t1
where id = (select id
from your_table t2
where t1.mid = t2.mid
and t1.sid = t2.sid
order by v desc, id desc
limit 1
);
This will give you one single row per mid, sid combination with max v (and latest id in case of ties).
Use MAX() function with GROUP BY clause
SELECT id, mid, sid, MAX(v) AS v, `timestamp`
FROM MyTable
GROUP BY mid, sid;
This returns rows with maximum values of v for each combination of mid and sid.

MYSQL sum from two different tables group by

I have two tables.
I need to combine each row of these two tables into a row in table3. I managed to get the table1 SUM amount but not table2.
Eg.
table user
+---------+-----------+
| user_id | user_name |
+---------+-----------+
| 001 | JOHN |
| 002 | ADAM |
+---------+-----------+
table1
+-----------+----------------+-------------------+---------------------+
| table1_id | table1_user_id | table1_amount | table1_date |
+-----------+----------------+-------------------+---------------------+
| 6 | 001 | 100 | 01/11/2014 10:55 |
| 7 | 002 | 100 | 01/11/2014 10:55 |
| 8 | 001 | 50 | 25/10/2014 10:55 |
| 9 | 001 | 100 | 23/10/2014 11:00 |
| 10 | 002 | 0 | 21/10/2014 11:00 |
+-----------+----------------+-------------------+---------------------+
table2
+-----------+----------------+----------------+--------------------+
| table2_id | table2_user_id | table2_amount | table2_date |
+-----------+----------------+----------------+--------------------+
| 1 | 001 | 100 | 15/11/2014 10:55 |
| 2 | 001 | 100 | 15/10/2014 10:55 |
| 3 | 002 | 100 | 11/10/2014 10:55 |
| 4 | 001 | 50 | 11/10/2014 10:55 |
+-----------+----------------+----------------+--------------------+
Expected Result:
Table3
+-----+---------+---------------+---------------+----------+---------+
| id | user_id | table1_amount | table2_amount | Year | Month |
+-----+---------+---------------+---------------+----------+---------+
| 1 | 001 | 100 | 100 | 2014 | 11 |
| 2 | 002 | 100 | 0 | 2014 | 11 |
| 3 | 001 | 150 | 150 | 2014 | 10 |
| 4 | 002 | 0 | 100 | 2014 | 10 |
+-----+---------+---------------+---------------+----------+---------+
My try but it does not show the expected result. The amount of table2_amount in every row is NULL :
SQL=" INSERT INTO table3
SELECT user_id,SUM(table1_amount),t2.amount2,
YEAR(table1_date),MONTH(table1_date) FROM table1 a
LEFT JOIN
(SELECT c.table2_user_id,SUM(c.table2_amount) as amount2,c.table2_date
FROM table2 c
GROUP BY DATE_FORMAT(c.table2_date,'%Y-%m'),c.table2_user_id ASC
) t2
on t2.table2_user_id = a.table1_user_id AND t2.table2_date = a.table1_date
GROUP BY DATE_FORMAT(a.table1_date,'%Y-%m'),table1_user_id ASC ";
"
This a nice task for UNION
SELECT tx.uid,SUM(tx.a1),SUM(tx.a2),YEAR(tx.d),MONTH(tx.d)
FROM
(
SELECT t1.table1_user_id as uid,
t1.table1_amount as a1,
0 as a2,
t1.table1_date as d
FROM table1 t1
UNION
SELECT t2.table2_user_id as uid,
0 as a1,
t2.table2_amount as a2,
t2.table2_date as d
FROM table2 t2
) tx
GROUP BY DATE_FORMAT(d,'%Y-%m'),uid ASC
Thanks to David162795 for the enlightening discussion.
The missed point is group INNER QUERY by date and user id when the date from two tables are different.
We need to group them by their individual date in the Inner Query and then group the main SELECT query by the time variable.
This goes my answer for this case :
$SQL = "
INSERT INTO table3 (user_id, table1_amount, table2_amount,Year, Month)
SELECT tx.uid, SUM(tx.sum1), SUM(tx.sum2),YEAR(tx.d) as year,MONTH(tx.d) as month
FROM
(SELECT b.table1_user_id as uid,b.table1_amount as sum1,0 as sum2,
b.table1_date as d FROM table1 b
GROUP BY DATE_FORMAT(d,'%Y-%m'),uid ASC
UNION
SELECT c.table2_user_id as uid,0 as sum1,
sum(c.table2_amount) as sum2,c.table2_date as d1
FROM table2 c
GROUP BY DATE_FORMAT(d1,'%Y-%m'),uid ASC
) tx
GROUP BY year,month,uid"

Sql Query to retrieve data using table

I need query to get the count of participants in each level.
Note: participant who is in level 4 should not be in other levels ex: Level 3,2,1. I.e. I'm only interested in the highest level of each participant.
Table :
+----+-------+----------+
| ID | Level | Date |
+----+-------+----------+
| 38 | 1 | 06 -05 |
| 38 | 2 | 08 -05 |
| 38 | 3 | 12 -05 |
| 38 | 4 | 13 -05 |
| 39 | 1 | 13 -05 |
| 39 | 2 | 13 -05 |
| 40 | 1 | 12 -05 |
+----+-------+----------+
Needed Output:
+-------+-------+
| Count | Level |
+-------+-------+
| 1 | 1 |
| 1 | 2 |
| 0 | 3 |
| 1 | 4 |
+-------+-------+
Try this:
SELECT count(t2.Id) as count,t1.Level as level
FROM table1 t1
LEFT JOIN
(SELECT Id,MAX(Level) as Level
FROM table1
GROUP BY Id ) t2 on t1.Id=t2.Id and t1.Level=t2.Level
GROUP BY t1.Level
You can test it on SQL Fiddle
I think you're looking for
Select Count(*), Level FROM table_name GROUP BY Level
the expected result is to know how many people have a level as max level, grouping by level.
SELECT count(id) as count, level
FROM
(
SELECT id, max(level) as level
FROM myTable
GROUP BY id
) t1
GROUP BY level;

How to hidden / delete data in row if same from the top

I have table transaction from mysql query like this.
____________________________________________________________
| id | noskom | nonpbm | noskop | npbp |
|____|_____________|____________|_____________|_____________|
| 1 | 001 | 10 | 20 | 20 |
| 2 | 001 | 20 | 10 | 20 |
| 3 | 002 | 5 | 10 | 20 |
| 4 | 003 | 30 | 5 | 20 |
|____|_____________|____________|_____________|_____________|
and i want result
select sum(nonpbm),sum(noskop),sum(npbp) from transaction group by noskom
____________________________________________________________
| id | noskom | nonpbm | noskop | npbp |
|____|_____________|____________|_____________|_____________|
| 1 | 001 | 30 | 30 | 40 |
| 2 | | | | |
| 3 | 002 | 5 | 10 | 20 |
| 4 | 003 | 30 | 5 | 20 |
|____|_____________|____________|_____________|_____________|
what is mysql query
SELECT
noskom ,
SUM(nonpbm) as nonpbm ,
sum(noskop) as noskop ,
sum(npbp) as npbp
FROM mytable
GROUP BY noskom
If you want to list the IDs of all the rows that contributed to the aggregated values, you can also add this column:
GROUP_CONCAT(id ORDER BY id) AS id
This will give you a comma-separated list of IDs. You can specify a different separator, like EOL:
GROUP_CONCAT(id ORDER BY id SEPARATOR '\r\n') AS id
'\r\n' is the standard line separator in the Windows world. Change to just '\n' or '\r' if you are on a different platform.
select transaction.id,t.noskom,t.sum_nonpbm,t.sum_noskop,t.sum_npbp
from transaction
left join
(
select noskom, min(id) min_id,
sum(nonpbm) sum_nonpbm,
sum(noskop) sum_noskop,
sum(npbp) sum_npbp
from transaction
group by noskom
) t on (transaction.id=t.min_id)
order by transaction.id
Try this query ::
select tbl2.id,tbl1.noskom,tbl1.sum1,tbl1.sum2,tbl1.sum3 from
(select id,noskom,sum(nonpbm) sum1,sum(noskop) sum2,sum(npbp) sum3 from tbl group by noskom) tbl1
right join
(select id,null a,null b,null c,null d
from tbl) tbl2
on tbl1.id = tbl2.id
order by 1 ;