mysql count from joins using 3 tables - mysql

i'm trying to build a chat application, in which i have these four tables
chat --id,group_id,body,time_posted[timestamp]
chat_groups --establish id,name for a group
users_groups --link users to a group, also define if user stared this group FIELDS::user_id,group_id,stared[bool]
wall_visit --user_id,group_id,last_visit[timestamp]
the idea is every users join a group, and they post to it in chat.
chat_groups table is just for defining the room, while users_groups is for setting access of members to this group.
wall_visit table is a table that store specific user last time accessed specific group (since its many to many u know..)
now what im trying to establish is to get in one query,
the chat_groups the user in relation with
the count of messages posted to this group since user last login (from settings)
the count of members in this group
the group name
:)
i have been trying to hours now :( best i could come up with
SELECT w.last_visit,access.stared,cg.user_id,u.fullname as username,cg.name as group_name ,cgu.count_members,c.count_msgs,c.time_posted
FROM `chat_groups`cg
inner join chat_groups_users access on (access.group_id = cg.id and access.user_id = ?)
left outer join users u on u.id = cg.user_id
left join wall_visit w on w.group_id = cg.id
left join (select count(*) as count_members,group_id from group_users group by group_id) cgu on cgu.group_id = cg.id
left join (SELECT count(wv.id) as count_msgs,c.group_id,c.time_posted FROM chats c
left outer join `wall_visit` wv on (wv.group_id is not null and c.group_id = wv.group_id and c.time_posted > wv.last_visit)
group by c.group_id) c on c.group_id = cg.id
where cg.user_id = 1
this query is working ..ehh, my main problem is with the count of the messages in the group since last_visit.
what is the best methode to get message_count to work :( ??
can this query be optimized more?
Thanks SO community :)
My 2nd attempt
SELECT w.last_visit,access.stared,cg.user_id,u.fullname as username,cg.with_id,uu.fullname as with_name,cg.name as group_name ,cgu.count_members,c.count_msgs,c.time_posted
FROM `chat_groups`cg
inner join chat_groups_users access on (access.group_id = cg.id and access.user_id = 1)
left outer join users u on u.id = cg.user_id
left join wall_visit w on w.chat_id = cg.id
left outer join users uu on uu.id = cg.with_id
left join (select count(*) as count_members,group_id from chat_groups_users group by group_id) cgu on cgu.group_id = cg.id
left join (
SELECT group_id,count(c.id) as count_msgs,time_posted FROM `chats` c inner join wall_Visit wv on wv.chat_id = c.group_id where c.id > wv.last_visit group by c.group_id
) c on c.group_id = cg.id
where cg.user_id = 1

this should fix you count message problem
SELECT
`cg`.`user_id`, `cg`.`with_id`, `cg`.`name` AS `group_name`,
`access`.`stared`,
`u`.`fullname` AS `username`,
`w`.`last_visit`,
`uu`.`fullname` AS `with_name`,
`cgu`.`count_members`,
`c`.`count_msgs`, `c`.`time_posted`
FROM `chat_groups` AS `cg`
INNER JOIN `chat_groups_users` AS `access`
ON (`access`.`group_id` = `cg`.`id` AND `access`.`user_id` = `cg`.`user_id`)
LEFT OUTER JOIN `users` AS `u`
ON (`u`.`id` = `cg`.`user_id`)
LEFT JOIN `wall_visit` AS `w`
ON (`w`.`chat_id` = `cg`.`id`)
LEFT OUTER JOIN `users` AS `uu`
ON (`uu`.`id` = `cg`.`with_id`)
LEFT JOIN (
SELECT COUNT(*) AS `count_members`, `group_id`
FROM `chat_groups_users`
GROUP BY
`group_id`
) AS `cgu`
ON (`cgu`.`group_id` = `cg`.`id`)
LEFT JOIN (
SELECT count(`c`.`id`) AS `count_msgs`, `c`.`time_posted`
FROM `chats` AS `c`
INNER JOIN `wall_visit` AS `wv`
ON (`wv`.`chat_id` = `c`.`group_id`)
WHERE
`c`.`time_posted` > `wv`.`last_visit`
GROUP BY
`c`.`group_id`
) AS `c`
ON (`c`.`group_id` = `cg`.`id`)
WHERE `cg`.`user_id` = 1
otherwise u have to setup a fiddle

Related

Syntax error or access violation when trying to select distrinct

I'm honestly not sure why I'm getting an error since I just moved the two selects to be distrinct and in the left outer join but they were working before in the main opening select statement
SELECT body, timestamp FROM chats
LEFT OUTER JOIN messages ON chats.id = messages.chat_id
LEFT OUTER JOIN (SELECT DISTINCT sender.first_name AS sender_first_name, sender.last_name AS sender_last_name,
sender.username AS sender_username) users AS sender ON chats.from_user_id = sender.id
LEFT OUTER JOIN (SELECT DISTINCT reciever.first_name AS reciever_first_name, reciever.last_name AS reciever_last_name,
reciever.username AS reciever_username) users AS reciever ON chats.to_user_id = reciever.id
WHERE from_user_id = :fromUserId AND to_user_id = :toUserId
Also tried doing it with the FROM clause
LEFT OUTER JOIN (SELECT DISTINCT sender.first_name AS sender_first_name, sender.last_name AS sender_last_name,
sender.username AS sender_username FROM users) users AS sender ON chats.from_user_id = sender.id
This is not a valid subquery:
(SELECT DISTINCT sender.first_name AS sender_first_name, sender.last_name AS sender_last_name,
sender.username AS sender_username) users AS sender
You haven't explained what the query is supposed to be doing, but I speculate that you want:
SELECT body, timestamp,
us.first_name AS sender_first_name, us.last_name AS sender_last_name, us.username AS sender_username
ur.first_name AS receiver_first_name, ur.last_name AS receiver_last_name, ur.username AS receiver_username
FROM chats c LEFT OUTER JOIN
messages m
ON c.id = m.chat_id LEFT OUTER JOIN
users us
ON c.from_user_id = us.id LEFT OUTER JOIN
users ur
ON c.to_user_id = ur.id
WHERE c.from_user_id = :fromUserId AND c.to_user_id = :toUserId;
You should not need DISTINCT when joining to a table on the id.

how can I select three tables with join in mysql?

I want join this tables in only query mysql, I'm trying but it doesn't work.
I want get by idtravel as this pseudocode:
select *
from travel t
left join user
left join vehicule
where idtravel = ?
select *
from
(select *
from vehicle v
left join user u on v.user_idv= u.id
select *
from travel t
left join user u on t.user_idt= u.id) as res
select *
from travel t
left join `user` u on u.id = t.user_idt
left join vehicle v on u.id = v.user_idv
where t.idtravel = ?
SELECT user.*, vehicle.name as vehicale_name, travel.* FROM user
left join `vehicle` on vehicle.user_id = user.id
left join `travel` on travel.user_id = user.id
where travel.idtravel = ?
SELECT * FROM user u
LEFT JOIN travel t ON u.id = t.user_idt
LEFT JOIN vehicle v ON u.id = v.user_idv
WHERE t.idtravel = ?;

MYSQL: Handling Multiple LEFT JOINS

I have a query with one LEFT JOIN that works fine. When I add a second LEFT JOIN to a table with multiple records per field in the first table, however, I am getting the product of the results in the two tables ie books x publishers returned. How can I prevent this from happening?
SELECT a.*,b.*,p.*, group_concat(b.id as `bids`)
FROM authors `a`
LEFT JOIN books `b`
ON b.authorid = a.id
LEFT JOIN publishers `p`
on p.authorid = a.id
GROUP by a.id
EDIT:
Figured it out. The way to do this is to use subqueries as in this answer:
SELECT u.id
, u.account_balance
, g.grocery_visits
, f.fishmarket_visits
FROM users u
LEFT JOIN (
SELECT user_id, count(*) AS grocery_visits
FROM grocery
GROUP BY user_id
) g ON g.user_id = u.id
LEFT JOIN (
SELECT user_id, count(*) AS fishmarket_visits
FROM fishmarket
GROUP BY user_id
) f ON f.user_id = u.id
ORDER BY u.id;
If you do multiple LEFT Joins, your query will return a cartesian product of the results. To avoid this and get only one copy of fields you desire, do a subquery for each table you wish to join as below. Hope this helps someone in the future.
SELECT u.id
, u.account_balance
, g.grocery_visits
, f.fishmarket_visits
FROM users u
LEFT JOIN (
SELECT user_id, count(*) AS grocery_visits
FROM grocery
GROUP BY user_id
) g ON g.user_id = u.id
LEFT JOIN (
SELECT user_id, count(*) AS fishmarket_visits
FROM fishmarket
GROUP BY user_id
) f ON f.user_id = u.id
ORDER BY u.id;

LEFT JOIN latest row using a date column

I am trying to select the latest row from a LEFT JOIN not on the main query. This is my SQL:
SELECT *
FROM messages
LEFT JOIN users
ON messages.message_to = users.user_id
OR messages.message_user = users.user_id
LEFT JOIN message_replies
ON messages.message_id = message_replies.message_reply_main
LEFT JOIN user_personal_information
ON users.user_id =
user_personal_information.user_personal_information_user
I want to select the latest row from:
LEFT JOIN message_replies
ON messages.message_id = message_replies.message_reply_main
My column is called: message_reply_date - how can I use that to LEFT JOIN the latest row?
message_replies:
CREATE TABLE IF NOT EXISTS `message_replies` (
`message_reply_id` int(11) NOT NULL,
`message_reply_user` int(11) NOT NULL,
`message_reply_main` int(11) NOT NULL,
`message_reply_message` text NOT NULL,
`message_reply_date` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`message_reply_read` int(11) NOT NULL DEFAULT '0'
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
I am using this for the WHERE clause:
WHERE m.message_user = ?
OR m.message_to = ?
AND m.message_deleted=0
AND m.message_permdeleted=0
GROUP BY mr.message_reply_main
ORDER BY mr.message_reply_date DESC
If i understood the question, i'll do it this simple way:
SELECT *
FROM messages
LEFT JOIN users
ON messages.message_to = users.user_id OR messages.message_user = users.user_id
LEFT JOIN message_replies
ON messages.message_id = message_replies.message_reply_main
LEFT JOIN user_personal_information
ON users.user_id = user_personal_information.user_personal_information_user
WHERE message_replies.message_reply_date = (SELECT MAX(message_reply_date) FROM message_replies WHERE message_reply_main = messages.message_id)
/* AND more criterias */
No grouping in the main query but just using a MAX evaluation of message_reply_date in a subquery regarding the WHERE criteria on message_reply_date itself.
There is a "left join" method for getting the most recent message (as well as several others). But in keeping with the preferred join method of the question:
SELECT *
FROM messages m LEFT JOIN
users u
ON m.message_to = u.user_id OR m.message_user = u.user_id LEFT JOIN
message_replies mr
ON m.message_id = mr.message_reply_main LEFT JOIN
user_personal_information upi
ON u.user_id = upi.user_personal_information_user LEFT JOIN
message_replies mr2
ON mr2.message_reply_main = m.message_id AND
mr2.message_reply_date > mr.message_reply_date
WHERE mr2.message_reply_main IS NULL;
I also added table aliases because they make a query easier to write and to read.
The idea is to match to the table again, but only for later messages. Then, the WHERE clause checks that none exist -- so it has the latest one.
Give this a go.
SELECT *
FROM messages
LEFT JOIN users
ON messages.message_to = users.user_id
OR messages.message_user = users.user_id
LEFT JOIN ( SELECT *
, MAX(message_reply_date) OVER (PARTITION BY message_id) AS most_recent_message_reply_date
FROM message_replies ) message_replies
ON messages.message_id = message_replies.message_reply_main
AND message_replies.message_reply_date = message_replies.most_recent_message_reply_date
LEFT JOIN user_personal_information
ON users.user_id =
user_personal_information.user_personal_information_user
I've substituted your direct call to the message_replies table with a Select that displays the table and the maximum reply date grouped by the message_id. I've then included this in the join to filter all answers other than the one your looking for.
Have a go, any problem drop me a message, always happy to help.
What you want to do is emulate the rank & partition functionality seen in MSSQL & PL/SQL, essentially ranking on date & partitioning by message_reply_main.
This can be done in MySQL using an outer join & a count on your partition, while using a < or > in the join for the ranking criteria. This allows you to do your standard join, but also where rank = 1, returning the largest date.
SELECT *
FROM messages
LEFT JOIN users
ON messages.message_to = users.user_id
OR messages.message_user = users.user_id
LEFT JOIN (select mri.message_reply_main
, mri.message_reply_date
, mri.message_reply_message
, mri.message_reply_read
, count(mri2.message_reply_main) + 1 as rank
FROM message_replies mri
left join message_replies mri2 on mri.message_reply_date < mri2.message_reply_date
and mri.message_reply_main = mri2.message_reply_main
group by mri.message_reply_main, mri.message_reply_message
, mri.message_reply_read, mri.message_reply_date) mr ON messages.message_id = mr.message_reply_man
AND mr.rank = 1
LEFT JOIN user_personal_information
ON users.user_id =
user_personal_information.user_personal_information_user

How can we show records even if id is null?

SELECT groups.*
, roles.rol_display_name
, users.usr_fname
FROM groups, groups_roles, roles, users
WHERE groups.id = groups_roles.groups_id and
roles.id = groups_roles.roles_id and
groups.grp_manager_id = users.id
In my query if all the AND conditions are true then only it will show all the records but my requirement is even if manager.id is null it should show all the records with users.usr_fname as null.
Use left join instead of inner join (implicit or otherwise) when you join users table:
SELECT g.*
, r.rol_display_name
, u.usr_fname
FROM groups g
JOIN groups_roles gr on g.id = gr.groups_id
JOIN roles r on r.id = gr.roles_id
LEFT JOIN users u on g.grp_manager_id = u.id