Select Unique Rows Based on Single Distinct Column - MySQL - 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.

Related

Mysql Join get Latest value of each group

Mysql version: 8.0.21
I am lookig of get the latest value of each "TableData" which has the type "fruit".
Table Name: TableNames
_________________________________________
| id | name | id_group | type |
|-----------------------------------------|
| 0 | AppleGroup | apple | fruit |
| 1 | BananaGroup | banana | fruit |
| 2 | OtherGroup | other | other |
Table Name: TableData
__________________________
| id | id_group | value |
|--------------------------|
| 0 | apple | 12 |
| 1 | banana | 8 |
| 2 | apple | 3 | <--get latest
| 3 | banana | 14 |
| 4 | banana | 4 | <--get latest
With this Query I get all the items, but I am looking for the lastest of each.
I already tried to group by and order by, but the problem is that I first need to order by and then group by, seems that's not possible in Mysql.
SELECT
n.name,
d.value
FROM TableNames n
INNER JOIN
(
SELECT *
FROM TableData
) d ON d.`id_group` = n.`id_group`
WHERE type = 'fruit'
Expected ouput:
_____________________
| name | value |
|---------------------|
| AppleGroup | 3 |
| BananaGroup | 4 |
Without ROW_NUMBER(), because you can be on an older version of MySQL (before 8.0), you can create an inner join with the max(id):
SELECT
TableNames.name,
TableData.value
FROM
TableData
INNER JOIN (
SELECT
id_group,
MAX(id) as max
FROM TableData
GROUP BY id_group) x ON x.id_group = TableData.id_group
INNER JOIN TableNames on TableNames.id_group = TableData.id_group
WHERE x.max = TableData.id
see: DBFIDDLE
On MySQL 8+, we can use ROW_NUMBER():
WITH cte AS (
SELECT tn.name, tn.id_group, td.value,
ROW_NUMBER() OVER (PARTITION BY td.id_group ORDER BY td.id DESC) rn
FROM TableNames tn
INNER JOIN TableData td
ON td.id_group = tn.id_group
WHERE tn.type = 'fruit'
)
SELECT name, value
FROM cte
WHERE rn = 1
ORDER BY id_group;
For optimization, you may consider adding the following index to the TableData table:
CREATE INDEX idx ON TableData (id_group);
Note that on InnoDB, MySQL will automatically include id at the end of the index, hence the index is (id_group, id). This should let MySQL efficiently do the join in the CTE and also compute ROW_NUMBER.

MySQL NOT IN Select

I have a following table in my project
+----+--------+-----------+-----------+
| id | old_id | op_status | tr_status |
+----+--------+-----------+-----------+
| 1 | | issue | Approved |
| 2 | | issue | Approved |
| 3 | | issue | Approved |
| 4 | 1 | issue | Issued |
| 5 | 3 | issue | Issued |
+----+--------+-----------+-----------+
I want to select records WHERE tr_status='Approved' and NOT IN id in the old_row_id. In this example no need to select id(s) 1 and 3 that are in old_row_id as the following result.
+----+--------+-----------+-----------+
| id | old_id | op_status | tr_status |
+----+--------+-----------+-----------+
| 2 | | issue | Approved |
+----+--------+-----------+-----------+
I used the following query.
SELECT id, old_row_id, op_status, tr_status FROM table WHERE id NOT IN (old_row_id).
But outs the following result.
+----+--------+-----------+-----------+
| id | old_id | op_status | tr_status |
+----+--------+-----------+-----------+
| 1 | | issue | Approved |
| 2 | | issue | Approved |
| 3 | | issue | Approved |
+----+--------+-----------+-----------+
What may be wrong with me ? can anyone help me ?
I would phrase your query using exists logic:
SELECT t1.id, t1.old_id, t1.op_status, t1.tr_status
FROM yourTable t1
WHERE
t1.tr_status = 'Approved' AND
NOT EXISTS (SELECT 1 FROM yourTable t2 WHERE t2.old_id = t1.id);
Demo
Try doing something like this:
SELECT id, old_id, op_status, tr_status
FROM table
WHERE id NOT IN (SELECT DISTINCT old_id FROM table)
AND tr_status = 'Approved'
You will want to left join the table to itself on the old ID, then eliminate the records where there is a match.
For example:
SELECT A.id, A.old_row_id, A.op_status, A.tr_status
FROM table A
LEFT JOIN table B ON A.id = B.old_row_id
WHERE B.id IS NULL
AND A.tr_status = 'Approved';
In they way you've tried to solve it, you're matching the old_id with the value of the row itself. You must derivate the table to create a cartesian product:
SELECT id, old_id, op_status, tr_status
FROM table
WHERE id NOT IN (SELECT IFNULL(old_id, 0) FROM table)
AND tr_status = 'Approved'
Also IFNULL to also include those record in the subquery, otherwise can't be compared with null

Duplicate and get the last item from the mysql table

table 1 t1
+----+----------+
| id | name |
+----+----------+
| 1 | free |
| 2 | basic |
| 3 | advanced |
+----+----------+
table 2 t2
+----+-------+------+
| id | t1_fk | cost |
+----+-------+------+
| 1 | 2 | 1650 |
| 3 | 3 | 2000 |
| 4 | 2 | 550 |
+----+-------+------+
I want to get the output of t2 table but without duplicates. I was able to get this using GROUP BY function. Also i need the last item on the duplicate (i got stuck here).
Here's what i tried and it didn't work.
SELECT id cost FROM t2 GROUP BY t1_fk ORDER BY MAX(id) DESC
any help
On MySQL 8+, we can use ROW_NUMBER here:
WITH cte AS (
SELECT *, ROW_NUMBER() OVER (PARTITION BY t1_fk ORDER BY id DESC) rn
FROM t2
)
SELECT id, t1_fk, cost
FROM cte
WHERE rn = 1;
On earlier versions of MySQL, one canonical way to handle this would be to use a join to a subquery which finds the max id value for each t1_fk:
SELECT a.id, a.t1_fk, a.cost
FROM t2 a
INNER JOIN
(
SELECT t1_fk, MAX(id) AS max_id
FROM t2
GROUP BY t1_fk
) b
ON a.t1_fk = b.t1_fk AND a.id = b.max_id;

Select the lastest one of each result in 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

How to query last records

so, if i have this table:
| ID | Date | Status | Value |
| 1 | 2-2-2012 | A | 5 |
| 2 | 3-4-2012 | B | 3 |
| 1 | 5-6-2012 | C | 1 |
| 2 | 1-1-2012 | D | 4 |
and I need to get total value and "most recent" status for every IDs, how to do the query? i tried using group by , but the somehow only oldest status shown in the query result.
I need to get the data became like this:
| ID | Date | Status |sum(Value)|
| 2 | 3-4-2012 | B | 7 |
| 1 | 5-6-2012 | C | 6 |
i'm a total newbie in this SQL thing, not an IT person, just because my boss ask to extract some data from our database....
thanks in advance...
Since you have not mentioned any RDBMs, the query below works on almost all RDBMS.
This uses a subquery which separately gets the latest date (assuming that the data type of date is really stored as DATE and not as a string) for every ID. The result of the subquery is then joined back on the table itself in order to get the other columns.
SELECT a.ID, a.Date, a.Status, b.TotalSum
FROM tableName a
INNER JOIN
(
SELECT ID, MAX(date) max_date, SUM(Value) totalSum
FROM tableName
GROUP BY ID
) b ON a.ID = b.ID AND
a.date = b.max_date
SQLFiddle Demo
If you are using mysql then this will work
SELECT id,date,status,sum(value)
FROM (select * from yourTable order by DATE desc ) t
group by ID
order by ID desc