I have two tables (messages and user). I want to select the last (msg_id,text) from the messages table for a particular ad_id and need to select the name of the user from the user table.
SELECT u.id
, m.date
, m.ad_id
, max(m.msg_id)as msg_id
, u.first_name
, m.text
, m.u_to_id
, m.u_from_id
FROM user u
JOIN messages m
ON CASE WHEN m.u_from_id ='14' THEN u.id = m.u_to_id
ELSE u.id = m.u_from_id END
AND (m.u_from_id='14' OR m.u_to_id='14')
AND m.ad_id='20'
GROUP BY CONCAT(m.ad_id,u.id)
ORDER by m.msg_id DESC
this query is working but I can't select t the last m.textTable structure
SELECT u.id, m.text
FROM user u
JOIN messages m ON m.msg_id = (SELECT max(msg_id) FROM messages WHERE u_from_id = u.id)
I simplified your query to show the logic relevant to your question. Basically you want to join your messages table on the msg_id that is equal to the inner query of the max msg_id with that user.
After so many experiments added a new column(bargainer) for identify the recipient and this query working fine for me
select m.msg_id,m.text,m.status,m.date,m.bargainer,m.ad_id,u.first_name,u.id from user u JOIN messages m where msg_id in (select max(msg_id) from messages m where m.ad_id=20 and u.id=m.bargainer group by(m.bargainer))group by(m.msg_id) order by msg_id DESC
Related
I'm developing a simple message system for my website. Here's an image of how the messages table works:
Picture of the MySql table
What I had in mind is to use a subquery and then use GROUP BY to get only the last message for every person that was sent to, in this case, the user 1. This is the first query:
SELECT m.user_id as user_id, u.username as username, u.avatar as avatar, m.message_id as messsage_id, m.message as message, m.date as read_date
FROM users_messages m
INNER JOIN users u
ON u.user_id = m.sender_id
WHERE m.receiver_id = 1
ORDER BY message_id DESC
Some data is from another table, but it isn't relevant. This query returns this result, which is expected:
Image with the results of the first query
I made the messages descriptive so you can see what's going on more clearly. Now, here comes the weird part. As you can see, the messages are sorted by message ID, so the comments are sorted from new to old. All good here. However, when I expecute the following query...
SELECT q.user_id, q.username, q.avatar, q.message, q.read_date
FROM (SELECT u.user_id as user_id, u.username as username, u.avatar as avatar, m.message_id as messsage_id, m.message as message, m.date as read_date
FROM users_messages m
INNER JOIN users u
ON u.user_id = m.sender_id
WHERE m.receiver_id = 1
ORDER BY message_id DESC) AS q
GROUP BY user_id
This should group the messages by user, so only the first one (the newest one in this case) should show up. However, this isn't the case. It ALWAYS returns the first messages for everyone, as seen in the following picture:
Picture with the results that make no sense
I'm really, really confused about this, and I can't find anything that helps me fix it.
The issue is with the GROUP BY, which selects an arbitrary result from the set you give it. I think you should be able to get the result you want by changing your first query:
SELECT u.user_id as user_id, u.username as username, u.avatar as avatar, m.message_id as message_id, m.message as message, m.date as read_date
FROM users_messages m
INNER JOIN users u
ON u.user_id = m.sender_id
WHERE m.receiver_id = 1 AND
m.message_id = (SELECT MAX(message_id)
FROM users_messages m1
WHERE m1.sender_id = u.user_id)
I have the tables users and statuses . I want to select all the users, plus any statuses they might have, but only the most recent status from each user.
Here is the code that doesn't work:
SELECT users.id, alias, gender, login, logout, users.create_date, statustext as statustxt, TIMESTAMPDIFF(YEAR,birthdate,CURDATE()) AS age
FROM users
LEFT JOIN statuses s ON users.id = s.user_id
WHERE s.ID = (
SELECT MAX(s2.ID)
FROM statuses s2
WHERE s2.user_id = s.user_id
)
This gets the users with the most recent statuses, but not the users from the users table as well. Maybe it can be fixed by some small adjustment?
I got the sub query by searching, but I don't understand how that code works. It seems to compare two versions of the same table (For example: WHERE s2.user_id = s.user_id ) . Where can I read about this sort of technique?
Is a sub query required in this case by the way?
If you can find a solution would be great, and some basic explanation of how it works would highly appreciated.
----------EDIT---------------
I took one of the responses (by maresa) and combined with the sub query of my initial code , and this works(!) It has 3 selects and looks a bit over complicated maybe?:
SELECT users.id, alias, gender, login, logout, users.create_date, statustext as statustxt, TIMESTAMPDIFF(YEAR,birthdate,CURDATE()) AS age
FROM users
LEFT JOIN (
SELECT user_id, statustext FROM statuses s
WHERE s.ID = (
SELECT MAX(s2.ID)
FROM statuses s2
WHERE s2.user_id = s.user_id
)
) as s ON users.id = s.user_id
I've encountered similar problem. This post is relevant: http://www.microshell.com/database/sql/optimizing-sql-that-selects-the-maxminetc-from-a-group/.
Regarding your specific query, since you care only the latest status, you want to first get the latest status from each users. Assuming that the latest status has the latest id (based on your sample), the SQL would be below:
SELECT
MAX(ID), statustext, user_id
FROM
statuses
GROUP BY
user_id
What the above query does is, to get the latest status per user_id. Once you get that, you can think of it as if it's a table. Then simply join on this "table" (the query) instead of the real one (statuses table). Therefore, your query would be like below:
SELECT
users.id, alias, gender, login, logout, users.create_date, statustext as statustxt, TIMESTAMPDIFF(YEAR,birthdate,CURDATE()) AS age
FROM
users
LEFT JOIN (
SELECT
MAX(ID), user_id
FROM
statuses
GROUP BY
user_id
) as s ON users.id = s.user_id
LEFT JOIN statuses ON statuses.ID = s.ID -- EDIT: Added this line.
You might use a subselect as the join, and limit to show only 1 row:
SELECT
users.id,
alias,
gender,
login,
logout,
users.create_date,
statustext as statustxt,
TIMESTAMPDIFF(YEAR,birthdate,CURDATE()) AS age
FROM users
LEFT JOIN (
SELECT user_id,statustext
FROM statuses s1
WHERE s1.user_id = users.id
ORDER BY status_date DESC
LIMIT 1
) s ON users.id = s.user_id
You could use a in clause with a tuple
SELECT
users.id
, alias
, gender
, login
, logout
, users.create_date
, statustext as statustxt
, TIMESTAMPDIFF(YEAR,birthdate,CURDATE()) AS age
FROM users
LEFT JOIN statuses s ON users.id = s.user_id
WHERE (s.user_id, s.ID) in (
SELECT user_id, MAX(s2.ID)
FROM statuses s2
group by user_id
)
I am trying to use the following code to get the 6 users with which the current user has most recently chatted. I have two problems. First of all, if the current user has recieved a message from the other user but has only sent, that other user isnt fetched. Second of all, the ORDER BY clause is causing an error. Im a beginner in SQL so I have no idea what's going on.
Thanks in Advance!
Here's the code:
SELECT users.*
FROM users INNER JOIN
messages fromuser
ON (fromuser.fromid = users.id) INNER JOIN
messages touser
ON (touser.toid = users.id)
WHERE fromuser.toid = :userid OR touser.fromid = :meid
GROUP BY users.id
ORDER BY MAX(messages.datetime)
LIMIT 6;
This should do your job, and it relies less on MySQL extensions than your other answer so far. I estimate that it would perform about the same, but it's surely wordier.
SELECT u.*
FROM (
SELECT DISTINCT otherid
FROM (
SELECT
m.fromid AS otherid,
MAX(m.datetime) as maxts
FROM messages m
WHERE m.toid = :userid
GROUP BY m.fromid
UNION ALL
SELECT
m.toid AS otherid,
MAX(m.datetime) as maxts
FROM messages m
WHERE m.fromid = :userid
GROUP BY m.toid
) um
ORDER BY maxts DESC
LIMIT 6
) otheru
INNER JOIN users u
ON u.id = otheru.otherid
Your logic is doomed to fail, because one users.id cannot be two different values at the same time. I think this query does what you want:
SELECT u.*
FROM messages m INNER JOIN
users u
ON (m.fromid = u.id AND m.toid = :userid) OR
(m.toid = u.id AND m.fromid = :userid )
GROUP BY u.id
ORDER BY MAX(m.datetime) DESC
LIMIT 6;
Notice that it joins to the users table by the id that is not the current user.
Need a little bit of help with a query.
SELECT id,email FROM user_info WHERE username!=''
AND email='example#gmail.com'
GROUP BY email ORDER BY id DESC LIMIT 1
What this does currently is fetch a distinct email but not the newest ID for an account under this email.
It sounds like you can apply an aggregate to the id field and it will return the most recent id for each email:
SELECT Max(id), email
FROM user_info
WHERE username!=''
AND email='example#gmail.com'
GROUP BY email
Unfortunately, when you apply a GROUP BY without an aggregate there is no guarantee what id will be returned unless you specify to select the max.
If you want to return the username that is associated with the max(id), then you can use a subquery:
SELECT i.MaxId,
i.email,
u.username
FROM user_info u
inner join
(
select max(id) MaxId, email
from user_info
WHERE username!=''
AND email='example#gmail.com'
group by email
) i
on u.id = i.maxid
and u.email = i.email
WHERE username!=''
AND email='example#gmail.com';
If by "newest id" you mean the largest value, then here's one approach that makes use of MySQL user variables to retain the value from previous rows, so a comparison can be made:
SELECT IF(u.email=#prev_email,1,0) AS dup_email_ind
, u.id
, u.username
, #prev_email := u.email AS email
FROM user_info u
CROSS
JOIN ( SELECT #prev_email := NULL) i
WHERE u.username != ''
AND u.email = 'example#gmail.com'
GROUP
BY u.email DESC
, u.id DESC
, u.username DESC
This returns all of the rows, with an indicator of whether the row is considered an older "duplicate" email or not. Rows that have dup_email_ind = 1 are identified as older duplicates, dup_email_ind = 0 indicates that this row is the latest row (the row with the largest id value) for a given email value.
(Usually, when I'm looking for duplicates like this, it's helpful for me to return both, or all, of the rows that are "duplicates".)
To return only the rows with the "newest id", wrap the query above (as an inline view) in another query: the output from the query is used a row source for the outer query.)
SELECT d.*
FROM (
-- the query above gets put here
) d
WHERE d.dup_mail_ind = 0
Another approach is to use a correlated subqueries in the SELECT list, although this is really only suitable for returning small sets. (This approach can have serious performance issues with large sets.)
SELECT ( SELECT u1.id
FROM user_info u1
WHERE u1.email = e.email
AND u1.username != ''
ORDER BY u1.id DESC
LIMIT 1
) AS id
, ( SELECT u2.username
FROM user_info u2
WHERE u2.email = e.email
AND u2.username != ''
ORDER BY u2.id DESC
LIMIT 1
) AS username
, e.email
FROM ( SELECT u.email
FROM user_info u
WHERE u.email = 'example#gmail.com'
AND u.username != ''
GROUP BY u.email
) e
I'm trying to count all messages sent by users AFTER they uploaded a photo.
I'm trying something like this.
select messages.created_at, count(*) as count from messages
inner join users on messages.user_id = users.id
inner join photos on photos.user_id = users.id
where
some_users_messages.created_at > some_users_first_photo.created_at
group by YEARWEEK(messages.created_at)
I'm thinking this needs to be a subquery? I'm not sure how to do this concept of one particular user's messages/photos in MySQL. Any ideas?
Thanks!
This would count the number of messages sent after the first photo per user:
select messages.user_id
, count(*) as count
from messages
where messages.created_at >
(
select min(created_at)
from photos
where photos.user_id = messages.user_id
)
group by
messages.user_id