Why can't I use this code to select the max row - mysql

I'm trying to select the row that contains the largest number and have accomplished it using this fairly simple query:
SELECT s1.score, name
FROM scores s1
JOIN (
SELECT id, MAX(score) score
FROM scores
GROUP BY id
) s2 ON s1.score = s2.score
All it does (If im not wrong), is just checking if the score field is equal the the MAX(score). So why can't we just do it using one single SELECT statement ?. Something like this:
SELECT id, score
FROM scores
GROUP BY id
HAVING MAX(score) = score
*The code above does not work, I want to ask why it is not working, because its essentially doing the same thing as the previous code I posted
Also here's the data I'm working with:

The problem in your second query is the fact that the GROUP BY clause requires all non-aggregated fields within its context. In your case you are dealing with three fields (namely "id", "score" and "MAX(score)") and you're referencing only one (namely "id") inside the GROUP BY clause.
Fixing that would require you to add the non-aggregated "score" field inside your GROUP BY clause, as follows:
SELECT id, score
FROM scores
GROUP BY id, score
HAVING MAX(score) = score
Though this would lead to a wrong aggregation and output, because it would attempt to get the maximum score for each combination of (id, score).
And if you'd attempt to remove the "score" field from both the SELECT and GROUP BY clauses, to solve the non-aggregated columns issue, as follows:
SELECT id
FROM scores
GROUP BY id
HAVING MAX(score) = score
Then the HAVING clause would complain as long it references the "score" field but it is found neither within the SELECT clause nor within the GROUP BY clause.
There's really no way for you to use that kind of notation, as it either violates the full GROUP_BY mode, or it just returns the wrong output.

It returns all persons with same score which the score is the max:
WITH CTE AS (
SELECT *, ROW_NUMBER() OVER(ORDER BY score desc) RN
FROM scores
)
SELECT * FROM CTE
WHERE CTE.RN = 1

Here's what your queries return
DROP table if exists t;
create table t
(id int,score int);
insert into t values
(1,10),(2,20),(3,20);
SELECT s1.id,s1.score
FROM t s1
JOIN (
SELECT id, MAX(score) score
FROM t
GROUP BY id
) s2 ON s1.score = s2.score ;
+------+-------+
| id | score |
+------+-------+
| 1 | 10 |
| 2 | 20 |
| 2 | 20 |
| 3 | 20 |
| 3 | 20 |
+------+-------+
5 rows in set (0.001 sec)
SELECT id, score,max(score)
FROM t
GROUP BY id
HAVING MAX(score) = score
+------+-------+------------+
| id | score | max(score) |
+------+-------+------------+
| 1 | 10 | 10 |
| 2 | 20 | 20 |
| 3 | 20 | 20 |
+------+-------+------------+
3 rows in set (0.001 sec)
Neither result seems to be what you are looking for. You could clarify by posting sample data and desired outcome.

Related

How to get lowest value from posts with the same ID [duplicate]

This question already has answers here:
Retrieving the last record in each group - MySQL
(33 answers)
Closed 2 years ago.
My sample table:
ID | Post_id | Score
1 | 1 | 33
2 | 1 | 43
3 | 1 | 27
4 | 1 | 66
I want to get rows with the lowest value (Score). In this case it is:
ID | Post_id | Score
3 | 1 | 27
My query:
SELECT * FROM table WHERE post_id = '1' GROUP BY post_id ORDER BY Score ASC
But that doesn't work because it returns me: Score: 33
How to fix it? What if I have thousands of rows and want post_id to be unique for the lowest values?
You must use subquery selecting min values for each post_id.
SELECT a.* FROM records a
JOIN
( SELECT post_id, MIN(score) as min_score
FROM records GROUP BY post_id
) b
ON a.score=b.min_score;
Output
| id | post_id | score |
| --- | ------- | ----- |
| 3 | 1 | 27 |
| 5 | 2 | 20 |
View on DB Fiddle
For a single id, just remove the group by and use limit:
SELECT *
FROM table
WHERE post_id = 1
ORDER BY Score ASC
LIMIT 1;
I assume that post_id is a number. Compare numbers to numbers, not to strings.
EDIT:
If you want this per post_id, then just use a correlated subquery:
select t.*
from t
where t.score = (select min(t2.score) from t t2 where t2.post_id = t.post_id);
If you may have multiple rows with the lowest score, you can do it with a sub-query :
SELECT *
FROM test
WHERE post_id = 1
AND score = (
SELECT MIN(score)
FROM test
WHERE post_id = 1
)
Fiddle : https://www.db-fiddle.com/f/3ppntnA77HFpKRU82h32Gv/1
IF you're using MySQL v8.0, you can use the ROW_NUMBER() function to order the result. That way you can choose the row with the lower score and return everything from it:
select
sq.id, sq.post_id, sq.score
from
(select id, post_id, score
, row_number() over (partition by post_id order by score) RowNo
from test) sq
where sq.RowNo = 1
Here is a Fiddle to test the code: https://www.db-fiddle.com/#&togetherjs=8dHSCs50Iq
I also included another post_id beside your sample data, to demonstrate how it reacts to multiple post_id's
The below should do the trick:
Select
id,
score,
Post_id,
min(score)
from
table
where
score = min(score);

How to find the count of a particular number that can exist in 2 columns

I have a table that consist of a table that describes calls. Hence there is a to column and a from column. The problem is that I want the total messages sent by each number, which can be from or to. Refer to the table above for visuals.
I want the final table to be somethng that shows A : 3 , B: 2 , C:1 and D:1.
How do u count the numbers in 2 columns and sum them up?
One solution would be to first UNION ALL two aggregate queries to gather the count of occurences of each value in the two different columns, and them sum the results in an outer query, like:
SELECT val, SUM(cnt) cnt
FROM (
SELECT `from` val, COUNT(*) cnt FROM mytable GROUP BY `from`
UNION ALL
SELECT `to`, COUNT(*) FROM mytable GROUP BY `to`
) x
GROUP BY val
This demo on DB Fiddle with your sample data returns:
| val | cnt |
| --- | --- |
| A | 3 |
| B | 2 |
| C | 1 |
| D | 1 |
| E | 1 |
You can unpivot the data and aggregate:
select person, count(*) as num_calls
from ((select from as person from t) union all
(select to as person from t
) c
group by person;
Note that from and to are really, really bad names for columns because they are SQL keywords. I haven't escaped them in the query, because that just clutters the query and I assume the real columns have better names.

select non group by columns with count in Mysql

I have a table tbl with three columns:
id | fk | dateof
1 | 1 | 2016-01-01
2 | 1 | 2016-01-02
3 | 2 | 2016-02-01
4 | 2 | 2016-03-01
5 | 3 | 2016-04-01
I want to get the results like this
Id count of Id max(dateof)
2 | 2 | 2016-01-02
4 | 2 | 2016-03-01
5 | 1 | 2016-04-01
My try
SELECT id,tbl.dateof dateof
FROM tbl
INNER JOIN
(SELECT fk, MAX(dateof) dateof ,
count(id) cnt_of_id -- How to get this count value in the result
FROM tbl
GROUP BY fk) temp
ON tbl.fk = temp.fk AND tbl.dateof = temp.dateof
This is an aggregation query, but you don't seem to want the column being aggregated. That is ok (although you cannot distinguish the rk that defines each row):
select count(*) as CountOfId, max(dateof) as maxdateof
from t
group by fk;
In other words, your subquery is pretty much all you need.
If you have a reasonable amount of data, you can use a MySQL trick:
select substring_index(group_concat(id order by dateof desc), ',', 1) as id
count(*) as CountOfId, max(dateof) as maxdateof
from t
group by fk;
Note: this is limited by the maximum intermediate size for group_concat(). This parameter can be changed and it is typically large enough for this type of query on a moderately sized table.
You obviously want one result row per fk, so group by it. Then you want the max ID, the row count and the max date for each fk:
select
max(id) as max_id,
count(*) as cnt,
max(date_of) as max_date_of
from tbl
group by fk;

Limit MySQL Results to One From Each "Group"

Suppose we have a table like the one below.
Id | Name | Group
-----------------
1 | John | 1
2 | Zayn | 2
3 | Four | 2
4 | Ben_ | 3
5 | Joe_ | 2
6 | Anna | 1
The query below will select all of them.
SELECT `Name` FROM `Table` WHERE 1;
How would I select only one person from each group? Who it is doesn't really matter, as long as there's only one name from group 1 and one name from group 2 etc.
The GROUP BY clause isn't fit for this (according to my error console) because I am selecting non aggregated values, which makes sense.
The DISTINCT clause isn't great here either, since I don't want to select the "Group" and definitely not group by their names.
If is not important the resulting name You can anawy leverage some group functions eg with max or min..
leverage the group functions
select max(name) from your_table
group by Group;
otherwise you can use subquery
select name from your_table
where Id in (select min(Id) from your_table group by Group);

MAX function in MySQL does not return proper key value

I have a table called tbl_user_sal:
| id | user_id | salary | date |
| 1 | 1 | 1000 | 2014-12-01 |
| 2 | 1 | 2000 | 2014-12-02 |
Now I want to get the id of the maximum date. I used the following query:
SELECT MAX(date) AS from_date, id, user_id, salary
FROM tbl_user_sal
WHERE user_id = 1
But it gave me this output:
| id | user_id | salary | from_date |
| 1 | 1 | 2000 | 2014-12-02 |
Which is correct as far as the max date being 2014-12-02, but the corresponding id is not correct. This happens for other records as well. I used order by to check but that was not successful either. Can anyone shed some light on this?
Note: Its not necessary that max date will have max id, according to my needs. Records can have max date but id may be older.
If you only want to retrieve that information for a single user, which you seem to, because of your WHERE clause, just use ORDER BY and LIMIT:
SELECT *
FROM tbl_user_sal
WHERE user_id = 1
ORDER BY date DESC
LIMIT 1
If you want to do that for every user, however, you will have to get a little bit fancier. Something like that should do it:
SELECT t2.id, user_id, date
--find max date for each user_id
FROM (SELECT user_id, MAX(date) AS date
FROM tbl_user_sal
GROUP BY user_id) AS t1
--join ids for each max date/user_id combo
JOIN tbl_user_sal AS t2
USING (user_id, date)
--limit to 1 id for every user_id
GROUP BY
user_id
You are missing group by clause Try this:
select max(awrd_date) as from_date,awrd_id
from tbl_user_sal
where awrd_user_id = 106
group by awrd_id
What I believe you should do here is have a subquery that pulls the max date, and your outer query looks for the row with that date.
It looks like this:
SELECT *
FROM myTable
WHERE date = (SELECT MAX(date) FROM myTable);
Additional things may need to be added if you want to search for a specific user_id, or get the largest date for each user_id, but this gives your expected results for this example here.
Here is the SQL Fiddle.