How to make a query to GROUP BY x DESC - mysql

The following SELECT statement
select *
from messages
where receiverID = '5'
group BY senderID
order by id DESC
database:
id | senderID | receiverID | message
1 | 245 | 5 | test 1
2 | 89 | 5 | test 2
3 | 79 | 5 | test 3
4 | 245 | 5 | test 4
5 | 245 | 5 | test 5
For senderID=245 I expected to return the row with id=5 , but it dosent it returns row with id=1, but i want the last row. How to achieve that ?
returns:
id | senderID | receiverID | message
1 | 245 | 5 | test 1
2 | 89 | 5 | test 2
3 | 79 | 5 | test 3
Ohh I made it :D
so this is the code that worked,for anyone with similar question
SELECT * FROM ( SELECT * FROM messages WHERE
receiverID = '5' ORDER BY id DESC) AS m GROUP BY senderID ORDER BY id DESC

This is not possible. You have to do something like:
[...] WHERE `id` = (SELECT MAX(`id`) FROM `messages` WHERE `receiverID` = '5')

Personally I'd consider a subquery, something along the lines of this should do the job for you
SELECT messagesOrdered.*
FROM (
SELECT *
FROM messages
WHERE receiverID = '5'
ORDER BY id DESC
) AS messagesOrdered
GROUP BY senderID
You may wish to check what keys you have set up depending on how large the table is.
The problem with using MAX is that if you use MAX on the id field then it will get the number you are looking for, however using MAX on another field does not get the data that matches that id. Using the subquery method, the inner query is doing the sorting and then the GROUP on the outside will group based on the order of rows in the inner query.

SELECT * FROM messages m
JOIN
( SELECT senderID, MAX(id) AS last
FROM messages
WHERE receiverID = '5'
GROUP BY senderID ) mg
ON m.id = mg.last

Not sure I understand your question completely, but it sounds to me like you want:
select max(id),
senderId,
max(receiverId),
max(message)
from messages
where receiverID = '5'
group BY senderID
order by id DESC
Note that you need to include message into your aggregate as well, otherwise you'll get unpredicatable results (other DBMS wouldn't allow leaving out the max(message) but MySQL will simply return a random row from the group).

Here it goes mine :)
select m1.* from messages m1
left join messages m2
on m1.senderid = m2.senderid and m1.id < m2.id
where m2.id is null and receiverID = '5'
Given your example this would return:
+----+----------+------------+---------+
| ID | SENDERID | RECEIVERID | MESSAGE |
+----+----------+------------+---------+
| 2 | 89 | 5 | test 2 |
| 3 | 79 | 5 | test 3 |
| 5 | 245 | 5 | test 5 |
+----+----------+------------+---------+

Related

How to get n rows of records of given m ids in Mysql by id desc

Suppose i have a table like this
id | user_id | rating
1 | 500 | 5
2 | 501 | 3
3 | 500 | 5
4 | 502 | 4
5 | 502 | 1
How can i write a mysql query to find the last 10 records for each id of given three ids (500, 501,502) by id desc
assuming your id is an auto increment column and the three ids (500, 501,502) are for user_id
then you could use
select *
from my_table
where user_id in (500, 501,502)
order by id desc
limit 10
Being a bit lazy here's a way to get the last 2 per user_id by using a variable to allocate a row number joined to the max occurrences per user
DROP TABLE IF EXISTS T;
CREATE TABLE T
(ID INT AUTO_INCREMENT PRIMARY KEY, USER_ID INT, RATING INT);
INSERT INTO T (USER_ID,RATING) VALUES
(500,1),
(502,1),
(500,2),(500,3),
(502,2),
(600,1);
SELECT U.*
FROM
(
SELECT S.ID,S.USER_ID,S.RATING,
T.MAXROWS
FROM
(
SELECT ID,USER_ID,RATING,
IF(USER_ID <> #P,#R:=1,#R:=#R+1) RN,
#P:=USER_ID
FROM T
ORDER BY USER_ID ASC,ID ASC
) S
JOIN
(SELECT USER_ID,COUNT(*) MAXROWS FROM T GROUP BY USER_ID) T ON T.USER_ID = S.USER_ID
) U
WHERE U.RATING > U.MAXROWS - 2 AND U.USER_ID IN(500,502);
+----+---------+--------+---------+
| ID | USER_ID | RATING | MAXROWS |
+----+---------+--------+---------+
| 3 | 500 | 2 | 3 |
| 4 | 500 | 3 | 3 |
| 2 | 502 | 1 | 2 |
| 5 | 502 | 2 | 2 |
+----+---------+--------+---------+
4 rows in set (0.00 sec)
You can use UNION ALL operator to get 10 result of each id
(select * from test
where user_id =500
order by id desc
limit 10)
UNION ALL
(select * from test
where user_id =501
order by id desc
limit 10)
UNION ALL
(select * from test
where user_id =502
order by id desc
limit 10)
order by id desc;
Check here for DEMO
NOTE: Reviewer are welcome to enhance or improve the query.

How to group by unique user ID and sort by latest messages ID? [duplicate]

This question already has answers here:
SQL select only rows with max value on a column [duplicate]
(27 answers)
Closed 4 years ago.
my message table
message_id | message_body_content | sender_user_id | receiver_user_id
1 | hello world | 1000 | 1200
2 | hello world 2 | 1001 | 1200
3 | hello world again | 1000 | 1200
4 | xyz | 1001 | 1200
5 | abc | 1200 | 1999
I want to group by my message table by sender_user_id unique and by last message_id
so output should be like;
message_id | message_body_content | sender_user_id | receiver_user_id
3 | hello world again | 1000 | 1200
4 | xyz | 1001 | 1200
Because latest message_id for sender_user_id IS 3. This is in theory.
When I try
SELECT * FROM message WHERE receiver_user_id = 1200
GROUP BY sender_user_id
ORDER BY message_id DESC
It did not work. Because MySQL grouping first by message_id (min message_id value)
then sorting.
How can I get unique sender message by max message_id
I don't think you want to "group". You want to filter. So, I think you want:
SELECT m.*
FROM message m
WHERE m.receiver_user_id = 1200 AND
m.message_id = (SELECT MAX(m2.message_d)
FROM message m2
WHERE m2.receiver_user_id = m.receiver_user_id AND
m2.sender_user_id = m.sender_user_id
)
ORDER BY m.message_id DESC;
select message_id, message_body_content, sender_user_id, receiver_user_id
from message t
where t.message_id = (select max(t1.message_id)
from message t1
where t1.sender_user_id = t.sender_user_id
)
order by message_id;
you can use sub-query
select m.* from message m
where (m.message_id,m.sender_user_id) in(
SELECT max(message_id) as message_id ,sender_user_id
FROM message
GROUP BY sender_user_id
) and m.receiver_user_id = 1200
ORDER BY m.message_id DESC

sql sorting inside group by with two column

My table:
id | request | subject | date
1 | 5 | 1 | 576677
2 | 2 | 3 | 576698
3 | 5 | 1 | 576999
4 | 2 | 3 | 586999
Need to select unique records by two columns(request,subject) with showing last inserted id's.
My query:
SELECT *,MAX(id)
FROM `tbl`
GROUP BY CONCAT(`request_id`, `subject_id`)
HAVING (COUNT(`request_id`)>1 order by MAX(id) desc
But results:
id | request | subject | date
2 | 2 | 3 | 576698
1 | 5 | 1 | 576677
How to get records with id's 3 and 4 ?
Try this:
SELECT MIN(id) id, request, subject, MAX(`date`) `date`
FROM `tbl`
GROUP BY request, subject;
See it run on SQL Fiddle.
You can try this.
SELECT T.*
FROM T
INNER JOIN
(
SELECT MAX(`ID`) as ID,`request`,`subject`
FROM T
GROUP BY `request`,`subject`
HAVING COUNT(`ID`) > 1
)AS T1 ON T.ID = T1.ID
SQLFiddle
Thanks for all. My result is
SELECT MAX(id), id, request, subject, date
FROM `tbl`
GROUP BY request, subject having count(request)>1 order by MAX(id) desc

Return latest entry result providing there is a review or close entry

I have a table that holds the answers to a question which is asked at entry to the system, at review periods and then at closure. The client can be opened and closed multiple times during their life on the system.
I am trying to get the latest 'entry' result from the table which also has either an associated 'review' or 'close' result.
This is my table (I have just included 1 user but the actual table has thousands of users):
row | user_id | answer | type | date_entered |
----+---------+--------+--------+--------------+
1 | 12 | 3 | entry | 2016-03-13 |
2 | 12 | 1 | review | 2016-03-14 |
3 | 12 | 7 | review | 2016-03-16 |
4 | 12 | 7 | close | 2016-03-17 |
5 | 12 | 8 | entry | 2016-03-20 |
6 | 12 | 2 | review | 2016-03-21 |
7 | 12 | 3 | close | 2016-03-22 |
8 | 12 | 1 | entry | 2016-03-28 |
So for this table the query would just return row 5 because the 'entry' on row 8 doesn't have any 'review' or 'closure' records after it.
Hopefully that makes sense.
SELECT a.*
FROM my_table a
JOIN
( SELECT x.user_id
, MAX(x.date_entered) date_entered
FROM my_table x
JOIN my_table y
ON y.user_id = x.user_id
AND y.date_entered > x.date_entered
AND y.type IN ('review','close')
WHERE x.type = 'entry'
GROUP
BY x.user_id
) b
ON b.user_id = a.user_id
AND b.date_entered = a.date_entered;
Basically you can seperate your query into two sub-queries. First query should get lastest record id (review and closure). Second query should have row_id > found_id.
SELECT *
FROM my_table
WHERE type = 'entry'
AND row_id > (SELECT Max(row_id)
FROM my_table
WHERE ( type = 'review'
OR type = 'close' ))
Please be careful about that; subquery may return zero-set.
I could think of several ways of doing it. But first a note: your date_entered field seems to be just a date. To tell which occurs "later" I'm going to use row because e.g. if both entry and review occurred on the same date, it's not possible to tell from the date_entered which one was later.
I just list a couple of solutions. The first one might be more efficient, but you should measure.
Here's a join against a subquery:
SELECT
m1.*
FROM
mytable m1
JOIN (SELECT
row, user_id
FROM
mytable
WHERE
type IN ('review', 'close') AND
user_id = 12
ORDER BY row DESC LIMIT 1) m2 ON m1.user_id = m2.user_id
WHERE
m1.user_id = 12 AND
m1.row < m2.row
ORDER BY
row DESC LIMIT 1
Here's a subquery for max:
SELECT
*
FROM
mytable
WHERE
row = (SELECT
MAX(m1.row)
FROM
mytable m1,
mytable m2
WHERE
m1.user_id = m2.user_id AND
m1.type = 'entry' AND
m2.type IN ('review', 'close') AND
m1.row < MAX(m2.row))

SQL get last messages from/to certain user

I'm trying to make SQL query from table,
- get last messages from all pairs of users with user 36 as sender or recipient, and join them with users table to get names. I've managed to create something like this, but still want to ask if there is more simple | efficient solution.
Mysql version - 5.5.31
table: messages
fields:
sender_user_id, recipient_user_id, date, text
query:
SELECT
*
FROM
(SELECT
(CASE sender_user_id > recipient_user_id
WHEN true THEN CONCAT(recipient_user_id, '|', sender_user_id)
WHEN false THEN CONCAT(sender_user_id, '|', recipient_user_id)
END) as hash,
sender_user_id,
recipient_user_id,
date,
text,
u.first_name
FROM
fed_messages
LEFT JOIN fed_users as u ON ((fed_messages.sender_user_id = 36 AND fed_messages.recipient_user_id = u.id) OR (fed_messages.recipient_user_id = 36 AND fed_messages.sender_user_id = u.id))
WHERE
sender_user_id = 36 OR recipient_user_id = 36
ORDER BY date DESC) as main
GROUP BY hash;
Thanks.
Upd. Data from sample messages table:
mysql> SELECT id, sender_user_id, recipient_user_id, text, date FROM federation.fed_messages WHERE id > 257;
-----+----------------+-------------------+-----------------+---------------------+
| id | sender_user_id | recipient_user_id | text | date |
+-----+----------------+-------------------+-----------------+---------------------+
| 258 | 40 | 36 | and one more | 2013-06-06 10:57:17 |
| 259 | 36 | 38 | another message | 2013-06-06 11:03:49 |
| 260 | 38 | 36 | some mes | 2013-06-06 12:29:33 |
| 261 | 38 | 36 | message | 2013-06-06 12:29:53 |
| 262 | 36 | 38 | message | 2013-06-06 12:47:26 |
| 263 | 36 | 40 | some message | 2013-06-10 16:22:46 |
The result should be with ids - 262, 263
I'm using SQL Server 2008, you don't say which database you are using.
From the information you have supplied your query seems overly complex for the output you require. Here's a simple query to get all the messages involving user 36:
SELECT
sender.msg_user_name AS sender_user_name
,recipient.msg_user_name AS recipient_user_name
,msg_date
,msg_text
FROM
dbo.Fed_Messages
INNER JOIN dbo.Fed_User AS sender
ON sender.msg_user_id = sender_user_id
INNER JOIN dbo.Fed_User AS recipient
ON recipient.msg_user_id = recipient_user_id
WHERE
sender_user_id = 36
OR recipient_user_id = 36
ORDER BY
msg_date DESC
I've had to change some field names as in SQL Server some of the names you have chosen are reserved words.
SQL Fiddle: http://sqlfiddle.com/#!3/b8e88/1
EDIT:
Now you've added some more information, and shown there is an id field on the message table, you could use something like this (note: I have SQL Server so you will probably have to change the query for MySQL):
SELECT sender.msg_user_name AS sender_user_name
,recipient.msg_user_name AS recipient_user_name
,msg_date
,msg_text
FROM dbo.Fed_Messages
INNER JOIN dbo.Fed_User AS sender ON sender.msg_user_id = sender_user_id
INNER JOIN dbo.Fed_User AS recipient ON recipient.msg_user_id = recipient_user_id
INNER JOIN ( SELECT MAX(id) AS most_recent_message_id
FROM dbo.Fed_Messages
GROUP BY CASE WHEN sender_user_id > recipient_user_id
THEN recipient_user_id
ELSE sender_user_id
END -- low_id
,CASE WHEN sender_user_id < recipient_user_id
THEN recipient_user_id
ELSE sender_user_id
END -- high_id
) T ON T.most_recent_message_id = dbo.Fed_Messages.id
WHERE sender_user_id = 36
OR recipient_user_id = 36
ORDER BY msg_date DESC
The SELECT in the FROM part of the query finds the most recent message (based on the id, I'm assuming it's an auto incrementing number) for each ordered pair of sender/recipient user id's. The result of that is rejoined to the Fed_Messages table to ensure we get the names for sender/receiver correct.
Updated SQL Fiddle: http://sqlfiddle.com/#!3/1f07a/2