Reference outside inner join query - mysql

I have the following query for a basic messaging system. It shows the messages that you've sent. However, it is showing the first_name and second_name for the account referenced by sender_id. Instead I want it to output the name columns referenced by receiver_id
ie) SELECT first_name, second_name WHERE accounts.account_id = messages.receiver_id
Do I need to write a seperate query for this or can I make this one output it in a single query? I've been thinking quite hard about this problem!
SELECT first_name, second_name, message_id, title, body, seen, urgent, type, messages.timestamp
FROM messages
INNER JOIN accounts
ON messages.sender_id=accounts.account_id
WHERE sender_id = ?

You can do this with two joins:
SELECT s.first_name as sender_first, s.second_name as sender_last,
r.first_name as rec_first, r.second_name as rec_last,
m.message_id, m.title, m.body, m.seen, m.urgent, m.type, m.timestamp
FROM messages m inner join
accounts s
ON m.sender_id = s.account_id inner join
accounts r
on m.receiver_id = r.account_id
WHERE m.sender_id = ?

Related

SQL query to combine columns from 3 different tables?

So basically I have 3 differnet tables named member, phone_message and pc_message each with the following columns:
member = (id, cell_num)
phone_message = (phone_num, content, received_at)
pc_message = (member_id, content, sent_at)
What i'm trying to do here is to list all the messages including both phone and pc that are received or sent for each member. To do that i need to combine those tables into one using SQL queries like JOIN and UNION. Any help?
You can try below query, I have used inner join so that messages only sent by matching members will be diplayed.
In addition to that, I have added one extra column as device which will identify whether the message is sent on phone or pc.
select m.id as memberId, m.cell_num as phoneNumber, p.content as Message, p.received_at as recieveDate,'Phone' as device from member m
inner join phone_message p on m.cell_num = p.phone_num
union
select m.id as memberId, m.cell_num as phoneNumber, pc.content as Message, pc.sent_at as recieveDate,'PC' as device from member m
inner join pc_message pc on m.id = pc.member_id
Try this;
select m.id as memberId, m.cell_num as phoneNumber, p.content, p.received_at as recieveDate from member m
inner join phone_message p on m.cell_num = p.phone_num
union
select m.id as memberId, m.cell_num as phoneNumber, pc.content, pc.sent_at as recieveDate from member m
inner join pc_message pc on m.id = pc.member_id

Inner Join SQL table multiple times for the same table

I have two tables (msg and users). I am trying to join these two tables together and print out the first and last name of the sender and the recipient as well as all of the message content if the message is marked as flag. I keep getting errors though, and have not found a good way to do this yet.
Pictures of tables for reference: https://imgur.com/a/wYuptfR
SQL query I'm using right now:
SELECT msg.*, users.uuid AS users.ruuid, users.uuid AS users.suuid, users.firstName, users.lastName
FROM msg
INNER JOIN users ON users.ruuid = msg.recipient
AND INNER JOIN users ON users.suuid = msg.sender
WHERE msg.flag = 0
You can query the same table several times if you use different aliases. Also, note you shouldn't have an and between the two join clauses:
SELECT msg.*,
r.uuid AS ruuid, r.firstName AS rfirstname, r.lastName AS rlastname,
s.uuid AS suuid, s.firstName AS sfirstname, s.lastName AS slastname
FROM msg
INNER JOIN users r ON r.uuid = msg.recipient
INNER JOIN users s ON s.uuid = msg.sender
WHERE msg.flag = 0
Try This
SELECT msg.*, u.uuid , u.firstName, u.lastName, s.uuid , s.firstName, s.lastName
FROM msg
INNER JOIN users u ON u.uuid = msg.recipient
INNER JOIN users s ON s.uuid = msg.sender
WHERE msg.flag = 0

MySql sorted subquery with group by in main query

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)

Left join to get most recent entries

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
)

Disambiguating identical columns in a join

[Yes, I've searched for an answer for this here and in google but this is a little difficult to query for.]
(MySQL database.)
messages table:
messageid
senderid
recipientid
people table:
personid
name
I wish to issue a query that returns the following:
messageid sender_name recipient_name
1 larry jane
2 mark alice
etc.
The following doesn't do it, and I expected that it would not, but it's a place to start:
select m.messageid, p.name as "sender_name", p.name as "recipient_name"
from messages m, people p
where m.senderid = p.personid and m.recipientid = p.personid
The issue is that I don't know how in sql to specifically reference the sender and the recipient since they are part of the same join clause, if that makes sense.
thanks
try:
select m.messageid, pSender.name as "sender_name", pRecipient.name as "recipient_name"
from messages m
inner join people pSender on m.senderId = pSender.personId
inner join people pRecipient on m.recipientid = pRecipient.personId
For your join method (i think this should work... i'm not very familiar with comma joins)
select m.messageid, p.name as "sender_name", p.name as "recipient_name"
from messages m, people pSender, people pRecipient
where m.senderid = pSender.personid and m.recipientid = pRecipient.personid
You can join the same table into the query twice, just alias it differently, something aking to:
select m.messageid, s.name as "sender_name", r.name as "recipient_name"
from messages m
inner join people s on m.senderid = s.personid
inner join people r on m.recipientid = r.personid
Your query return only messages that is sent from a person to itself. Something like:
select m.messageid, p1.name as sender_name, p2.name as recipient_name
from messages m,
join people p1
on m.senderid = p1.personid
join people p2
on m.recipientid = p2.personid
That is you need one join for sender and one join for receiver