I have a messages table that can contain the primary message, as well as sub-messages. What i want to achieve is only get the top 2 most recent sub-messages.
SELECT message.id, message.date_sent,message.object_id, message.content,
SubMessage1.content, SubMessage1.id, SubMessage1.date_sent,
SubMessage2.content, SubMessage2.id, SubMessage2.date_sent
FROM messages As Message
LEFT
OUTER
JOIN ( SELECT messages.object_id as object_id, messages.content as content,
messages.id as id , messages.date_sent as date_sent
FROM messages
ORDER
BY messages.date_sent ASC LIMIT 1,1
) as SubMessage1
ON Message.id = SubMessage1.object_id
LEFT
OUTER
JOIN ( SELECT messages.object_id as object_id, messages.content as content,
messages.id as id , messages.date_sent as date_sent
FROM messages
ORDER
BY messages.date_sent ASC LIMIT 2,1
) as SubMessage2
ON Message.id = SubMessage2.object_id
WHERE Message.id = 1
When I remove the limit, I always get the first result. But when I put in the limit I get null values. Any ideas? Suggestions?
SELECT * FROM messages WHERE id = 1
Union all
SELECT * FROM messages WHERE object_id=1
ORDER BY date_sent ASC LIMIT 2
The problem here is that the ORDER BY ... LIMIT ... in the subqueries is being applied before the ON Message.id = ... .object_id in the joins. As a result, the joins only have the chance to examine one message and determine if it's a submessage. Most of the time, it won't be, so you'll get NULL values.
To fix this, you need to move the content of your ON-clauses into WHERE-clauses inside the subquery. Normally that would be a bit of a mess, but since you're only looking to retrieve a single-record result-set, it's not too bad:
SELECT message.id, message.date_sent, message.object_id, message.content,
SubMessage1.content, SubMessage1.id, SubMessage1.date_sent,
SubMessage2.content, SubMessage2.id, SubMessage2.date_sent
FROM messages AS Message
LEFT
OUTER
JOIN ( SELECT messages.object_id AS object_id, messages.content AS content,
messages.id AS id, messages.date_sent AS date_sent
FROM messages
WHERE messages.object_id = 1
ORDER
BY messages.date_sent ASC LIMIT 1,1
) AS SubMessage1
ON TRUE
LEFT
OUTER
JOIN ( SELECT messages.object_id AS object_id, messages.content AS content,
messages.id AS id, messages.date_sent AS date_sent
FROM messages
WHERE messages.object_id = 1
ORDER
BY messages.date_sent ASC LIMIT 2,1
) AS SubMessage2
ON TRUE
WHERE Message.id = 1
;
(Note that this includes the parameter three times in the query: less than ideal, perhaps, but really not a problem.)
If you want 2 rows, use
LIMIT 2
In SQL land, counting is from 1 not 0.
Also, in mysql LIMIT with 2 params is:
LIMIT offset, row_count
But with 1 param it's:
LIMIT row_count
I'd like to meet the genius who decided to move the param like that and kick his in the pants.
The LIMIT command's starting index is 0. You may want to change your limits to (0, 1) and (1, 1).
Related
How can I display the records received from this query in ASC?
SELECT accounts.Nickname, chat.AccountID, chat.Message, chat.DateTime, accounts.Color
FROM (chat INNER JOIN accounts ON chat.AccountID = accounts.AccountID)
ORDER BY chat.MessageID DESC
LIMIT 100
Put your original query in a derived table (the subquery), and order its result in ASC order.
SELECT * FROM
(
SELECT accounts.Nickname, chat.AccountID, chat.Message, chat.DateTime, accounts.Color, chat.MessageID
FROM (chat INNER JOIN accounts ON chat.AccountID = accounts.AccountID)
ORDER BY chat.MessageID DESC
LIMIT 100
) dt
ORDER BY MessageID ASC
My query is below
SELECT *
FROM
(
SELECT
usr.*,
messages.message_text, messages.message_id
FROM
`user` as usr
LEFT JOIN
(
SELECT
message.*
FROM
message AS message
ORDER BY
message.updated_at
DESC
) AS messages
ON
`usr`.`user_id` = `messages`.`sender_id` OR `usr`.`user_id` = `messages`.`receiver_id`
WHERE
`usr`.`is_delete` = 0 AND `usr`.`is_active` = 1
ORDER BY
messages.updated_at
DESC
) AS result
GROUP BY
result.user_id
if i remove the group by then it work well , but i want the result with group by Please Help.
I have one message table in that i have saved the sender id and receiver id, both id have foreign key with user table and i want the recent message send by user and user details
So for latest message i am using order by desc and after getting all the messages with descending order i am using group by but it is not working.
It was very hard making the table schemas and the data insert in them which would act like your scenario. So next time, please provide a fiddle link to your schema/situation.
Here were the issues in your query:
You dont need a subquery after LEFT JOIN
ORDER BY clause is always after GROUP BY
You must mention all the columns that you are SELECTing in the GROUP BY clause.
Tweaked your query a bit:
SELECT *
FROM
(
SELECT
usr.*,
messages.message_text, messages.message_id, messages.updated_at
FROM
user as usr
LEFT JOIN message messages
--(
-- SELECT
-- message.*
-- FROM
-- message AS message
-- ORDER BY
-- message.updated_at
-- DESC
--) AS messages
ON
usr.user_id = messages.sender_id OR usr.user_id = messages.receiver_id
WHERE
usr.is_delete = 0 AND usr.is_active = 1
--ORDER BY
--messages.updated_at
--DESC
) AS result
GROUP BY
result.user_id,result.name,result.is_delete,result.is_active,result.message_text,result.message_id,updated_at
ORDER BY
result.updated_at
DESC
Hope it helps :)
I have a relatively basic query that fetches the most recent messages per conversation:
SELECT `message`.`conversation_id`, MAX(`message`.`add_time`) AS `max_add_time`
FROM `message`
LEFT JOIN `conversation` ON `message`.`conversation_id` = `conversation`.`id`
WHERE ((`conversation`.`receiver_user_id` = 1 AND `conversation`.`status` != -2)
OR (`conversation`.`sender_user_id` = 1 AND `conversation`.`status` != -1))
GROUP BY `conversation_id`
ORDER BY `max_add_time` DESC
LIMIT 12
The message table contains more than 911000 records, the conversation table contains around 680000. The execution time for this query, varies between 4 and 10 seconds, depending on the load on the server. Which is far too long.
Below is a screenshot of the EXPLAIN result:
The cause is apparently the MAX and/or the GROUP BY, because the following similar query only takes 10ms:
SELECT COUNT(*)
FROM `message`
LEFT JOIN `conversation` ON `message`.`conversation_id` = `conversation`.`id`
WHERE (`message`.`status`=0)
AND (`message`.`user_id` <> 1)
AND ((`conversation`.`sender_user_id` = 1 OR `conversation`.`receiver_user_id` = 1))
The corresponding EXPLAIN result:
I have tried adding different indices to both tables without any improvement, for example: conv_msg_idx(add_time, conversation_id) on message which seems to be used according to the first EXPLAIN result, however the query still takes around 10 seconds to execute.
Any help improving the indices or query to get the execution time down would be greatly appreciated.
EDIT:
I have changed the query to use an INNER JOIN:
SELECT `message`.`conversation_id`, MAX(`message`.`add_time`) AS `max_add_time`
FROM `message`
INNER JOIN `conversation` ON `message`.`conversation_id` = `conversation`.`id`
WHERE ((`conversation`.`receiver_user_id` = 1 AND `conversation`.`status` != -2)
OR (`conversation`.`sender_user_id` = 1 AND `conversation`.`status` != -1))
GROUP BY `conversation_id`
ORDER BY `max_add_time` DESC
LIMIT 12
But the execution time is still ~ 6 seconds.
You should create Multiple-Column Index on the columns which are in your WHERE clause and which you want to SELECT (except conversation_id). (reference)
conversation_id should be an index in both table.
Try to avoid 'Or' in Sql query this will make the fetching slow. Instead use union or any other methods.
SELECT message.conversation_id, MAX(message.add_time) AS max_add_time FROM message INNER JOIN conversation ON message.conversation_id = conversation.id WHERE (conversation.sender_user_id = 1 AND conversation.status != -1)) GROUP BY conversation_id
union
SELECT message.conversation_id, MAX(message.add_time) AS max_add_time FROM message INNER JOIN conversation ON message.conversation_id = conversation.id WHERE ((conversation.receiver_user_id = 1 AND conversation.status != -2) ) GROUP BY conversation_id ORDER BY max_add_time DESC LIMIT 12
Instead of depending on a single table message, have two tables: One for message, as you have, plus another thread that keeps the status of the thread of messages.
Yes, that requires a little more work when adding a new message -- update a column or two in thread.
But it eliminates the GROUP BY and MAX that are causing grief in this query.
While doing this split, see if some other columns would be better off in the new table.
SELECT `message`.`conversation_id`, MAX(`message`.`add_time`) AS `max_add_time`
FROM `message`
INNER JOIN `conversation` ON `message`.`conversation_id` = `conversation`.`id`
WHERE ((`conversation`.`receiver_user_id` = 1 AND `conversation`.`status` != -2)
OR (`conversation`.`sender_user_id` = 1 AND `conversation`.`status` != -1))
GROUP BY `conversation_id`
ORDER BY `max_add_time` DESC
LIMIT 12
You can try with INNER JOIN, if your logic not get affect using it.
you can modify this query by avoiding max() use
select * from(
select row_number() over(partition by conversation_id order by add_time desc)p1
)t1 where t1.p1=1
I have tried to program a inbox that display messages in the order they were received and then by if they have been read or not, it seemed to work for a while, but not it doesn't. It may have only worked under certain circumstances maybe..
Anyway here is my query;
SELECT `id`, `from_userid`, `read`, max(sent) AS sent
FROM (`who_messages`)
WHERE `to_userid` = '41'
GROUP BY `from_userid`
ORDER BY `read` ASC, `sent` DESC
I believe the problem is that the messages are being grouped in the wrong order.. as the inbox is always showing as read, when new messages exist. I get the right time of the new messages, but I am guessing this because I selected max(sent).
Is my logic wrong? or can I sort and then group as all my efforts have resulted in 'Every derived table must have its own alias'
Setup an SQL Fiddle - here's the best I came up with. Basically I do the ordering first in a sub-query then group them afterwards. That seemed to work with the (limited) test data I entered.
SELECT *
FROM (SELECT id, from_userid, is_read, sent
FROM who_messages
WHERE to_userid = 41
ORDER BY from_userid ASC, is_read ASC) m
GROUP BY m.from_userid
ORDER BY m.is_read ASC, m.sent DESC
See the fiddle to play around: http://sqlfiddle.com/#!2/4f63d/8
You are selecting non-grouping fields in a grouped query. It is not guaranteed which record of the group will be returned, and ORDER BY is processed after GROUP BY.
Try this:
SELECT m.*
FROM (
SELECT DISTINCT from_userid
FROM who_messages
WHERE to_userid = 41
) md
JOIN who_messages m
ON m.id =
(
SELECT mi.id
FROM who_message mi
WHERE (mi.to_userid, mi.from_userid) = (41, md.from_userid)
ORDER BY
mi.sent DESC, mi.id DESC
LIMIT 1
)
Create an index on who_message (to_userid, from_userid, sent, id) for this to work fast.
Update
The above query will return the record for the last message from any given user (including its read status). If you want to check that you have any unread messages from the user, use this:
SELECT m.*, md.all_read
FROM (
SELECT from_userid, MIN(read) AS all_read
FROM who_messages
WHERE to_userid = 41
GROUP BY
from_userid
) md
JOIN who_messages m
ON m.id =
(
SELECT mi.id
FROM who_message mi
WHERE (mi.to_userid, mi.from_userid) = (41, md.from_userid)
ORDER BY
mi.sent DESC, mi.id DESC
LIMIT 1
)
For this to work fast, create an index on who_message (to_userid, from_userid, read) (in addition to the previous index).
As Quassnoi said, you are using a GROUP BY query and ordering on 'read' which is not an aggregate function. Therefore you can't be certain of the value used by the MySQL engine (usually the last of the group but...)
I would suggest writing your query this way, as it doesn't involve any subquery and has some many other performance-friendly usage:
SELECT
from_userid,
COUNT(*) AS nb_messages,
SUM(NOT is_read) AS nb_unread_messages,
MAX(sent) AS last_sent
FROM who_messages
WHERE to_userid = 41
GROUP BY from_userid
ORDER BY nb_unread_messages DESC, last_sent DESC;
(I used Andy Jones' fiddle schema: http://sqlfiddle.com/#!2/4f63d/8.
By the way, many thanks Andy, this site is great !)
Hope this help !
"inbox that display messages in the order they were received and then by if they have been read or not ... however it is suppose to be the latest message" - assumes read is a nullable date/time column, and messages are stored in the order they are sent (newer have larger id than older - autoid)
SELECT wm.id, wm.from_userid, (wm.read IS NULL) as unread, wm.sent
FROM (SELECT MAX(id) AS id FROM who_messages WHERE to_userid = '41' GROUP BY from_userid) sub
INNER JOIN who_messages wm ON sub.id = wm.id
ORDER BY wm.sent DESC, wm.read
I want to select the last 10 rows but the order should remain the asc only.
Its like displaying the last page in a forum but the post are still aligned in the right order.
I tried to do this
SELECT users.display,appreply.*,users.userid,users.avtar FROM appreply
LEFT JOIN users ON users.userid=appreply.userid
WHERE appreply.appid='$appid'
ORDER by apprepid DESC
LIMIT 10
But as i said this disturbs the order.
Please help me
try
SELECT X.* FROM
(
SELECT users.display,a.apprepid apprepid_ar, a.appid appid_ar, a.reply reply_ar, a.userid userid_ar, a.browser browser_ar, a.os os_ar, a.time time_ar,users.userid,users.avtar FROM appreply a
LEFT JOIN users ON users.userid=a.userid
WHERE a.appid='$appid'
ORDER by a.apprepid DESC
LIMIT 10
) X ORDER BY X.apprepid_ar ASC
You might be having an auto-increment field in the table, which is usually 'Id'.
Instead of using the DESC operator on the apprepid field, use the Id field as:
SELECT users.display,appreply.*,users.userid,users.avtar FROM appreply
LEFT JOIN users ON users.userid=appreply.userid
WHERE appreply.appid='$appid'
**ORDER by id DESC**
LIMIT 10 ORDER BY apprepid ASC