Am I running too many subqueries in my SQL statement? - mysql

I have the following tables:
users:
user_id
user_name
message:
message_id
thread_id
to_id
from_id
title
message_text
message_date
status
The desired result of the query I run is to list the title of the most recent message from the most recent threads a long with the thread_id and username and to sort the result by date in descending order. I'm only going to be listing about 10 to 20 results at a time most likely.
The SQL query I came up with seems to be doing this so far, but I feel like I have over complicated it and that there may be a more optimal way to write my query.
SELECT personal_messages.message_id,
personal_messages.thread_id,
personal_messages.body,
users.username
FROM users, personal_messages
WHERE message_id IN
(SELECT MAX(message_id) from personal_messages GROUP BY thread_id)
AND users.id IN
(SELECT users.id FROM users WHERE users.id = personal_messages.from_id)
ORDER BY personal_messages.message_date DESC
Also, if anyone knows a way to get the count of all the messages with the same thread_id, that would be awesome!
Any tips would be greatly appreciated!

One of the subselects could be unnecessary
SELECT personal_messages.message_id, personal_messages.thread_id, personal_messages.body, users.username
FROM users INNER JOIN personal_messages ON (users.id = personal_messages.from_id)
WHERE message_id IN (SELECT MAX(message_id) from personal_messages GROUP BY thread_id)
ORDER BY personal_messages.message_date DESC
Edit: Also, if anyone knows a way to get the count of all the messages with the same thread_id, that would be awesome!
SELECT MAX(message_id), COUNT(message_id) FROM personal_messages GROUP BY thread_id

I don't think there are too many subqueries in your query but it can be written more optimally. I think the below query should work equally well:
SELECT `PM`.`message_id`, `PM`.`thread_id`, `PM`.`body`, `U`.`username`
FROM `users` `U`
INNER JOIN `personal_messages` `PM` ON `PM`.`from_id` = `U`.`user_id`
ORDER BY `PM`.`message_date` DESC;
The issues that I've found with your query are:
The first sub-query - SELECT MAX(message_id) from personal_messages GROUP BY thread_id - will always return a single resultset, so using an IN doesn't make sense
For the second sub-query - SELECT users.id FROM users WHERE users.id = personal_messages.from_id - you may use an INNER JOIN instead, like in my example
Hope the above helps.
Also, I'm not sure on what info are you actually trying to retrieve. But the above query should give you the details for the most recent message, along with its thread id and username.
EDITED query to select one latest message per thread:
SELECT *
FROM (
SELECT `PM`.`message_id`, `PM`.`thread_id`, `PM`.`body`, `U`.`username`
FROM `users` `U`
INNER JOIN `personal_messages` `PM` ON `PM`.`from_id` = `U`.`user_id`
ORDER BY `PM`.`message_date` DESC;
) `TT`
GROUP BY `TT`.`thread_id`;

SELECT tbl.message_id, personal_messages.thread_id
, personal_messages.body, users.username
FROM users u
JOIN personal_messages tbl on tbl. from_id = u.UsersId
WHERE tbl.message_id IN (SELECT MAX(message_id) from personal_messages
GROUP BY thread_id)
ORDER BY personal_messages.message_date DESC

What you have already is pretty good. If you want to do it all with one query, you could get the COUNT(message_id) GROUP BY thread_id as part of the same subselect that gets the MAX(message_id) GROUP BY thread_id:
SELECT personal_messages.message_id,
personal_messages.thread_id,
personal_messages.body,
users.username,
thread.countmessages
FROM personal_messages
JOIN users ON users.id=personal_messages.from_id
JOIN
(SELECT COUNT(message_id) countmessages,
MAX(message_id) maxmessage_id
FROM personal_messages
GROUP BY thread_id) AS thread
ON thread.maxmessage_id=personal_messages.message_id
ORDER BY personal_messages.message_date DESC

Related

Order rows by amount of columns in another table

I'm currently outputting all of my members by adding the MySQL clause ORDER BY id DESC, but I feel that doesn't reward people that are active on my service.
I thought about judging the order by the amount of entries in another table they have under their ID.
Essentially, I'm asking if it's possible to order columns in a MAIN table counting the amount of rows where the users ID is in the column of the row.
Something pseudo to this
SELECT user_id,name,etc FROM users ORDER BY (
COUNT(SELECT FROM users_interactions WHERE user_id = user_id) *******
) ASC
In the end of the COUNT statement, the user_id = user_id was just a guess.
You are almost there - what you need to do is to put COUNT inside SELECT:
SELECT user_id,name,etc FROM users u ORDER BY (
SELECT COUNT(*)
FROM users_interactions i
WHERE i.user_id = u.user_id
) ASC
You could also do it using a JOIN, like this:
SELECT u.user_id, u.name, u.etc
FROM users u
LEFT OUTER JOIN users_interactions i ON i.user_id = u.user_id
GROUP BY u.user_id, u.name, u.etc
ORDER BY COUNT(*) ASC

Selecting last record from inner joined records

I made a post 6 days ago but no one seemed to anwser correctly and it was eventually abandoned so here I post it again (sorry for DPing but this is important to me)
I have 2 tables - people (query is based on people.id so no need to ss the entire table) and messages (http://prntscr.com/94iq2e)
I have a query which is grouping messages with people and that is working fine but each person is grouped with the first message he sent and I need to make it so that it groups people with the LAST message they sent
Here is the query which is grouping people with the FIRST message
SELECT people.id,
people.avatar,
people.firstname,
people.lastname,
LEFT(messages.message, 90) AS message,
messages.seen,
messages.date
FROM people
INNER JOIN messages
ON people.id = messages.sender_id
WHERE reciever_id = '". $user_data['id'] ."'
GROUP BY sender_id
ORDER BY date DESC limit 11
Link to the previous topic -> Selecting last record from INNER JOIN and grouping
You could use a subquery that returns the maximum date:
SELECT sender_id, MAX(date) AS max_date
FROM messages
GROUP BY sender_id
and join people table with this subquery, and then join to the messages table to get the message with the maximum date:
SELECT
people.id,
people.avatar,
people.firstname,
people.lastname,
LEFT(messages.message, 90) AS message,
messages.seen,
messages.date
FROM
people INNER JOIN (
SELECT sender_id, MAX(date) AS max_date
FROM messages
GROUP BY sender_id
) lm ON people.id = lm.sender_id
INNER JOIN messages
ON people.id = messages.sender_id AND
lm.max_date=messages.date
WHERE
reciever_id = ...
ORDER BY
...
I would recommend using a sub query to pull out the latest messages. The below link may of help to you, it details a few possible solutions.
SQL join: selecting the last records in a one-to-many relationship
Cheers,
Bob
I dont know mysql but in TSQL it would be like this to get last message:
ISNULL(SELECT TOP 1 messages.message FROM messages WHERE people.id = messages.sender_id ORDER BY messages.id DESC,'')
Whole code would look like this:
SELECT people.id,
people.avatar,
people.firstname,
people.lastname,
ISNULL(SELECT TOP 1 messages.message FROM messages WHERE people.id = messages.sender_id ORDER BY messages.id DESC,'') AS message,
messages.seen,
messages.date
FROM people
INNER JOIN messages
ON people.id = messages.sender_id
WHERE reciever_id = '". $user_data['id'] ."'
GROUP BY sender_id
ORDER BY date DESC limit 11

MySQL query with GROUP BY and ORDER BY timestamp DESC

I am saving the history of Facebook likes for a page, identified by user_id.
Now from this table, I need to get a set representing the user_id's and their latest number of likes, based on the most recent timestamp.
I started off with this:
SELECT *
FROM facebook_log
GROUP BY user_id
ORDER BY timestamp DESC;
But that does not do what I want because it returns the first records with the lowest timestamps.
I read something online about GROUP returning the very first records from the table.
I also understood something about JOIN the table with itself, but that doesn't work either, or I did something wrong.
If you just need the user_id and the timestamp, you can just do
select f.user_id, max(f.timestamp)
from facebook_log
group by user_id;
if you need all the data from the table, you can do
select f.*
from facebook_log f
inner join (select max(timestamp) mt, user_id
from facebook_log
group by user_id) m
on m.user_id = f.user_id and m.mt = f.timestamp
You can also get the latest number of likes by using this MySQL trick:
select f.user_id, max(f.timestamp),
substring_index(group_concat(f.numlikes order by f.timestamp desc), ',', 1) as LatestLikes
from facebook_log f
group by f.user_id;

Building SQL query to join three tables to get messaging info

I have the following three tables for a messaging system:
`messaging_messagethread`
- id
- subject
- initiator_id # who creates the thread
- recipient_id
`messaging_message`
- id
- thread_id
- content
- timestamp
- sender_id
`messaging_messagestatus` # a status will be created for each recipient of a message
- id
- message_id
- recipient_id
- status
Given a user, I need to build a query to get the following:
Show Thread ID (distinct),
content and timestamp of the most recent message in that thread
Remove any threads with the most recent message status='deleted'.
Here is what I have so far:
SELECT DISTINCT thread.id as thread_id, timestamp.timestamp
FROM messaging_messagethread thread
INNER JOIN
(SELECT thread_id, MAX(timestamp) as timestamp
FROM messaging_message GROUP BY thread_id) timestamp
ON thread.id = timestamp.thread_id
WHERE initiator_id = 4 OR thread.recipient_id = 4 ORDER BY timestamp.timestamp DESC
This gives me distinct thread id's ordered by most recent timestamp. (My first of three points). How would I build the entire query?
You can use correlated subqueries to get the most recent message of a particular thread. Try this:
SELECT
a.id,
b.content,
b.timestamp
FROM
messaging_messagethread a
INNER JOIN
messaging_message b ON a.id = b.thread_id
WHERE
b.timestamp =
(
SELECT MAX(timestamp)
FROM messaging_message
WHERE thread_id = a.id
)
AND b.id NOT IN
(
SELECT message_id
FROM messaging_messagestatus
WHERE status = 'deleted'
)
AND 4 IN (a.initiator_id, a.recipient_id)
ORDER BY
b.timestamp DESC
If I understand you correctly, I believe this is what you want.
The problem is that you want the latest message from each thread,
unfortunately there's no way to retrieve the top member withing a group AFAIK
So what Im doing is basically getting all the messages from each thread_id
that the user is related to then simply returning the top id ... oh and checking
that message hasn't being deleted.
IM NOT SURE IF THIS IS CORRECT.
But please reconsider a different approach maybe using a programming language,
or multiple queries.
CAUSE THIS IS GOING TO BE SLOW...
SELECT thread_id, content, timestamp
FROM messaging_message
WHERE messaging_message.id IN
(
SELECT messaging_message.id
FROM messaging_message
INNER JOIN messaging_messagestatus ON
(messaging_messagestatus.message_id = messaging_message.id)
WHERE messaging_message.thread_id IN
(SELECT DISTINCT id
FROM messaging_messagethread
WHERE initiator_id = 4 OR recipient_id = 4) AND
messaging_messagestatus.status != 'deleted'
ORDER BY timestamp DESC
LIMIT 1
)
ORDER BY timestamp DESC
good luck.

MySQL latest unique user chats not working

I'm trying to get latest created datetime for unique user_id. I've got below query but it does not seem to be working....It does not get the latest created time.
SELECT * FROM `chats`
WHERE receiver_id = 1
GROUP BY user_id
ORDER BY created DESC
Is there a reason why?
UPDATE:
Actually I found my answer myself. Please look below. I had to use INNER JOIN for nested searched and filtered result table then find using where clause of that result then left join on that table to get the data I needed!
SELECT c.*, users.username
FROM chats c
INNER JOIN(
SELECT MAX(created) AS Date, user_id, receiver_id, chat, type, id
FROM chats
WHERE receiver_id = 1
GROUP BY user_id ) cc ON cc.Date = c.created AND cc.user_id = c.user_id
LEFT JOIN users ON users.id = c.user_id
WHERE c.receiver_id = 1
You should use a MAX aggregate function -
SELECT user_id, MAX(created) latest_datetime FROM chats
GROUP BY user_id;
Try this query, it will show all users and latest created datetime.