Select the lastest one of each result in MySQL - mysql

Say if I have a table similar to this but including more columns and more rows (These are the only relevant ones):
+-------+----+
| name | id |
+-------+----+
| james | 1 |
| james | 2 |
| james | 3 |
| adam | 4 |
| max | 5 |
| adam | 6 |
| max | 7 |
| adam | 8 |
+-------+----+
How could I get it so that it would only show the max(id) from each name like:
+-------+----+
| name | id |
+-------+----+
| adam | 8 |
| max | 7 |
| james | 3 |
+-------+----+
I currently just have this
"select * from table order by id desc"
but this just shows the latest ids. I only want to be able to see one of each name.
So basically show only the highest id of each name

You would use aggregation and max():
select name, max(id)
from table t
group by name
order by max(id) desc
limit 40;
EDIT:
If you need select * with the highest id, then use the not exists approach:
select *
from table t
where not exists (select 1 from table t2 where t2.name = t.name and t2.id > t.id)
order by id desc
limit 40;
The "not exists" essentially says: "Get me all rows in the table where there is no other row with the same name and a higher id". That is a round-about way of getting the maximum row.

One way to achieve this is to leverage a non-standard GROUP BY extension in MySQL
SELECT *
FROM
(
SELECT *
FROM table1
ORDER BY id DESC
) q
GROUP BY name
-- LIMIT 40
or another way is to grab a max id per name first and then join back to your table to fetch all other columns
SELECT t.*
FROM
(
SELECT MAX(id) id
FROM table1
GROUP BY name
-- LIMIT 40
) q JOIN table1 t
ON q.id = t.id
ORDER BY name;
Output:
| NAME | ID |
|-------|----|
| adam | 8 |
| james | 3 |
| max | 7 |
Here is SQLFiddle demo

Related

SQL query to find maximum salary

I have a table like this
----------------------
| ID | Name | Salary |
| -- | --- | --- |
| 1 | A | 1000 |
| 2 | B | 4000 |
| 3 | C | 5000 |
| 4 | B | 600 |
| 5 | C | 2000 |
| 6 | A | 5000 |
| 7 | B | 4000 |
----------------------
And I want to query the maximum salary in the whole table and the maximum salary of each student. I can write two queries like,
>> SELECT MAX(Salary) FROM TABLE
>> SELECT NAME, MAX(SALARY) FROM TABLE GROUP BY NAME
Now, I want to do the same in a single query without using two queries. How do I approach?
ROLLUP can be used to give an extra row as a 'summary', and so give the overall MAX value...
SELECT name, MAX(salary)
FROM TABLE
GROUP BY name
WITH ROLLUP
ORDER BY GROUPING(name) DESC,
name
Which would yield...
| Name | Salary |
| --- | --- |
| NULL | 5000 |
| A | 5000 |
| B | 4000 |
| C | 5000 |
here is one way:
SELECT NAME, MAX(SALARY) , max(max(salary)) over()
FROM TABLE GROUP BY NAME
You can use GROUP BY to group around the Name field and ORDER BY ... DESC on the Salary field to get the max.
SELECT PrimaryField, MaxField
FROM MyTable
GROUP By PrimaryField
ORDER BY MaxField DESC;
Explanation:
SELECT PrimaryField, MaxField — The fields we want.
FROM MyTable — The table we want.
GROUP By PrimaryField — What we want to be treated as a primary field.
ORDER BY MaxField DESC; — Since we group by PrimaryField, we get only one row for each PrimaryField unique value. If we order the MaxField, we can get the min or max of it as needed.
For you specifically:
SELECT Name, Salary
FROM TABLE
GROUP By Name
ORDER BY Salary DESC;

Select Unique Rows Based on Single Distinct Column - MySQL

I want to select rows that have a distinct email, see the example table below:
Table Name = Users
+----+---------+-------------------+-------------+
| id | title | email | commentname |
+----+---------+-------------------+-------------+
| 3 | test | rob#hotmail.com | rob |
| 4 | i agree | rob#hotmail.com | rob |
| 5 | its ok | rob#hotmail.com | rob |
| 6 | hey | rob#hotmail.com | rob |
| 7 | nice! | simon#hotmail.com | simon |
| 8 | yeah | john#hotmail.com | john |
+----+---------+-------------------+-------------+
The desired result would be:
+----+-------+-------------------+-------------+
| id | title | email | commentname |
+----+-------+-------------------+-------------+
| 5 | its ok| rob#hotmail.com | rob |
| 7 | nice! | simon#hotmail.com | simon |
| 8 | yeah | john#hotmail.com | john |
+----+-------+-------------------+-------------+
Distinct value should be latest entry in Table Example id = 6
What would be the required SQL?
If you are using MySQL 5.7 or earlier, then you may join your table to a subquery which finds the most recent record for each email:
SELECT t1.id, t1.title, t1.email, t1.commentname
FROM yourTable t1
INNER JOIN
(
SELECT email, MAX(id) AS latest_id
FROM yourTable
GROUP BY email
) t2
ON t1.email = t2.email AND t1.id = t2.latest_id;
If you are using MySQL 8+, then just use ROW_NUMBER here:
WITH cte AS (
SELECT id, title, email, commentname,
ROW_NUMBER() OVER (PARTITION BY email ORDER BY id DESC) rn
FROM yourTable
)
SELECT id, title, email, commentname
FROM cte
WHERE rn = 1;
Note: Your expected output probably has a problem, and the id = 6 record is the latest for rob#hotmail.com.
You can try below using correlated subquery
select * from table1 a
where id in (select max(id) from table1 b where a.email=b.email group by b.email)
If 'id' is unique or primary key you could use this one:
select * from Users where id in (select max(id) from Users group by commentname)
Above one would up your database performance because the correlated subqueries comes from the fact that the subquery uses information from the outer query and the subquery executes once for every row in the outer query.So,I will suggest you using my answer if 'id' is unique.

Fetch first N rows including tie values MYSQL

+-----+-------+-----+
| id | Name |Votes|
+-----+-------+-----+
| 1 | Joe | 36 |
| 2 | John | 34 |
| 3 | Mark | 42 |
| 4 | Ryan | 29 |
| 5 | Jay | 36 |
| 6 | Shawn | 39 |
+-----+-------+-----+
For this example, what I want is to retrieve the rows with the first 3 highest votes. However, if you'll notice, there are two rows with the same vote count. So this should be the result:
+-----+-------+-----+
| id | Name |Votes|
+-----+-------+-----+
| 3 | Mark | 42 |
| 6 | Shawn | 39 |
| 1 | Joe | 36 |
| 5 | Jay | 36 |
+-----+-------+-----+
How to achieve this?
You will have to perform an INNER JOIN, using the table back on itself. First, you want to select the top 3 unique/distinct scores, and this can be done by using:
SELECT DISTINCT Votes FROM mytable ORDER BY Votes DESC LIMIT 3
Now that you have obtained the top 3 scores, you want to join it back to the original table:
SELECT t1.* FROM mytable AS t1
INNER JOIN
(SELECT DISTINCT Votes FROM mytable ORDER BY Votes DESC LIMIT 3) AS topvotes
ON
topvotes.Votes = t1.Votes
ORDER BY t1.Votes DESC
Refer to a simple diagram for the strategy:
For this query to be efficient, you will want to index the Votes column so that the subquery can fish out distinct votes quickly ;)
Here is a proof-of-concept SQLfiddle: http://sqlfiddle.com/#!9/c78f0/10
Probably not the most efficient, but I think this should work:
SELECT * FROM scores WHERE score IN(SELECT score FROM scores ORDER BY score DESC LIMIT 3)
Although this can yield an error about limit not being supported in subqueries.
A workaround;
SELECT * FROM scores WHERE score IN(SELECT * FROM (SELECT score FROM scores ORDER BY score DESC LIMIT 3) AS t)

I need to get the average for every 3 records in one table and update column in separate table

Table Mytable1
Id | Actual
1 ! 10020
2 | 12203
3 | 12312
4 | 12453
5 | 13211
6 | 12838
7 | 10l29
Using the following syntax:
SELECT AVG(Actual), CEIL((#rank:=#rank+1)/3) AS rank FROM mytable1 Group BY rank;
Produces the following type of result:
| AVG(Actual) | rank |
+-------------+------+
| 12835.5455 | 1 |
| 12523.1818 | 2 |
| 12343.3636 | 3 |
I would like to take AVG(Actual) column and UPDATE a second existing table Mytable2
Id | Predict |
1 | 11133
2 | 12312
3 | 13221
I would like to get the following where the Actual value matches the ID as RANK
Id | Predict | Actual
1 | 11133 | 12835.5455
2 | 12312 | 12523.1818
3 | 13221 | 12343.3636
IMPORTANT REQUIREMENT
I need to set an offset much like the following syntax:
SELECT #rank := #rank + 1 AS Id , Mytable2.Actual FROM Mytable LIMIT 3 OFFSET 4);
PLEASE NOTE THE AVERAGE NUMBER ARE MADE UP IN EXAMPLES
you can join your existing query in the UPDATE statement
UPDATE Table2 T2
JOIN (
SELECT AVG(Actual) as AverageValue,
CEIL((#rank:=#rank+1)/3) AS rank
FROM Table1, (select #rank:=0) t
Group BY rank )T1
on T2.id = T1.rank
SET Actual = T1.AverageValue

LIMIT results to n unique column values?

I have some MySQL results like this:
---------------------------
| name | something_random |
---------------------------
| john | ekjalsdjalfjkldd |
| alex | akjsldfjaekallee |
| alex | jkjlkjslakjfjflj |
| alex | kajslejajejjaddd |
| bob | ekakdie33kkd93ld |
| bob | 33kd993kakakl3ll |
| paul | 3k309dki595k3lkd |
| paul | 3k399kkfkg93lk3l |
etc...
This goes on for 1000's of rows of results. I need to limit the number of results to the first 50 unique names. I think there is a simple solution to this but I'm not sure.
I've tried using derived tables and variables but can't quite get there. If I could figure out how to increment a variable once every time a name is different I think I could say WHERE variable <= 50.
UPDATED
I've tried the Inner Join approach(es) suggested below. The problem is this:
The subselect SELECT DISTINCT name FROM testTable LIMIT 50 grabs the first 50 distinct names. Perhaps I wasn't clear enough in my original post, but this limits my query too much. In my query, not every name in the table is returned in the result. Let me modify my original example:
----------------------------------
| id | name | something_random |
----------------------------------
| 1 | john | ekjalsdjalfjkldd |
| 4 | alex | akjsldfjaekallee |
| 4 | alex | jkjlkjslakjfjflj |
| 4 | alex | kajslejajejjaddd |
| 6 | bob | ekakdie33kkd93ld |
| 6 | bob | 33kd993kakakl3ll |
| 12 | paul | 3k309dki595k3lkd |
| 12 | paul | 3k399kkfkg93lk3l |
etc...
So I added in some id numbers here. These ID numbers pertain to the people's names in the tables. So you can see in the results, not every single person/name in the table is necessarily in the result (due to some WHERE condition). So the 50th distinct name in the list will always have an ID number higher than 49. The 50th person could be id 79, 234, 4954 etc...
So back to the problem. The subselect SELECT DISTINCT name FROM testTable LIMIT 50 selects the first 50 names in the table. That means that my search results will be limited to names that have ID <=50, which is too constricting. If there are certain names that don't show up in the query (due to some WHERE condition), then they are still counted as one of the 50 distinct names. So you end up with too few results.
UPDATE 2
To #trapper: This is a basic simplification of what my query looks like:
SELECT
t1.id,
t1.name,
t2.details
FROM t1
LEFT JOIN t2 ON t1.id = t2.some_id
INNER JOIN
(SELECT DISTINCT name FROM t1 ORDER BY id LIMIT 0,50) s ON s.name = t1.name
WHERE
SOME CONDITIONS
ORDER BY
t1.id,
t1.name
And my results look like this:
----------------------------------
| id | name | details |
----------------------------------
| 1 | john | ekjalsdjalfjkldd |
| 3 | alex | akjsldfjaekallee |
| 3 | alex | jkjlkjslakjfjflj |
| 4 | alex | kajslejajejjaddd |
| 6 | bob | ekakdie33kkd93ld |
| 6 | bob | 33kd993kakakl3ll |
| 12 | paul | 3k309dki595k3lkd |
| 12 | paul | 3k399kkfkg93lk3l |
...
| 37 | bill | kajslejajejjaddd |
| 37 | bill | ekakdie33kkd93ld |
| 41 | matt | 33kd993kakakl3ll |
| 50 | jake | 3k309dki595k3lkd |
| 50 | jake | 3k399kkfkg93lk3l |
----------------------------------
The results stop at id=50. There are NOT 50 distinct names in the list. There are only roughly 23 distinct names.
My MySql syntax may be rusty, but the idea is to use a query to select the top 50 distinct names, then do a self-join on name and select the name and other information from the join.
select a.name, b.something_random
from Table b
inner join (select distinct name from Table order by RAND() limit 0,50) a
on a.name = b.name
SELECT DISTINCT name FROM table LIMIT 0,50
Edited: Ahh yes I misread question first time, this should do the trick though :)
SELECT a.name, b.something_random
FROM `table` b
INNER JOIN (SELECT DISTINCT name FROM `table` ORDER BY RAND() LIMIT 0,50) a
ON a.name = b.name ORDER BY a.name
How this work is the (SELECT DISTINCT name FROMtableORDER BY RAND() LIMIT 0,50) part is what pulls out the names to include in the join. So here I am taking 50 unique names at random, but you can change this to any other selection criteria if you want.
Then you join those results back into your table. This links each of those 50 selected names back to all of the rows with a matching name for your final results. Finally ORDER BY a.name just to be sure all the rows for each name end up grouped together.
This should do it:
SELECT tA.*
FROM
testTable tA
INNER JOIN
(SELECT distinct name FROM testTable LIMIT 50) tB ON tA.name = tB.name
;