Reputation: 141
First of all, I would like to apologize for the vague title of my question. It's very difficult for me to explain the situation, since I'm not an expert in SQL.
I need help in writing a query.
I have a table, called HELLO-MESSAGES, which contains several columns: ID, From, To, Body, Sent (with the data the message has been sent).
I have these rows:
What I would like to do is to display in a table, all different chats. If my telephone number is 345xxxxxxx, I would like to display a table with the following layout:
<table>
<tr>
<td><b>339xxxxxxx</b><br>Fine thanks, and you?</td>
</tr>
<tr>
<td><b>340xxxxxxx</b><br>The quick...</td>
</tr>
</table>
For example, just like WhatsApp or any other IMs do. I want to display the number of the person I'm chatting to, and the last message sent in that chat. I've a really bad experience in programming queries: this is what I've produced so far:
SELECT * FROM 'HELLO-MESSAGES' WHERE 'From'='345xxxxxxx' OR 'To'='345xxxxxxx'
But it returns EVERY message, as expected!
Please help a beginner that does not like SQL so much :)
EDIT
SELECT *
FROM (
SELECT MAX(d.id) AS max_id
FROM (
SELECT df.id
FROM ( SELECT xf.from
, xf.to
, MAX(xf.sent) AS max_date
FROM `HELLO-MESSAGES` xf
WHERE xf.from = '" . $_SESSION['ID'] . "'
GROUP BY xf.from, xf.to
) mf
JOIN `HELLO-MESSAGES` df
ON df.from = mf.from
AND df.to = mf.to
AND df.sent = mf.max_date
UNION ALL
SELECT dt.id
FROM ( SELECT xt.from
, xt.to
, MAX(xt.sent) AS max_date
FROM `HELLO-MESSAGES` xt
WHERE xt.to = '" . $_SESSION['ID'] . "'
GROUP BY xt.from, xt.to
) mt
JOIN `HELLO-MESSAGES` dt
ON dt.from = mt.from
AND dt.to = mt.to
AND dt.sent = mt.max_date
) q1
JOIN `HELLO-MESSAGES` d
ON d.id = q1.id
GROUP BY IF(d.from<d.to,d.from,d.to),IF(d.from<d.to,d.to,d.from)
) q2
JOIN `HELLO-MESSAGES` r
ON r.id = q2.max_id
ORDER BY r.sent DESC, r.id DESC
Is that right, @spencer7593? Thanks!
Upvotes: 0
Views: 125
Reputation: 108380
As I indicated in my comments, for a table with a large number of rows, to improve query performance, I would consider implementing an additional "latest message" table to be maintained along with the HELLO-MESSAGES
table. But I won't delve into that, since that doesn't address the specific question you were asking. (But I suspect you'll be asking the question how to improve the performance of the query that gets the "latest message"... beyond ensuring appropriate indexes are available, and that table grows to a significant size.)
To get the latest message in each "conversation" sent from a specific a telephone number, you can use a query something like this:
SELECT df.id
, df.from
, df.to
, df.sent
, df.body
FROM ( SELECT xf.from
, xf.to
, MAX(xf.sent) AS max_date
FROM `HELLO-MESSAGES` xf
WHERE xf.from = '345xxxxxxx'
GROUP BY xf.from, xf.to
) mf
JOIN `HELLO-MESSAGES` df
ON df.from = mf.from
AND df.to = mf.to
AND df.sent = mf.max_date
If there's more than one message with the same maximum value of "sent" for a given conversation, this query will return all of those rows.
A corresponding query can be used to get the latest message in each "conversation" that is received by a specific telephone number.
SELECT dt.id
, dt.from
, dt.to
, dt.sent
, dt.body
FROM ( SELECT xt.from
, xt.to
, MAX(xt.sent) AS max_date
FROM `HELLO-MESSAGES` xt
WHERE xt.to = '345xxxxxxx'
GROUP BY xt.to, xt.from
) mt
JOIN `HELLO-MESSAGES` dt
ON dt.from = mt.from
AND dt.to = mt.to
AND dt.sent = mt.max_date
We can combine the results from those two queries, and return just the unique id
value for those messages. We can combine the sets with the UNION ALL
operator. (The MySQL optimizer generally doesn't do to well with 'OR' predicates; that's the reason for breaking the query into the two parts.)
SELECT df.id
FROM ( SELECT xf.from
, xf.to
, MAX(xf.sent) AS max_date
FROM `HELLO-MESSAGES` xf
WHERE xf.from = '345xxxxxxx'
GROUP BY xf.from, xf.to
) mf
JOIN `HELLO-MESSAGES` df
ON df.from = mf.from
AND df.to = mf.to
AND df.sent = mf.max_date
UNION ALL
SELECT dt.id
FROM ( SELECT xt.from
, xt.to
, MAX(xt.sent) AS max_date
FROM `HELLO-MESSAGES` xt
WHERE xt.to = '345xxxxxxx'
GROUP BY xt.to, xt.from
) mt
JOIN `HELLO-MESSAGES` dt
ON dt.from = mt.from
AND dt.to = mt.to
AND dt.sent = mt.max_date
The set returned by this query will include the id
values of the latest "sent" and "received" message in each conversation.
If we are guaranteed that the id
value of a row for a "later" message will be greater than the id
value of a row with an "earlier" message, we can take advantage of that, to deal with the issue of more than one "latest" message in the conversation; two or more rows with the same value of Sent
.
We can use that to whittle down the results from the previous query to just the latest message in each conversation. We'll rerepresent the result of the previous query as q1
in the next query:
SELECT MAX(d.id) AS max_id
FROM (
subquery
) q1
JOIN `HELLO-MESSAGES` d
ON d.id = q1.id
GROUP BY IF(d.from<d.to,d.from,d.to), IF(d.from<d.to,d.to,d.from)
That get's the id
values of the messages we want to return. We'll represent that result as q2
in the next query.
SELECT r.id
, r.from
, r.to
, r.sent
, r.body
FROM (
) q2
JOIN `HELLO-MESSAGES` r
ON r.id = q2.max_id
ORDER BY r.sent DESC, r.id DESC
Putting that all together, we get a pretty ugly query:
SELECT r.id
, r.from
, r.to
, r.sent
, r.body
FROM (
SELECT MAX(d.id) AS max_id
FROM (
SELECT df.id
FROM ( SELECT xf.from
, xf.to
, MAX(xf.sent) AS max_date
FROM `HELLO-MESSAGES` xf
WHERE xf.from = '345xxxxxxx'
GROUP BY xf.from, xf.to
) mf
JOIN `HELLO-MESSAGES` df
ON df.from = mf.from
AND df.to = mf.to
AND df.sent = mf.max_date
UNION ALL
SELECT dt.id
FROM ( SELECT xt.from
, xt.to
, MAX(xt.sent) AS max_date
FROM `HELLO-MESSAGES` xt
WHERE xt.to = '345xxxxxxx'
GROUP BY xt.to, xt.from
) mt
JOIN `HELLO-MESSAGES` dt
ON dt.from = mt.from
AND dt.to = mt.to
AND dt.sent = mt.max_date
) q1
JOIN `HELLO-MESSAGES` d
ON d.id = q1.id
GROUP BY IF(d.from<d.to,d.from,d.to),IF(d.from<d.to,d.to,d.from)
) q2
JOIN `HELLO-MESSAGES` r
ON r.id = q2.max_id
ORDER BY r.sent DESC, r.id DESC
EDIT: missing qualifier for ambiguous column reference
EDIT: fixed expressions in GROUP BY
for q2. (The rows need to be collapsed/grouped by the distinct combined values of from
and to
. One approach to fixing that is to add an expression to the GROUP BY
. That fix is applied to the queries above.
This answer illustrates one approach to returning the specified result. This is not necessarily the best approach. The usage of inline views (derived tables in the MySQL venacular) can impose a (sometimes significant) performance penalty.
Upvotes: 1
Reputation: 45
What you are looking for, I think is not that simple and you need to have good knowledge of sql. I think that the answer is in the following link:
Get Last conversation row from MySQL database table
Hope this helps you. Best regards !
Upvotes: 1
Reputation: 26
You can compare string with LIKE function. In your case:
SELECT * FROM `HELLO-MESSAGES` WHERE `From` LIKE "345%" OR `To` LIKE "345%" ORDER BY `Sent` DESC LIMIT 1;
See more: http://dev.mysql.com/doc/refman/5.7/en/string-comparison-functions.html
Upvotes: 0