Select unique values(based on two columns) in group by mysql - mysql

I am new to SQL.
I have written following query in MySQL
select * from msgs where (msgs.toid = 1 or msgs.fid = 1) group by fid,toid;
this query returns values as shown below.
|message_id | toid | fid |
68 4 1
70 1 9
72 1 4
78 5 1
80 9 1
My expected result should be
|message_id | toid | fid |
72 1 4
78 5 1
80 9 1
It mean I should not get repeated values(based on two columns values toid,fid) and it should be the highest id value.
The combination of toid and fid should be unique.

You can use greatest(),least() to check whether another row with the same combination of id's and a higher message id does not exist (which means the selected row has the highest message id for the given combination).
select * from msgs m where (toid = 1 or fid = 1)
and not exists (
select 1 from msgs m2
where greatest(m2.toid,m2.fid) = greatest(m.toid,m.fid)
and least(m2.toid,m2.fid) = least(m.toid,m.fid)
and m2.message_id > m.message_id
)
EDIT - another way using row_number()
select * from (
select * ,
row_number() over (partition by greatest(toid,fid),
least(toid,fid) order by message_id desc) rn
from msgs m where (toid = 1 or fid = 1)
) t1 where rn = 1

You can use this query:
SELECT
MAX(message_id) AS message_id,
LEAST(toid, fid) AS toid,
GREATEST(toid, fid) AS fid
FROM
msgs
GROUP BY
LEAST(toid, fid) AS toid,
GREATEST(toid, fid) AS fid
yes it will return for example
80 1 9
instead of
80 9 1
but since (1,9) is equivalent to (9, 1) it should not be a problem. If it is, see FuzzyTree's answer.

SELECT *
FROM msgs m
WHERE NOT EXISTS ( SELECT 'a'
FROM msgs m2
WHERE m2.msg_id > m.msg_id
AND (
( m.toid = m2.toid
AND m.fid = m2.fid
)
OR ( m.toid = m2.fid
AND m.fid = m2.toid
)
)
)
AND (m.toid = 1 OR m.fid = 1)
Return the last messages for conversation between two user

You could also try this.
select * from msgs m
where (toid = 1 or fid = 1)
and not exists ( select 1 from msgs m2
where (m2.toid + m2.fid) = (m.toid + m.fid)
and m2.message_id > m.message_id
)

Related

get the total count but exclude certain condition

Hello I had this table:
id | user_id | status
1 | 34 | x
2 | 35 | x
3 | 42 | x
4 | 42 | y
My goal is to count the data with X status except if the user has a another data with Y status, it will exclude in the count. So instead of 3, it will only count 2 since the 3rd row has another data which is the 4th row with y status.
SELECT * FROM logs
AND user_id NOT IN (SELECT user_id FROM logs WHERE status = 'y')
GROUP BY user_id;
We can try the following aggregation approach:
SELECT COUNT(*) AS cnt
FROM
(
SELECT user_id
FROM logs
GROUP BY user_id
HAVING MIN(status) = MAX(status) AND
MIN(status) = 'x'
) t;
The above logic only counts a user having one or more records only having x status.
You can do it this way, I only modify a bit on your sql
SELECT COUNT(*) FROM (
SELECT u_id FROM tbl WHERE u_id NOT IN
(SELECT u_id FROM tbl WHERE status = 'y')
GROUP BY u_id
) as t
You can use inner join:
SELECT
count(t1.id) AS `cnt`
FROM
`test` AS t1,
`test` AS t2
WHERE
t2.`status`='y'
&& t1.`user_id` != t2.`user_id`;

mysql removing duplicates with where clause

i am trying to remove duplicate records having same hid values.
Here's the Fiddle:
https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=86e8ed00cf0a496da490eae5d7aae093
Table product_match_unmatches:
ID hid flag
1 1 1
2 1 1
3 2 1
4 2 1
5 1 2
6 2 2
7 2 2
8 1 1
9 1 1
10 2 1
Now I want to remove duplicates hid from the table but for flag = 1 only.
This query will remove all duplicates except for the recent one, but irrespective of flag values:
DELETE pmu1
FROM dmf_product_match_unmatches as pmu1
LEFT JOIN ( SELECT MAX(ID) as ID, hid
FROM dmf_product_match_unmatches as pmu2
GROUP BY hid) pmu3 USING (ID, hid)
WHERE pmu3.ID IS NULL;
I tried to add where clause flag = 1 in the above query but this is not producing desired result.
DELETE pmu1
FROM dmf_product_match_unmatches as pmu1
LEFT JOIN ( SELECT MAX(ID) as ID, hid
FROM dmf_product_match_unmatches as pmu2
where flag = 1
GROUP BY hid
) pmu3 USING (ID, hid)
WHERE pmu3.ID IS NOT NULL;
The required output is:
ID hid flag
5 1 2
6 2 2
7 2 2
9 1 1
10 2 1
Do you need in
DELETE t1
FROM dmf_product_match_unmatches t1
JOIN dmf_product_match_unmatches t2 USING (hid, flag)
WHERE flag = 1
AND t1.id < t2.id;
?
https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=a5e9e95335573ebedd45cdcd577b5602
Using row_number
DELETE pmu1
FROM dmf_product_match_unmatches pmu1
JOIN (select id,
row_number() over(partition by hid order by id desc) rn
from dmf_product_match_unmatches
where flag = 1
) as pmu3 ON pmu1.ID = pmu3.ID
WHERE pmu3.rn > 1;
In sql server you can use the EXCEPT set operator:
declare #flag int = 1
delete dmf_product_match_unmatches
where id in (
select id
from dmf_product_match_unmatches
where flag = #flag
except
select max(id) id
from dmf_product_match_unmatches
where flag = #flag
group by hid, flag
)
In mysql you can use NOT EXISTS
declare #flag int = 1
delete d1
from dmf_product_match_unmatches d1
where flag = #flag
and not exists (
select max(id) id
from dmf_product_match_unmatches d2
group by hid, flag
having d1.id = max(d2.id)
)

select rows with condition in other rows

I want select rows from my table with last status_Id if there is a row with status_Id = 2 for that rows
ticketStatus_Id ticket_Id status_Id
======================================
1 1 1
2 1 2 -
3 1 3 *
4 2 1
5 3 1
6 3 2 - *
7 4 1
8 4 2 -
9 4 3
10 4 4 *
I want select just rows 3, 6, 10. there are another rows with status_Id = 2 (rows 2, 6, 8) for that ticket_Id,
In other word How to select rows 3,6,10 with ticket_Id =1,3,4 that there are another row with these ticket_Ids and status_Id=2 (rows 2,6,8)
If you want the complete row, then I would view this as exists:
select t.*
from t
where exists (select 1
from t t2
where t2.ticket_id = t.ticket_id and t2.status_id = 2
) and
t.status_Id = (select max(t2.status_id)
from t t2
where t2.ticket_id = t.ticket_id
);
If you just want the ticket_id and status_id (and not the whole row), I would recommend aggregation:
select ticket_id, max(status_id)
from t
group by ticket_id
having sum(status_id = 2) > 0;
In your case, ticketStatus_Id seems to increase with status_id, so you can use:
select max(ticketStatus_Id) as ticketStatus_Id, ticket_id, max(status_id) as Status_Id
from t
group by ticket_id
having sum(status_id = 2) > 0;
First, for each ticket we get the row with the highest status. We can do this with a self-join. Each row is joined with the row with the next highest status. We select the rows which have no higher status, those will be the highest. Here's a more detailed explanation.
select ts1.*
from ticket_statuses ts1
left outer join ticket_statuses ts2
on ts1.ticket_Id = ts2.ticket_Id
and ts1.status_Id < ts2.status_Id
where ts2.ticketStatus_Id is null
3 1 3
4 2 1
6 3 2
10 4 4
11 5 3
Note that I've added a curve-ball of 11, 5, 3 to ensure we only select tickets with a status of 2, not greater than 2.
Then we can use that as a CTE (or subquery if you're not using MySQL 8) and select only those tickets who have a status of 2.
with max_statuses as (
select ts1.*
from ticket_statuses ts1
left outer join ticket_statuses ts2
on ts1.ticket_Id = ts2.ticket_Id
and ts1.status_Id < ts2.status_Id
where ts2.ticketStatus_Id is null
)
select ms.*
from max_statuses ms
join ticket_statuses ts
on ms.ticket_id = ts.ticket_id
and ts.status_id = 2;
3 1 3
6 3 2
10 4 4
This approach ensures we select the complete rows with the highest statuses and any extra data they may contain.
dbfiddle
This is basicaly a "last row per group" problem. You will find some solutions here. My prefered solution would be:
select t.*
from (
select max(ticketStatus_Id) as ticketStatus_Id
from mytable
group by ticket_Id
) tmax
join mytable t using(ticketStatus_Id)
The difference in your question is that you have a condition requiring a specific value within the group. This can be solved with a JOIN within the subquery:
select t.*
from (
select max(t1.ticketStatus_Id) as ticketStatus_Id
from mytable t2
join mytable t1 using(ticket_Id)
where t2.status_Id = 2
group by t2.ticket_Id
) tmax
join mytable t using(ticketStatus_Id)
Result:
| ticketStatus_Id | ticket_Id | status_Id |
| --------------- | --------- | --------- |
| 3 | 1 | 3 |
| 6 | 3 | 2 |
| 10 | 4 | 4 |
View on DB Fiddle
A solution using window functions could be:
select ticketStatus_Id, ticket_Id, status_Id
from (
select *
, row_number() over (partition by ticket_Id order by ticketStatus_Id desc) as rn
, bit_or(status_Id = 2) over (partition by ticket_Id) > 0 as has_status2
from mytable
) x
where has_status2 and rn = 1
A quite expressive way is to use EXISTS and NOT EXISTS subquery conditions:
select t.*
from mytable t
where exists (
select *
from mytable t1
where t1.ticket_Id = t.ticket_Id
and t1.status_Id = 2
)
and not exists (
select *
from mytable t1
where t1.ticket_Id = t.ticket_Id
and t1.ticketStatus_Id > t.ticketStatus_Id
)
SELECT a.*
FROM t a
JOIN
(
SELECT ticket_id, MAX(status_id) max_status_id
FROM t
WHERE status_id >= 2
GROUP BY ticket_id
) b
ON a.ticket_id = b.ticket_id
AND a.status_id = b.max_status_id;
SELECT
MAX(m1.ticketstatus_Id) as ticket_status,
m1.ticket_Id as ticket,
MAX(m1.status_Id) as status
FROM mytable m1
WHERE
m1.ticket_Id in (select m2.ticket_Id from mytable m2 where m2.ticket_Id=m1.ticket_Id and m2.status_Id=2)
GROUP BY m1.ticket_Id

selecting a table values in other table query in mysql

I have table 'printorders' :
id | faktor | status
1 | 326548768 | 6
2 | 657657875 | 5
and I have table 'delivery' :
id | faktorids
1 | 326548768-657657875
2 | 876876575-548548534
I want to select from delivery where faktors in faktorids all are equal to 6
and here is my query :
SELECT *,
(
SELECT status
FROM printorders
WHERE faktor = (SUBSTRING(faktorids, 1, 9))
LIMIT 1
) AS d1,
(
SELECT status
FROM printorders
WHERE faktor = (SUBSTRING(faktorids, 11, 9))
LIMIT 1
) AS d2
FROM delivery
WHERE d1= 6 AND d2 = 6
but it do not work for me, where is problem?
I want to select from delivery where faktors in faktorids all are
equal to 6
i assume you only want factor 326548768 because thats the only one with status 6?
I believe you want this query more or less but hard to say without expected results (query not tested).
SELECT
printorders__status__6.*
FROM (
SELECT
printorders.*
FROM
printorders
WHERE
printorders.status = 6
) AS printorders__status__6
INNER JOIN
delivery
ON
FIND_IN_SET(
printorders__status__6.faktor
, REPLACE(
delivery.faktorids
, '-'
, ','
)
)
First, you have an extra comma after d2
) AS d2,
FROM delivery
Also your phrase should look a little like this
select * from
(SELECT *,
(
SELECTstatus
FROM printorders
WHERE faktor = (SUBSTRING(faktorids, 1, 9)) AND status ='requested'
LIMIT 1
) AS d1,
(
SELECT status
FROM printorders
WHERE faktor = (SUBSTRING(faktorids, 11, 9))
LIMIT 1
) AS d2
FROM delivery) main
WHERE d1= 6 AND d2 = 6

Mysql select query count until reach the condition

I have lists of users with his points and game id. I need to find the rank of the specified user based on the game order by the maximum lb_point. Below is my table.
lb_id user_id game_id room_id lb_point
------------------------------------------------
1 1 2 1 670
2 1 1 2 200
3 1 2 2 650
4 1 1 1 400
5 3 2 1 700
6 4 2 5 450
7 2 1 3 550
8 2 1 1 100
9 1 1 1 200
I have already done this by using PHP code and its working fine as follows.
$game_id = 2;
$user_id = 1;
$query_rnk = $this->db->query('SELECT user_id AS uid FROM leader_board WHERE game_id = "'.$game_id.'" GROUP by user_id ORDER BY lb_point DESC');
if ($query_rnk->num_rows() > 0){
$j=1;
foreach($query_rnk->result() as $row_rnk){
if($row_rnk->uid == $user_id){
$rnk_status = 1;
break;
}
$j++;
}
if($rnk_status == 1){
$resp['rank'] = $j;
}
}
Answer is: 2.
But i need to find this by using one query. Any idea?
You could do with a select counting the number of rows of a proper select
select count(*)
from (
select distinct user_id
from leader_board
where lb_point >= (select max( lb_point )
from leader_board
where user_id = 1
and game_id = 2 )
and game_id = 2
) t
If you want to get the rank of user_id = 1, try this:
SELECT urank
FROM (
SELECT user_id AS uid, #rank := #rand + 1 AS urank
FROM leader_board
CROSS JOIN (SELECT #rank := 0) t
WHERE game_id = '$game_id'
GROUP by user_id
ORDER BY lb_point DESC
) tmp
WHERE uid = '$user_id'
SELECT count(*)+1 as Rank from (select lb_point from games where game_id =2 order by lb_point DESC) as list where list.lb_point > (select max(lb_point) from games where user_id = 1 and game_id = 2)
This will give the rank 2 for user_id = 1 in game_id = 2 which is the answer you've mentioned in question.