MySQL Query with distinct on a column value - mysql

I have this table (with 100k+ rows):
room_id | emote_id | count | since
----------------------------------------
1 | 22 | 718| 1577135778
1 | 23 | 124| 1577135178
1 | 24 | 842| 1577135641
2 | 22 | 124| 1577135748
2 | 23 | 345| 1577136441
2 | 24 | 43| 1577543578
3 | 22 | 94| 1572135778
3 | 23 | 4718| 1577135641
3 | 24 | 18| 1577134661
4 | 22 | 78| 1577125641
4 | 23 | 128| 1577135778
4 | 24 | 278| 1577132577
I want to get for each emote_id the row where count is the highest for this emote_id
So for this example I'd like to get this as response:
room_id | emote_id | count | since
----------------------------------------
1 | 22 | 718| 1577135778
3 | 23 | 4718| 1577135641
1 | 24 | 842| 1577135641
I'm stuck at building the query and need help. :(

You can use nested subquery with max() aggregation
select t1.*
from tab t1
join (select emote_id, max(count) as count
from tab
group by emote_id ) t2
on t1.emote_id = t2.emote_id
and t1.count = t2.count
for db version 8+ you can use window analytic functions such as dense_rank():
select room_id, emote_id, count, since
from
(
select t.*, dense_rank() over (partition by emote_id order by count desc) as dr
from tab t
) tt
where tt.dr = 1
Demo
All matching maximum values for count return through use of dense_rank() in case of tie( having equal valued count for any of emote_id ). If analytic function was row_number(), then only one row would return even if tie occurs.

Related

Show first and last mysql record in group with other attributes

I would like to ask for help to display some information regarding queries made in a database. I have a table called membervalues. In this table, I have 4 fields => (id, memberid, submit_datetime, value). I want to show maximum and minimum value of each member based on record id. For example, I have following
id | memberid | submit_datetime | value
------------------------------------------------
1 | 1 | 2018-01-18 09:44:00 | 86
2 | 2 | 2018-01-18 10:32:00 | 95
3 | 3 | 2018-01-18 11:19:00 | 74
4 | 1 | 2018-01-18 17:57:00 | 84
5 | 3 | 2018-01-18 18:31:00 | 96
7 | 1 | 2018-01-19 06:31:00 | 86
8 | 2 | 2018-01-19 07:31:00 | 94
9 | 1 | 2018-01-19 08:31:00 | 87
What one query will return the following result in php loop?
memberid | min_record_value | max_record_value
------------------------------------------------
1 | 86 | 87
2 | 95 | 94
3 | 74 | 96
I can get the min and max id but through min(id) and max(id) with GROUP BY memberid, but I am not able to figure how to get record value with it as I required above. While searching I came across mysql - join first and last records by group type? and tried to opt for similar approach with
select x2.memberid, x2.value as min_record_value, y2.value as max_record_value
from (select *
from (select memberid, value
from membervalues
order by 1, 2) x1
group by 1) x2
join (select *
from (select memberid, value
from membervalues
order by 1, 2 desc) y1
group by 1) y2 on y2.memberid = x2.memberid
but it returns only first rows value for both as below.
memberid | min_record_value | max_record_value
------------------------------------------------
1 | 86 | 86
2 | 95 | 95
3 | 74 | 74
Any help will be greatly appreciated. Thanks
You can use the row_number() window function to assign a number, starting from 1, to each record for each memberid depending on the position of the record when ordered by submit_datetime and id (to resolve possible ties). Once in ascending order for the minimum and once in descending order for the maximum. Then you can join the derived table on common memeberid and that number and filter for that number being 1.
SELECT xmi.memberid,
xmi.value min_record_value,
xma.value max_record_value
FROM (SELECT v1.memberid,
v1.value,
row_number() OVER (PARTITION BY v1.memberid
ORDER BY v1.submit_datetime ASC,
v1.id ASC) rn
FROM membervalues v1) xmi
INNER JOIN (SELECT v2.memberid,
v2.value,
row_number() OVER (PARTITION BY v2.memberid
ORDER BY v2.submit_datetime DESC,
v2.id DESC) rn
FROM membervalues v2) xma
ON xma.memberid = xmi.memberid
AND xma.rn = xmi.rn
WHERE xma.rn = 1
AND xmi.rn = 1;

Using LIMIT in a subquery based on another field in MySQL

Is it possible to use LIMIT based on another column inside a subquery in MySQL? Here is a working query of what I mean.
SELECT id, name,
(SELECT AVG(value) FROM t2 WHERE t1id = t1.id ORDER BY value DESC LIMIT 4) as average
FROM t1
However I'd like to replace the "4" to a field inside t1.
Something like this where table t1 has fields id, name, size:
SELECT id, name,
(SELECT AVG(value) FROM t2 WHERE t1id = t1.id ORDER BY value DESC LIMIT t1.size) as average
FROM t1
I could join t1 and t2, but I'm not sure that works for this. Does it?
Edit:
Here's some sample data to show what I mean:
Table t1
| id | name | Size |
|----|------|------|
| 1 | Bob | 4 |
| 2 | Joe | 3 |
| 3 | Sam | 4 |
Table t2
| t1id | value |
|------|-------|
| 1 | 16 |
| 1 | 14 |
| 1 | 12 |
| 1 | 10 |
| 1 | 8 |
| 2 | 10 |
| 2 | 8 |
| 2 | 6 |
| 2 | 4 |
| 3 | 20 |
| 3 | 15 |
| 3 | 10 |
| 3 | 5 |
| 3 | 2 |
Expected result:
| id | name | avg |
|----|------|------|
| 1 | Bob | 13 |
| 2 | Joe | 8 |
| 3 | Sam | 12.5 |
Notice that the average is the average of only the top t1.size values. For example the average for Bob is 13 and not 12 (based on 4 values and not 5) and the average for Joe is 8 and not 7 (based on 3 values and not 4).
In MySQL, you have little choice other than LEFT JOIN and aggregation:
SELECT t1.id, t1.name, AVG(t2.value) as average
FROM t1 LEFT JOIN
(SELECT t2.*,
ROW_NUMBER() OVER (PARTITION BY t1id ORDER BY VALUE desc) as seqnum
FROM t2
) t2
on t2.t1id = t1.id AND seqnum <= t1.size
GROUP BY t1.id, t1.name;
Here is a db<>fiddle.
No, you cannot use a column reference in a LIMIT clause.
https://dev.mysql.com/doc/refman/8.0/en/select.html has detailed documentation about MySQL's SELECT statement including all its clauses.
It says:
The LIMIT clause can be used to constrain the number of rows returned by the SELECT statement. LIMIT takes one or two numeric arguments, which must both be nonnegative integer constants, with these exceptions:
Within prepared statements, LIMIT parameters can be specified using ? placeholder markers.
Within stored programs, LIMIT parameters can be specified using integer-valued routine parameters or local variables.
Expressions, including subqueries, are not mentioned as legal argument in the LIMIT clause.
A simple solution would be to do your task in two queries: the first to get the size and then use that value as a constant value in the second query that includes the LIMIT.
Not every task needs to be done in a single SQL statement.

Mysql query returning latest result for each element

Having a hard time wrapping my mind around what seems should be a simply query.
So let's say we have a table that keeps track of amount of widgets/balloons in each store by date. How would you get a list of stores and their latest widget/balloons count?
i.e.
mysql> SELECT * FROM inventory;
+----+------------+-------+---------+---------+
| id | invDate | store | widgets | balloons|
+----+------------+-------+---------+---------+
| 1 | 2011-01-01 | 3 | 50 | 35 |
| 2 | 2011-01-04 | 2 | 50 | 35 |
| 3 | 2013-07-04 | 3 | 12 | 78 |
| 4 | 2020-07-04 | 2 | 47 | 18 |
| 5 | 2020-08-06 | 2 | 16 | NULL |
+----+------------+-------+---------+---------+
5 rows in set (0.00 sec)
Would like the result table to list all stores and their latest inventory of widgets/baloons
store, latest widgets, latest balloons
+-------+-----------+---------+
| store | widgets | baloons |
+-------+-----------+---------+
| 2 | 16 | NULL |
| 3 | 12 | 78 |
+-------+-----------+---------+
or grab latest non NULL value for balloons.
This works for all versions of MySQL
select i.*
from inventory i
join
(
select store, max(invDate) as maxDate
from inventory
group by store
) tmp on tmp.store = i.store
and tmp.maxDate = i.invDate
With MySQL 8+ you can do window functions:
with cte as
(
select store, widgets, balloons,
ROW_NUMBER() OVER(PARTITION BY store ORDER BY invDate desc) AS rn
from inventory
)
select * from cte where rn = 1
You can use a correlated sub query to get latest record for each store
select i.*
from inventory i
where i.invDate = (
select max(invDate)
from inventory
where i.store = store
)
order by i.store
DEMO

How mysql add count colnum to a table

I have tableA and want add count colnum to it , count is total rows of tableA and is same at all rows in result table ,tableA like below :
note: table A is result of other query
tableA
id | name | rank |
-------------------
1 | John | 12 |
2 | Maria | 18 |
3 | Steph | 44 |
4 | Jay | 17 |
and result should be :
id | name | rank | total | rank/total
---------------------------------------
1 | John | 12 | 4 | 3
2 | Maria | 18 | 4 | 4.5
3 | Steph | 44 | 4 | 11
4 | Jay | 17 | 4 | 4.25
how can do that with MYSQL
To get the total rows in your table, do this:
SELEcT count(*) FROM TableA
You can then combine this query in the SELEcT of another query and make a computation from it, which gives this :
SELEcT *, (SELEcT count(*) FROM TableA) AS total, rank / (SELEcT count(*) FROM TableA) AS 'rank/total'
FROM tableA
if tableA is the result of another query, do
SELEcT *, (SELEcT count(*) FROM TableA) AS total, rank / (SELEcT count(*) FROM TableA) AS 'rank/total'
FROM ( insert_the_other_query_here ) tableA
Check if using a variable helps you helps you:
SET #total = SELECT COUNT(1) FROM tableA;
SELECT name, rank, #total AS total, rank/#total AS 'rank/total' FROM tableA;

MySQL query: select records with highest value in group

I have a MySQL table like this.
| season_id | round_1 | names | score_round_1
| 5 | 10 | John1 | 5
| 5 | 10 | John2 | 3
| 5 | 11 | John3 | 2
| 5 | 11 | John4 | 5
I want to select the records with highest score_round_1 in each round_1(10,11) group .
In this case the first and last rows would be selected.
I tried using the GROUP BY round_1 but that only returns the first row from the two.
Any advice?
Zolka
This is simple
select max(score_round_1),
name
from score
group by round_1
SELECT *
FROM table p1
WHERE score_round_1 = (
SELECT MAX( p2.score_round_1 )
FROM table p2
WHERE p1.round_1 = p2.round_1 ) ANDround_1 !=0
Use aggregate function MAX
SELECT names, MAX(score_round_1) GROUP BY round_1