Get all conversations from one user in different chats MYSQL - mysql

I am working on a facebook style chat and am trying to get all the latest conversations from one user with other users.
Right now I am getting all the latest conversations but in the case that the other user has written to me. What I want to do is for example I have 2 conversations, conversation 1 is the latest I wrote into, but now I write into conversation 2, when I refresh the page conversation 2 will be first.
Now if the other person of conversation 1 writes me, if I refresh page conversation 1 will be first.
This are the 2 tables I am working with:
CREATE TABLE users (
id int(255) not null auto_increment,
username varchar(150) null,
email varchar(150) null,
password varchar(255) null,
salt varchar(255) null,
pic varchar(255) not null,
primary key(id)
) ENGINE = INNODB DEFAULT CHARSET=utf8;
CREATE TABLE chat (
id int(255) not null auto_increment,
id_us int(255) not null,
id_receptor int(255) not null,
message varchar(3000) not null,
chat_date datetime not null,
primary key (id),
index chatUsId(id_us),
index chatRecId(id_receptor),
foreign key (id_us) references users(id) on delete cascade,
foreign key (id_receptor) references users(id) on delete cascade
) ENGINE = INNODB DEFAULT CHARSET=utf8;
And this is the query I am using
SELECT m.id_us, m.id_receptor, u.username, u.pic
FROM users AS u, chat AS m
WHERE (m.id_us = u.id AND m.id_receptor =:id_us or m.id_receptor = u.id AND m.id_us =:id_us)
AND m.chat_date = (SELECT MAX( c.chat_date ) FROM chat AS c WHERE m.id_us = c.id_us AND m.id_receptor = c.id_receptor )
group by u.username
ORDER BY m.id DESC
In this case :id_us could be for example 1
I know its not correct as it is not doing what I would like it to do, but I've been struggling trying to find the way to get the results as I want, but I am stucked, any help will be apreciated.
To explain myself better, what I am trying to do is for example when you chat in facebook, the facebook messages list to view your latest conversations.

OK how about this: (using 123 for id of user1)
select T1.user2_id, users.username, users.pic, max(cdate) maxDate from
(select chat.id_receptor user2_id, max(chat_date) cdate
from chat
where chat.id_us=123
group by chat.id_receptor
union distinct
(select chat.id_us user2_id, max(chat_date) cdate
from chat where chat.id_receptor = 123
group by chat.id_us)) T1
inner join users on (users.id = T1.user2_id)
group by T1.user2_id
order by maxDate desc

I want recent message also like whatsapp, and I have update your query as per my tables.
select T1.user2_id, message, user_profile.user_name, user_profile.avatar, max(cdate) message_date from (select messages.receiver_id user2_id, messages.message, (message_date) cdate from messages where messages.sender_id=2 group by messages.receiver_id union distinct(select messages.sender_id user2_id, messages.message, max(message_date) cdate from messages where messages.receiver_id = 2 group by messages.sender_id)) T1 inner join user_profile on (user_profile.id = T1.user2_id) group by T1.user2_id order by message_date desc

Just added another SELECT + JOIN. Please refer to below and let me know if any questions, will try to answer.
I would like to note that even though the above answer is marked is correct, in my understanding it does not covers the full needs of requested query as does not display the key item - i.e. last message per each chat.
$sql="
SELECT T2.maxDate, T2.user2_id, T2.ava, T2.userName,chat.user_to,chat.user_from,chat.body,chat.viewed FROM (SELECT T1.user2_id, users.userName, users.ava, max(cdate) maxDate FROM
(SELECT chat.user_to user2_id, max(msg_time) cdate
FROM chat WHERE chat.user_from=18
GROUP BY chat.user_to
union distinct
(SELECT chat.user_from user2_id, max(msg_time) cdate
FROM chat WHERE chat.user_to=18
GROUP BY chat.user_from)) T1
inner join users on (users.userID = T1.user2_id)
group by T1.user2_id
order by maxDate desc) T2
join chat on (T2.maxDate = chat.msg_time) ORDER BY T2.maxDate DESC";

Related

How to find percentage within a group in sql that has inner join as subquery?

I have 3 tables like this
create table Users (id serial primary key, country varchar(100) not null);
create table tweets(id serial primary key, user_id int, text varchar(100) not null, CONSTRAINT FK_TWEETSUSERS FOREIGN KEY (user_id)
REFERENCES Users(id));
create table Logins(user_id int, client varchar(100), CONSTRAINT FK_LOGIN_USERS FOREIGN KEY (user_id)
REFERENCES Users(id));
insert into Users
values
(1,'Japan'),
(2, 'Moroco'),
(3,'Japan'),
(4,'India'),
(5,'India'),
(6,'Japan'),
(7,'Moroco'),
(8,'China');
insert into tweets
values
(733,1,'I love #food'),
(734,1,'I love food'),
(735,2,'I love #food'),
(736,5,'I love food'),
(737,6,'I love #food'),
(738,3,'I love #food'),
(739,8,'I love #food');
insert into Logins
values
(1,'mobile-ios'),
(2,'mobile-ios'),
(3,'mobile-ios'),
(4,'web'),
(8,'mobile-ios');
I need to find percentage of users from each country whose users have used '#food' in their tweets and the other condition is that user should have logged in using 'mobile' device
I have written the following query so far -
select t.country, count(t.country) as tweet_users
from
(select Mobile_User_Tweets.user_id, U.country from Users as U
inner join
(select distinct user_id from tweets
where text like '%#food%'
and user_id in (select distinct user_id
from Logins
where client like '%mobile-%')) as Mobile_User_Tweets
on U.id = Mobile_User_Tweets.user_id) as t
group by t.country ;
This gives the number of users from a country that have user #food in their tweets
Result below -
country tweet_users
Japan 2
Moroco 1
China 1
I want the following result -
country tweet_users
Japan 66.67 -------------> (2 out of 3 users from Japan)
Moroco 50 -------------> (1 out of 2 users from Moroco)
China 100 -------------> (1 out of 1 user from China)
I tried number of different queries to find the percentage but haven't been able to get the result?
Can some one help me with this?
One way to achieve the results you want is to check in a derived table whether a user has made any tweets about #food; then you can LEFT JOIN that table to Users and Logins to determine the average number of users from each country that have logged in from mobile and tweeted about food:
SELECT u.country,
AVG(COALESCE(t.tfood, 0) AND COALESCE(l.client, '') LIKE '%mobile-%') * 100 AS tweet_users
FROM Users u
LEFT JOIN Logins l ON l.user_id = u.id
LEFT JOIN (
SELECT user_id, MAX(text LIKE '%#food%') AS tfood
FROM tweets
GROUP BY user_id
) t ON t.user_id = u.id
GROUP BY u.country
Output:
country tweet_users
China 100.0000
India 0.0000
Japan 66.6667
Moroco 50.0000
If you don't want countries with no users that meet the criteria, just add HAVING tweet_users > 0 to the end:
SELECT u.country,
AVG(COALESCE(t.tfood, 0) AND COALESCE(l.client, '') LIKE '%mobile-%') * 100 AS tweet_users
FROM Users u
LEFT JOIN Logins l ON l.user_id = u.id
LEFT JOIN (
SELECT user_id, MAX(text LIKE '%#food%') AS tfood
FROM tweets
GROUP BY user_id
) t ON t.user_id = u.id
GROUP BY u.country
HAVING tweet_users > 0
Demo on dbfiddle
Note this code takes advantage of the fact that in a numeric context, MySQL treats boolean expressions as 1 (true) or 0 (false).
Note that if a user might have multiple entries in the Logins table, you need to make a derived table from that too:
SELECT u.country,
AVG(COALESCE(t.tfood, 0) AND COALESCE(l.mclient, 0)) * 100 AS tweet_users
FROM Users u
LEFT JOIN (
SELECT user_id, MAX(client LIKE '%mobile-%') AS mclient
FROM Logins
GROUP BY user_id
) l ON l.user_id = u.id
LEFT JOIN (
SELECT user_id, MAX(text LIKE '%#food%') AS tfood
FROM tweets
GROUP BY user_id
) t ON t.user_id = u.id
GROUP BY u.country
Demo on dbfiddle

Using IN operator with a subquery

Having the following scheme
CREATE TABLE request (
`id` int,
`classroom` varchar(255),
`school` varchar(255),
`date_of_application` datetime,
`status` varchar(100),
`user_id` bigint(20) NOT NULL
);
CREATE TABLE user (
`id` int,
`full_name` varchar(255)
);
CREATE TABLE user_schools (
`user_id` int,
`schools_string` varchar(255)
);
I need to obtain a list of requests where the status field is equal to 'pending' and where the school field is located in the list of schools associated with a user
Right now i am trying the following query
SELECT
r.id,
u.full_name,
r.date_of_application,
r.classroom
FROM
request r
inner join
user u on u.id = r.user_id
WHERE
r.school IN (
SELECT
us.schools_string
FROM
user_schools us
WHERE
us.user_id = u.id
)
AND r.status = 'pending';
I currently get the total list of requests. I understand what is failing is the subquery.
Attached a sqlfiddle to facilitate your collaboration
The result I expect are three applications where applicants are: user user, user user and another assistant user
Thanks for any idea. If there is a better solution, I thank you.
Regards
The code is doing what you are asking. If you want user/requests that are in the list, then you can use select distinct like this:
SELECT DISTINCT u.full_name, r.date_of_application
FROM request r inner join
user u
on u.id = r.user_id
WHERE r.school IN (SELECT us.schools_string
FROM user_schools us
WHERE us.user_id = u.id
) AND
r.status = 'pending';
But if you want the classroom and request id, then you are going to get your original result set (or some variation).

mysql find recent user comments (part 2)

Cont. on mysql find recent user comments
Again, 2 tables:
create table user(
userID int auto_increment,
userName varchar(10),
userCreatedDate timestamp,
primary key(userID)
);
create table comment(
commentID int auto-increment,
userID int,
comment varchar(100),
primary key(commentID),
foreign key(userID) references user(userID)
);
This time the userCreateDate and commentID value is different from part 1.
And I want to find recent comment from the database.
My output like the following:
Here is the query that I tried:
select u.userID, max(c.commentID) as commentID, c.comment, u.userCreatedDate
from comment c
left join user u on c.userID = u.userID
group by u.userID
order by u.userCreateDate desc
However, I can't get my output.
Can someone help me?
You can do this as below and is called group wise max/min http://dev.mysql.com/doc/refman/5.7/en/example-maximum-column-group-row.html
select u.*,c.comment from user u
join comment c on c.userID = u.userID
join (
select max(commentID) as commentID,userID
from comment group by userID
)c1 on c1.commentID = c.commentID
try this query :-
select u.userID,commentID, c.comment, u.userCreatedDate
from comment c
left join user u on c.userID = u.userID
WHERE commentID=(SELECT MAX(c2.commentID)
FROM comment c2
WHERE c.userID = c2.userID)

mysql find recent user comments

I have 2 tables: user and comment.
create table user(
userID int auto_increment,
userName varchar(10),
userCreatedDate timestamp,
primary key(userID)
);
create table comment(
commentID int auto-increment,
userID int,
comment varchar(100),
primary key(commentID),
foreign key(userID) references user(userID)
);
And I want my output like the following:
I want to find recent comment from the database:
However, I tried like this and cannot get my output:
select u.userID, c.commentID, u.userCreateDate
from comment c
left join userID u on c.userID = s.userID
order by u.userCreateDate desc
How should I modify so that I can get my output?
You don't want to order it by the userCreateDate from the user table, because that will be the same for all comments. It would have been nice for you to have a commentCreateDate but since you don't have that you can order it by the commentID.
Change
order by u.userCreateDate desc
to
order by c.commentID desc
Edited to add:
You also need to change your select to get the actual comment.
select u.userID, c.commentID, u.userCreateDate
should be
select u.userID, c.commentID, c.comment, u.userCreateDate
to get your desired output. Plus I don't think you want to include userCreateDate in the output as people could confuse that with the date the comment was written.

Alternative to GROUP_CONCAT? Multiple joins to same table, different columns

I'm not even sure of the correct terminology here. MySQL newbie, more or less.
Given a couple tables defined as follows:
CREATE TABLE users
( user_id int(11) NOT NULL auto_increment
, name VARCHAR(255)
, pri_location_id mediumint(8)
, sec_location_id mediumint(8)
, PRIMARY KEY (user_id)
);
CREATE TABLE locations
( location_id mediumint(8) NOT NULL AUTO_INCREMENT
, name varchar(255)
, PRIMARY KEY (location_id)
)
I'm trying to do a query to get the user name and both primary and secondary locations in one go.
I can get one like this:
SELECT u.name AS user_name, l.name as primary_location FROM users u, locations l WHERE u.primary_location_id=l.location_id
But I'm drawing a total blank on the correct syntax to use to get both in one query.
SELECT u.name AS user_name, l1.name as primary_location , l2.name as secondary_location
FROM users u
JOIN locations l1 ON(u.pri_location_id=l1.location_id)
JOIN locations l2 ON(u.sec_location_id = l2.location_id);
First of, I would strongly consider changing your DB schema if allowable to add a users_locations table that can be used to properly describe this many to many relationship.
This table could look like:
user_id location_id location_type
1 1 primary
1 2 secondary
2 1 secondary
2 2 primary
and so forth.
You would likely want a compound primary key across all three columns. And location_type might best be enum data type.
Your query would then be like
SELECT
u.name AS user_name
l.name AS location_name
ul.location_type AS location_type
FROM users AS u
INNER JOIN user_location AS ul /* possibly use left join here if user can exist without a location */
ON u.user_id = ul.user_id
INNER JOIN locations AS l
ON ul.location_id = l.location_id
ORDER BY ul.location_type ASC
This would return up to two records per user (separate record for primary and secondary, primary listed first)
If you need this collapsed to a single record you could do this:
SELECT
u.name AS user_name
COALESCE(CASE WHEN ul.location_type = 'primary' THEN l.name ELSE NULL END CASE) AS primary_location,
COALESCE(CASE WHEN ul.location_type = 'secondary' THEN l.name ELSE NULL END CASE) AS secondary_location
FROM users AS u
INNER JOIN user_location AS ul /* possibly use left join here if user can exist without a location */
ON u.user_id = ul.user_id
INNER JOIN locations AS l
ON ul.location_id = l.location_id
GROUP BY `user_name`
If however you are stuck with current schema, then solution by #Jlil should work for you.