Fetching last 2 rows of multiple user in MySQL [duplicate] - mysql

This question already has answers here:
Grouping by a Top N in MySQL
(2 answers)
Closed 2 years ago.
I have a table to list the transaction made by users.
mysql> select * from transaction;
+-----------+----------+------------+
| emp_id_fk | trans_id | trans_date |
+-----------+----------+------------+
| 1 | 1 | 2008-01-01 |
| 1 | 2 | 2019-01-01 |
| 1 | 3 | 2020-01-01 |
| 2 | 4 | 2020-01-10 |
| 2 | 5 | 2020-01-16 |
| 2 | 6 | 2020-01-15 |
+-----------+----------+------------+
6 rows in set (0.00 sec)
I want to know the last 2 transactions made by the users along with their transaction ID.
The output should look something like this.
+-----------+----------+------------+
| emp_id_fk | trans_id | trans_date |
+-----------+----------+------------+
| 1 | 2 | 2019-01-01 |
| 1 | 3 | 2020-01-01 |
| 2 | 5 | 2020-01-16 |
| 2 | 6 | 2020-01-15 |
+-----------+----------+------------+
I've tried inner joins and group by clause but of no use. How can I generate this output?

If you are running MySQL 8.0, you can use window fuctions:
select *
from (
select t.*, row_number() over(partition by emp_id_fk order by trans_date desc) rn
from transactions t
) t
where rn <= 2
order by emp_id_fk, trans_date
If there may be more than one transaction for a given customer on the same date, consider adding another sorting criteria to the order by clause of the window function, such as trans_id for example:
row_number() over(partition by emp_id_fk order by trans_date desc, , trans_id) rn desc
In older versions, you could use a correlated subquery:
select t.*
from transactionts t
where (
select count(*)
from transactions t1
where t1.trans_date >= t.trans_date and t1.emp_id_fk = t.emp_id_fk
) <= 2

Related

How to select other columns of a table when grouping? [duplicate]

This question already has answers here:
SQL select only rows with max value on a column [duplicate]
(27 answers)
Closed 1 year ago.
Please assume this table:
// mytable
+--------+-------------+---------+
| num | business_id | user_id |
+--------+-------------+---------+
| 3 | 503 | 12 |
| 7 | 33 | 12 |
| 1 | 771 | 13 |
| 2 | 86 | 13 |
| 1 | 772 | 13 |
| 4 | 652 | 14 |
| 4 | 567 | 14 |
+--------+-------------+---------+
I need to group it based on user_id, So, here is my query:
select max(num), user_id from mytable
group by user_id
Here is the result:
// res
+--------+---------+
| num | user_id |
+--------+---------+
| 7 | 12 |
| 2 | 13 |
| 4 | 14 |
+--------+---------+
Now I need to also get the business_id of those rows. Here is the expected result:
// mytable
+--------+-------------+---------+
| num | business_id | user_id |
+--------+-------------+---------+
| 7 | 33 | 12 |
| 2 | 86 | 13 |
| 4 | 567 | 14 | -- This is selected randomly, because of the equality of values
+--------+-------------+---------+
Any idea how can I do that?
You don't group. You filter. One method uses window functions such as row_number():
select t.*
from (select t.*,
row_number() over (partition by user_id order by num desc) as seqnum
from mytable t
) t
where seqnum = 1;
Another method which can have slightly better performance with an index on (user_id, num) is a correlated subquery:
select t.*
from mytable t
where t.num = (select max(t2.num)
from mytable t2
where t2.user_id = t.user_id
);
You should think "group by" when you want to summarize rows. You should think "where" when you want to choose rows with particular characteristics.

Select non repeating non repeating columns base on another column [duplicate]

This question already has answers here:
How can I SELECT rows with MAX(Column value), PARTITION by another column in MYSQL?
(22 answers)
Closed 2 years ago.
So I have this table and I am trying to get the latest analysis_id
+----+---------+-------------+
| id | repo_id | analysis_id |
+----+---------+-------------+
| 1 | 20 | 3 |
+----+---------+-------------+
| 2 | 20 | 4 |
+----+---------+-------------+
| 3 | 20 | 5 |
+----+---------+-------------+
| 4 | 21 | 6 |
+----+---------+-------------+
| 5 | 22 | 7 |
+----+---------+-------------+
So how do I get the largest number from analysis_id without the repeating repo_id
+----+---------+-------------+
| id | repo_id | analysis_id |
+----+---------+-------------+
| 3 | 20 | 5 |
+----+---------+-------------+
| 4 | 21 | 6 |
+----+---------+-------------+
| 5 | 22 | 7 |
+----+---------+-------------+
A general MySQL 8+ friendly solution uses ROW_NUMBER:
WITH cte AS (
SELECT *, ROW_NUMBER() OVER (PARTITION BY repo_id ORDER BY analysis_id DESC) rn
FROM yourTable
)
SELECT id, repo_id, analysis_id
FROM cte
WHERE rn = 1;
You are looking for group by
SELECT MAX(id) ,repo_id ,MAX(analysis_id)
FROM YOUR_TABLE
GROUP BY repo_id
In MySQL 5+ you may use
SELECT *
FROM tablename t1
WHERE NOT EXISTS ( SELECT NULL
FROM tablename t2
WHERE t1.repo_id = t2.repo_id
AND t1.id < t2.id )

mysql select counter of current position [duplicate]

This question already has answers here:
Rank function in MySQL
(13 answers)
Closed 2 years ago.
Having a table like this
+--------+------------+
| orderID | productID |
+--------+------------+
| 100 | 10 |
| 100 | 11 |
| 101 | 10 |
| 101 | 11 |
| 101 | 12 |
+--------+------------+
I need to add a third column, which be counter of current position of product.
Like:
+--------+------------+--------------+
| orderID | productID | currentIndex |
+--------+------------+--------------+
| 100 | 10 | 1 |
| 100 | 11 | 2 |
| 101 | 10 | 1 |
| 101 | 11 | 2 |
| 101 | 12 | 3 |
+--------+------------+--------------+
Can help me, please?
I have now this query:
SELECT orderID, productID
FROM orders;
If you are running MySQL 8.0, `row_number() does exactly what you ask for:
select orderid, productid,
row_number() over(partition by orderid order by productid) currentindex
from orders;
In earlier versions, alternatives are either user variables, or a correlated subquery. I am going to recommend the second option - user variables are rather tricky to work with, and are now officially planned for future deprecation:
select orderid, productid,
(select count(*) from orders o1 where o1.orderid = o.orderid and o1.productid <= o.productid) currentindex
from orders o;

Sort a table but keep groups of rows together

How do I sort a table by it's minimum value per group but at the same time keep a group of rows together. Below a simple example of what i am trying to accomplish. The table is sorted by the lowest group value, but the group remains together. I am pretty sure this question has been asked already but i could not find an answer.
+---------+-------+
| Group | value |
+---------+-------+
| 1 | 3.99 |
| 1 | 10.99 |
| 3 | 12.69 |
| 1 | 20.95 |
| 2 | 19.95 |
| 3 | 10.09 |
+---------+-------+
Desired output
+---------+-------+
| Group | value |
+---------+-------+
| 1 | 3.99 |
| 1 | 10.99 |
| 1 | 20.95 |
| 3 | 10.69 |
| 3 | 12.09 |
| 2 | 19.95 |
+---------+-------+
If you are running MySQL 8.0, you can sort with window functions:
select t.*
from mytable t
order by min(value) over(partition by grp), value
In earlier versions, one option is to join an aggregate subquery:
select t.*
from mytable t
inner join (
select grp, min(value) min_value from mytable group by grp
) m on m.grp = t.grp
order by m.min_value, t.value
SELECT *,RN = ROW_NUMBER() OVER (PARTITION BY ID ORDER BY VALUE,ID) FROM TEMP

Select all rows where ID, but only the most recent where duplicates occur [duplicate]

This question already has answers here:
SQL select only rows with max value on a column [duplicate]
(27 answers)
Closed 5 years ago.
I have a table called Stats in my database:
| Game_ID | User_ID | Rank | Creation_date |
---------------------------------------------
| 1 | 1 | 1 | 2017-04-03 |
| 1 | 2 | 2 | 2017-04-03 |
| 1 | 3 | 3 | 2017-04-03 |
| 1 | 1 | 4 | 2017-05-03 |
And I currently use the following query to fetch all rows for a specific Game_ID:
"SELECT * FROM Stats WHERE Game_ID = 2 ORDER BY Rank ASC"
This returns exactly what is shown above, I would however like to return only one row per User_ID (The one with the most recent Creation_date), like this:
| Game_ID | User_ID | Rank | Creation_date |
---------------------------------------------
| 1 | 2 | 2 | 2017-04-03 |
| 1 | 3 | 3 | 2017-04-03 |
| 1 | 1 | 4 | 2017-05-03 |
Any help would be appreciated!
EDIT
I tried the solution above, and I'm certain it is the right one. I dont get duplicates anymore of the User_ID. However, I don't get the latest Creation_date. What am I missing?
The updated query:
SELECT a.Game_ID, a.User_ID, a.rank, a.Creation_date
FROM stats a
INNER JOIN (
SELECT User_ID, MAX(Creation_date), Creation_date
FROM stats
WHERE Game_ID = 2
GROUP BY User_ID
) b ON a.User_ID = b.User_ID AND a.Creation_date = b.Creation_date ORDER BY rank ASC;
Returns:
| Game_ID | User_ID | Rank | Creation_date |
---------------------------------------------
| 1 | 1 | 1 | 2017-04-03 |
| 1 | 2 | 2 | 2017-04-03 |
| 1 | 3 | 3 | 2017-04-03 |
In other words, not the row with the most recent Creation_date for User_ID 1.
Try this -
SELECT Game_ID, User_ID, MAX(Rank), Max(Creation_date)
FROM Stats
WHERE Game_ID = 2
GROUP BY Game_ID, User_ID, Creation_date
ORDER BY Rank, Creation_date