SQL referring on one table after join with another table - mysql

I got this MySQL query to output latest chat message a user with ID of 18 has had with other users. The query works until I add-in last JOIN, intended to calculate amount of unviewed msgs (unviewed_total). The error I get is 'column not found' for T2.rid1 and T2.rid2. Can't figure out how to refer these columns in a proper way.
SELECT T2.maxDate, T2.ava, T2.uname,chat.user_to,chat.user_from,chat.body,chat.request_id,chat.secondary_rid, T3.unviewed_total FROM
(SELECT T1.user2_id, users.uname, users.ava, max(cdate) maxDate, T1.rid rid1, T1.sec_rid rid2 FROM
(SELECT chat.user_to user2_id, max(msg_time) cdate,request_id rid,secondary_rid sec_rid
FROM chat WHERE chat.user_from=18
GROUP BY chat.user_to
union distinct
(SELECT chat.user_from user2_id, max(msg_time) cdate,request_id rid,secondary_rid sec_rid
FROM chat WHERE chat.user_to=18
GROUP BY chat.user_from)) T1
inner join users on (users.uid = T1.user2_id)
group by T1.user2_id
order by maxDate desc) T2
join chat on (T2.maxDate = chat.msg_time)
join (SELECT COUNT(viewed) unviewed_total FROM chat WHERE viewed='0' AND user_to=18 AND request_id IN (T2.rid1,T2.rid2)) T3
ORDER BY T2.maxDate DESC

The inner query of that join doesn't know about the table T2.
I suggest moving the join to the select statement.
Change
SELECT T2.maxDate, T2.ava, T2.uname,chat.user_to,chat.user_from,chat.body,chat.request_id,chat.secondary_rid, T3.unviewed_total FROM
to
SELECT T2.maxDate, T2.ava, T2.uname,chat.user_to,chat.user_from,chat.body,chat.request_id,chat.secondary_rid, (SELECT COUNT(viewed) unviewed_total FROM chat WHERE viewed='0' AND user_to=18 AND request_id IN (T2.rid1,T2.rid2)) unviewed_total
More about this technique here: https://www.essentialsql.com/get-ready-to-learn-sql-server-20-using-subqueries-in-the-select-statement/

Related

MySQL Query to retrieve last entry in another table

I have 2 mysql tables (Version Minor to MySql 8) in a simple chat app
Chat
id_chat
chat_name
1
My first chat
2
My Second Chat
And Chat_Message
id_chat_message
id_chat
message
date
1
1
How
03/Mar/2021
2
1
Are you
04/Mar/2021
3
2
This
05/Mar/2021
4
2
Is other
06/Mar/2021
How can I make a Query if I want to retrieve the last message for every chat?
The resultset should be
id_chat
chat_name
last_message
last_message_date
1
My first chat
Are you
04/Mar/2021
2
My Second Chat
Is other
06/Mar/2021
Thanks
After some tests I came with this, but I'm not 100% sure it works in all cases:
SELECT hc.id_chat, hc.chat_name, hcm.message as last_message, hcm.date as last_message_date from chat hc inner join (SELECT a.*
FROM chat_message a
INNER JOIN (
SELECT id_chat, MAX(date) date
FROM chat_message
GROUP BY id_chat
) b ON a.id_chat = b.id_chat AND a.date = b.date) hcm on hc.id_chat = hcm.id_chat group by hcm.id_chat;
Some inspiration came from SQL select only rows with max value on a column
On MySQL 8+, we can use ROW_NUMBER here:
WITH cte AS (
SELECT c.id_chat, c.chat_name, cm.message, cm.date,
ROW_NUMBER() OVER (PARTITION BY c.id_chat ORDER BY cm.date DESC) rn
FROM Chat c
INNER JOIN Chat_Message cm ON cm.id_chat = c.id_chat
)
SELECT id_chat, chat_name, message, date AS last_message_date
FROM cte
WHERE rn = 1;
On earlier versions of MySQL, we can join to a subquery which finds the latest chat message from the second table.
SELECT c.id_chat, c.chat_name, cm.message, cm.date AS last_message_date
FROM Chat c
INNER JOIN Chat_Message cm
ON cm.id_chat = c.id_chat
INNER JOIN
(
SELECT id_chat, MAX(date) AS max_date
FROM Chat_Message
GROUP BY id_chat
) t
ON t.id_chat = cm.id_chat AND t.max_date = cm.date;

(MySQL) Get last data in every group (SubQuery)

Table:
Table
Help me in. I'm suck in SubQuery. I wanna get the latest Name from every category group
Output:
Output
I already search some reference in google and still didn't understand. Hopefully this time I can understand
you can use join on max_date for each category
select * from
my_table m
inner join (
select category, max(date) max_date
from my_table
group by category) as t on m.date = t.max_date and m.category = t.category
Thx scaisEdge!!
I finally got it!
So this is my final query
select m.id, m.category, m.name, m.time
from my_table m
inner join (
select id, category, name, time
from my_table
order by id desc
) as t
on m.id = t.id
group by category

Selecting the last record and comparing a datetime

I'm building a discussion board and I want to get a list of unread topics.
A topic should be unread and selected if the created_at datetime for the last post in a topic is greater than the last time the currently logged in user viewed this topic.
http://sqlfiddle.com/#!2/4e2e99/1
If you delete all of the user view inserts ALL of the topics should be listed.
I have three tables:
topics
id
user_id
created_at
topic_posts
id
topic_id
created_at
topic_user_views
id
topic_id
created_at
My query so far (but it doesn't work):
SELECT DISTINCT `topics`.`id`, `topics`.`name`
FROM `topics`
INNER JOIN `topic_user_views`
ON `topic_user_views`.`topic_id` = `topics`.`id`
INNER JOIN `topic_posts`
ON `topic_posts`.`topic_id` = `topics`.`id`
WHERE `topic_posts`.`created_at` > `topic_user_views`.`created_at`
AND `topic_user_views`.`user_id` = 1
ORDER BY `id` DESC
I don't know how to compare a topic's last post's created_at column post to the last time this user has viewed the topic.
here is one way of doing it. we will use exists to test if the topic was viewed before the last topic post was created. see the sql fiddle - http://sqlfiddle.com/#!2/4e2e99/11
select t.id as topic_id, t.name as topic_name
from topics t
where not exists(
select tuv.topic_id, max(tuv.created_at) as last_view, max(tp.created_at) as last_post
from topic_user_views tuv
inner join topic_posts as tp
on tuv.topic_id=tp.topic_id and tuv.created_at > tp.created_at
group by topic_id
having t.id=topic_id)
ORDER BY id DESC
With this example, topics also will be selected if user have not seen anything yet:
SELECT DISTINCT
`topics`.`id`,
`topics`.`name`
FROM `topics`
LEFT JOIN `topic_user_views` AS tuv
ON `tuv`.`topic_id` = `topics`.`id`
WHERE (SELECT
1
FROM topic_posts AS tp
WHERE tp.topic_id = `topics`.`id`
AND (tp.created_at > tuv.created_at
OR tuv.created_at IS NULL)
LIMIT 1)IS NOT NULL
ORDER BY `id` DESC;
Here is what I came up with :
SELECT t1.topic_id, t3.account_user_id, t1.created_at last_post_date, t4.created_at as last_seen_date
FROM topic_posts t1
INNER JOIN (SELECT topic_id, MAX(created_at) as created_at
FROM topic_posts
GROUP BY topic_id) t2
USING (topic_id, created_at)
INNER JOIN topic_user_views t3
USING (topic_id)
INNER JOIN (SELECT topic_id, account_user_id, created_at
FROM topic_user_views
INNER JOIN (SELECT topic_id, account_user_id, MAX(created_at) as created_at
FROM topic_user_views
GROUP BY topic_id, account_user_id) _
USING (topic_id, created_at, account_user_id)) t4
ON t1.topic_id = t4.topic_id and t3.account_user_id = t4.account_user_id and t3.created_at = t4.created_at
WHERE t1.created_at > t4.created_at
AND t3.account_user_id = 1;
This idea is to join the date of the last posted message in a topic (t2) and the date of the last seen message in a topic by a user (t4), and then you just have to filter out the results you don't want.
If I'm not mistaken, in the SQLFiddle you provided, there are no topics that have not already been seen by users, so this returns nothing. I slightly modified it (you can see it here) and it seems to work as wanted.
To consider the topics never viewed by a user, I think the best solution is to use the account table with a RIGHT OUTER JOIN. Something similar to :
SELECT t1.topic_id, t3.account_user_id, t1.created_at last_post_date, t4.created_at as last_seen_date
FROM topic_posts t1
INNER JOIN (SELECT topic_id, MAX(created_at) as created_at
FROM topic_posts
GROUP BY topic_id) t2
USING (topic_id, created_at)
INNER JOIN topic_user_views t3
USING (topic_id)
INNER JOIN (SELECT topic_id, account_user_id, created_at
FROM topic_user_views
INNER JOIN (SELECT topic_id, account_user_id, MAX(created_at) as created_at
FROM topic_user_views
GROUP BY topic_id, account_user_id) _
USING (topic_id, created_at, account_user_id)) t4
ON t1.topic_id = t4.topic_id and t3.account_user_id = t4.account_user_id and t3.created_at = t4.created_at
RIGHT OUTER JOIN account
ON account.user_id = t3.account_user_id
WHERE t1.created_at > t4.created_at or t4.account_user_id IS NULL
AND t3.account_user_id = 1;
which I did not test.

SQL getting max id field on a LEFT JOIN

I am trying to pull the photo from tblimage that corresponds to the maxid in the tblimage for each user. Currently, I am getting all the messages from the message table and a random photo for the user that posted the message, I would like the photo to be the latest uploaded photo. the way its written now it just pulls a random photo from the table. any suggestions?
table structures are as such:
messages: msgid, message, user_id, event_id
tblimage: id, photo, userid
SELECT messages.*, tblimage.photo, max(tblimage.id)
FROM messages LEFT JOIN tblimage ON messages.user_id = tblimage.userid
GROUP BY messages.msg_id, messages.user_id
ORDER BY messages.msg_id DESC, tblimage.id desc
Try
SELECT messages.*, T2.photo
FROM messages
LEFT JOIN (SELECT userid, MAX(id) AS maxid
FROM tblimages
GROUP BY userid) AS T1
ON messages.user_id = T1.userid
LEFT JOIN tblimages AS T2
ON T2.id = T1.maxid
ORDER BY messages.msg_id DESC
which finds max(id) for each user in tblimages, then uses that to join each user to the latest photo for that user.

Report Query for multiple subqueries

Getting multiple records from table with subquery joins
SELECT
COUNT(*) AS total_count,
(SELECT
chat_box.user_id,
chat_box.message,
members.id,
members.display_name
FROM chat_box INNER JOIN members
ON chat_box.user_id = members.id
ORDER BY chat_id DESC LIMIT 1),
(SELECT COUNT(DISTINCT user_id) FROM chat_box) AS users_count
FROM chat_box
This is what I have so far, I want to get the members.display_name from the inner join where the chat_box.user_id = members.id as an output along aside the chat_box.message and save members.display_name and chat_box.message to a variable. Any help is appreciated.
It is not exactly clear what you are trying to do, but it seems like you could use something like this:
select u.user_id,
u.message,
u.id,
u.display_name,
cb1.total_count,
cb1.users_count
from
(
SELECT cb.user_id ,
cb.message,
m.id,
m.display_name
FROM chat_box cb
INNER JOIN members m
ON cb.user_id = m.id
) u
CROSS JOIN
(
select COUNT(*) AS total_count,
COUNT(DISTINCT user_id) AS users_count
FROM chat_box
) cb1