Select all from table where user_id exists in both tables - mysql

I am creating a small chat/ message system. I have four tables: thread, thread_participant, message and message_read_state. I know it might not be the best solution, but I just wanted to give this a go for a project of mine. How can i select all new messages coming in for a specific user without selecting the his/her own message?
thread table
id
----------
1
2
thread_participant table
thread_id user_id
---------- --------
1 6
1 7
message table
id thread_id send_date message sending_user_id
---------- -------- -------- -------- --------
1 1 2016-05-12 HELLO 7
2 1 2016-05-12 HELLO BACK 6
message_read_state
message_id user_id s read_date
----- -------- --------
1 6 2016-05-12
2 7 2016-05-12
EDIT
What I have so far. This select all the threads I have participated, but I would like to display only the incoming message, without my response.
SELECT message.id, message.send_date, message.message, tbl_users.user_id, message.thread_id
, (SELECT message_read_state.read_date
FROM message_read_state
WHERE message_read_state.message_id = message.id
and message_read_state.user_id = 6) AS ReadState
FROM message INNER JOIN tbl_users ON message.sending_user_id = tbl_users.user_id
WHERE ( message.id in
( SELECT (message.id)
FROM thread_participant INNER JOIN message
ON thread_participant.thread_id = message.thread_id
WHERE thread_participant.user_id = 6
GROUP BY thread_participant.user_id
)
)
ORDER BY message.send_date ASC;

What about this;)
SELECT message.id, message.send_date, message.message, tbl_users.user_id, message.thread_id
, (SELECT message_read_state.read_date
FROM message_read_state
WHERE message_read_state.message_id = message.id
and message_read_state.user_id = 6) AS ReadState
FROM message INNER JOIN tbl_users ON message.sending_user_id = tbl_users.user_id
WHERE ( message.id in
( SELECT (message.id)
FROM thread_participant INNER JOIN message
ON thread_participant.thread_id = message.thread_id
WHERE thread_participant.user_id = 6
GROUP BY thread_participant.user_id
)
)
AND message.sending_user_id <> 6
ORDER BY message.send_date ASC;
I've just add AND message.sending_user_id <> 6 in your original sql. If you don't want response, just let sending_user_id is not you, then you get what you want. :D

Related

Get the last message for each thread

SQL Fiddle
http://sqlfiddle.com/#!2/1c5fc3/1
I am trying to create simple messaging system, but I am having trouble with the desired results from the SQL queries.
Here are the tables I have; I am trying to get INBOX data..
INBOX Definiton for this problem:
This should be threaded display in inbox, ie. google mail, but only to show the last message in that thread with the user who originaly created the thread and the last user who replied in the thread, if the last user is the same user that created the thread and there are no replies in beetween the message doesnt belog in inbox.
TABLES:
THREAD
id_thread
id_last_message
id_user_inital
id_user_last
THREAD_USERS
id
id_thread
id_user
THREAD_MESSAGES
id_thread_messages
id_user_sender
id_thread
datetime
subject
body
MESSAGE_STATUS
id_messsage_status
id_thread_messages
id_user
status
datetime
My logic is:
once a message has been sent
THREAD
id_thread id_last_message id_user_inital id_user_last
1 1 1 1
THREAD_USERS
id id_thread id_user
1 1 1
2 1 2
THEREAD_MESSAGES
id_thread_messages id_user_sender id_thread datetime subject body
1 1 1 07.09.2014 16:02 'title' 'text message'
MESSAGE_STATUS
id_message_status id_thread_messages id_user status datetime
1 1 1 4 07.09.2014 16:02
2 1 2 1 07.09.2014 16:02
Lets say status can be
0 = deleted (do not show at all)
1 = new (show only to user that is on the receiving end)
2 = read (this status will be shown to all users in the thread)
3 = replied (show only to user that makes this action)
4 = sent (show only to user that makes this action)
Query :
SELECT *
FROM thread
JOIN thread_users
ON thread.id_thread = thread_users.id_thread
JOIN thread_messages
ON thread.id_thread = thread_messages.id_thread
JOIN message_status
ON thread_messages.id_thread_messages = message_status.id_thread_messages
WHERE
thread_users.id_user = 2
AND message_status.status != 0
AND message_status.status != 4
AND thread.id_user_last != message_status.id_user
sample data
THREAD
id_thread id_last_message id_user_inital id_user_last
1 4 1 2
2 2 3 3
3 3 4 4
THREAD_USERS
id id_thread id_user
1 1 1
2 1 2
3 2 3
4 2 2
5 3 4
6 3 2
THEREAD_MESSAGES
id_thread_messages id_user_sender id_thread datetime subject body
1 1 1 07.09.2014 16:02 'title' 'text message'
2 3 2 07.09.2014 16:05 'hey two' 'foo'
3 4 2 07.09.2014 16:07 'hey two' 'bar'
4 2 1 07.09.2014 16:10 'title' 'replay on 1st'
MESSAGE_STATUS
id_message_status id_thread_messages id_user status datetime
1 1 1 4 07.09.2014 16:02
2 1 2 1 07.09.2014 16:02
3 2 3 4 07.09.2014 16:05
4 2 2 1 07.09.2014 16:05
5 3 4 4 07.09.2014 16:07
6 3 2 1 07.09.2014 16:07
7 4 2 4 07.09.2014 16:10
8 4 1 1 07.09.2014 16:10
How would you extract INBOX data from this situation, as I am spinning in circles for hours and can't quite get what I am doing wrong.
Thank you.
Updated solution after taking into account explanations for message status:
SELECT DISTINCT t.*, tm.* , ms.*
FROM thread t
-- tm should be last message
INNER JOIN thread_messages tm ON t.id_thread = tm.id_thread
INNER JOIN message_status ms ON (ms.id_thread_messages = tm.id_thread_messages)AND
(ms.id_user=2)AND
(ms.status!=0)
-- try to find message after tm, and then in WHERE filter only those cases where there is no message after tm
LEFT JOIN thread_messages tm_next
INNER JOIN message_status ms_next ON (ms_next.id_thread_messages = tm_next.id_thread_messages)AND
(ms_next.id_user=2)AND
(ms_next.status!=0)
ON (t.id_thread = tm_next.id_thread)and
(tm_next.datetime>tm.datetime)
LEFT JOIN thread_messages tm_other
INNER JOIN message_status ms_other ON (ms_other.id_thread_messages = tm_other.id_thread_messages)AND
(ms_other.id_user=2)AND
(ms_other.status!=0)
ON (t.id_thread = tm_other.id_thread)and
(tm_other.id_thread_messages!=tm.id_thread_messages)and
(tm_other.id_user_sender!=2)
WHERE
-- ensure tm is last message in thread
(tm_next.id_thread is null)and
(
-- there is a non deleted message from another user in current thread
(tm_other.id_thread_messages is not null)or
-- last message is not from current user
(tm.id_user_sender!=2)
)
SqlFiddle is here.
Let me know is this working for you.
i think this is the solution that you are looking for
SELECT * FROM thread
JOIN thread_users ON thread.id_thread = thread_users.id_thread
JOIN thread_messages ON thread.id_thread = thread_messages.id_thread
JOIN message_status ON thread_messages.id_thread_messages = message_status.id_thread_messages
WHERE thread_users.id_user = 2
AND thread_users.id_user = message_status.id_user
AND message_status.status != 0
AND message_status.status != 4
AND thread.id_user_last != message_status.id_user
Now with the description of what INBOX means I suggest you rely heavily on EXISTS clause in your query. Here is just an example of how it can look like:
SELECT *
FROM thread t INNER JOIN
thread_messages tm ON t.id_thread = tm.id_thread
WHERE
EXISTS ( -- User is in the thread.
SELECT * FROM thread_users tu
WHERE t.id_thread = tu.id_thread AND tu.id_user = 2
)
AND NOT EXISTS ( -- Exclude earlier messages for thread.
SELECT * FROM thread_messages WHERE
tm.id_thread = id_thread AND datetime > tm.datetime
AND EXISTS (-- Exclude deleted messages here
SELECT * FROM message_status
WHERE thread_messages.id_thread_messages = id_thread_messages
AND status != 0
AND status != 4
)
)
AND EXISTS ( -- Include messages that were not deleted or send to self.
SELECT * FROM message_status
WHERE tm.id_thread_messages = id_thread_messages
AND status != 0
AND status != 4
AND t.id_user_last != id_user -- not sure what is this for
)
AND EXISTS ( -- Include threads with messages from several users
SELECT * FROM thread_messages
WHERE tm.id_thread = id_thread
AND tm.id_user_sender != t.id_user_inital)
http://sqlfiddle.com/#!2/1c5fc3/39

Select inner join with n_m table

I have three tables as following:
USERS TABLE
id_user| name |
---------------
1 | ...
2 | ...
SERVICES TABLE
id_service | name |
-------------------
1 | ...
2 | ...
3 | ...
USER_SERVICES TABLE (n-m)
id_user | id_service
--------------------
1 | 1
1 | 2
2 | 1
And I need to do a SELECT starting from "SELECT * FROM users" and then, getting the users by services. Ex. I need to get every user with services = 1 and services = 2 (and maybe he has other more services, but 1 and 2 for sure).
I did the following:
SELECT *
FROM `users`
INNER JOIN user_services ON users.id_user = user_services.id_user
WHERE id_service=1 AND id_service=2
But this, of course dont works since there is not a single record matching service = 1 and service = 2.
What can I do?
Add an extra join for the other service you want to check:-
SELECT *
FROM `users`
INNER JOIN user_services us1 ON users.id_user = us1.id_user AND us1.id_service=1
INNER JOIN user_services us2 ON users.id_user = us2.id_user AND us2.id_service=2
select t.*,
(select count(*) from user_services where id_user = t.id_user) how_much
from users t;
Is this what you want???
It shows the data of the users and how much services are in the services table. Other possibility is this:
select t.*,
(case when (select count(*)
from user_services where id_user = 1) > 0
then 'service1'
else 'null'
end) has_service_1
from users t;
The problem with this select is that you have to repeat this case...end as much times as id_services you have, so it doesn't make sense if the number of services is increasing over time. On the contrary, if it is a somewhat fixed number, and it is not a big number, this could be a solution.

provide extra return column of boolean type which evaluates another query

I am trying to develop a query that will
Give the last message in every thread that was not written by the currentUserID (right now accomplished with group by thread_id, order by message_id desc AND WHERE results.sender_id != currentUserID)
If the user started a thread but received no response then the thread should not show up (right now accomplished by simply filtering the data with WHERE results.sender_id != currentUserID)
Provide a boolean column to tell if a response was sent in this thread (not accomplished but would involve finding the last element from the query and seeing if the sender_id is = to current logged in user id)
http://sqlfiddle.com/#!2/85971/2
Synopsis
User 1 sends a message with User 2 with
Thread ID: 1
Subject: 'Hi Subject'
Body: 'Hi Body'
User 2 responds:
Thread ID: 1
Body: 'Hi Body Response'
As User 1 the SQL Should Return
THREAD_ID |SUBJECT | SENDER_ID | BODY | Responded
1 | Hi Subject | 2 | Hi Body Response | false
As User 2 the SQL Should Return
THREAD_ID |SUBJECT | SENDER_ID | BODY | Responded
1 | Hi Subject | 1 | Hi Body | true
Please see fiddle
My shot at it was the following which takes acre of 1 and 2 but not 3, I tried to mess around with UNIONS to achieve this but got nowhere.
SELECT * FROM (
SELECT t.id as thread_id, t.subject, m.sender_id, m.body
FROM messages m
JOIN threads t ON (m.thread_id = t.id)
ORDER BY t.id asc, m.cdate desc
) as results
WHERE results.sender_id != currentUserID
GROUP BY results.thread_id;
Check the queries below. I also added the message ID column.
First Result ...
SELECT t.id as thread_id, t.subject, m.sender_id, m.body, m.id,
(SELECT CASE s.status WHEN 1 THEN 'true' ELSE 'false' END
FROM STATUS s where s.message_id = m.id and s.user_id = 1) Responded
FROM messages m
JOIN threads t ON (m.thread_id = t.id)
WHERE m.sender_id != 1
Second one...
SELECT t.id as thread_id, t.subject, m.sender_id, m.body, m.id,
(SELECT CASE s.status WHEN 1 THEN 'true' ELSE 'false' END
FROM STATUS s where s.message_id = m.id and s.user_id = 2) Responded
FROM messages m
JOIN threads t ON (m.thread_id = t.id)
WHERE m.sender_id != 2
Thanks!
#leo.

MySQL: get count value in joins and if else

In My application I want to display all my friends and no of cheque given and received
Table Transaction Table Friends
---------------------------- -----------------------------
id given_id rev_id amt id who_id whom_id who_name
----------------------------- -------------------------------
1 2 1 1k 1 1 2 sss
2 2 3 1k 2 3 2 fff
3. 3 2 2k 3 4 1 eee
4 1 2 2k 4 2 1 iii
----------------------------- ------------------------------
Result whom_id=2 name=iii -> Friends (sss,fff)
=> sss gives totally 1 cheque and
sss receives totally 1 cheque and
fff gives 1 cheque and
fff receives 1 cheque and
I tried This one..
SELECT
p.who_id,
p.who_name,
COUNT( r1.give_id ) ,
COUNT( r1.rec_id )
FROM
friends p
LEFT JOIN Transaction r1
ON p.who_id = r1.give_id OR p.who_id = r1.rec_id
WHERE
p.whom_id = 1
GROUP BY p.who_id
Please provide me the best way to do this....
You can do it like this
SELECT
f.who_name,
count(t.given_id) GivenTotal,
count(lt.rev_id) as RecievedTotal
from friends as f
left join transaction as t
on t.given_id = f.who_id
left join transaction as lt
on lt.rev_id = f.who_id
where f.whom_id = 2
group by t.given_id
Demo
Output
who_name | GivenTotal | RecievedTotal
----------------------------------
sss | 1 | 1
fff | 1 | 1
try this,
select
id,
count(given_id) as given,
count(rev_id) as reveive
from Friends
GROUP BY id;
and if you whant the difference between the two amounts try this
select
id,
count(given_id) as given,
count(rev_id) as reveive,
given-receive as diff
from Friends
GROUP BY id;
You can use HAVING with group by ,
instead
WHERE p.whom_id = 1
There are two ways to do this, both involve joining on the Transactions table twice.
You can use subqueries:
SELECT f.who_id,
f.who_name,
t1.TotalGiven,
t2.TotalRev
FROM friends f
LEFT JOIN
(
select count(t.given_id) TotalGiven, t.given_id
from Transactions t
group by t.given_id
) t1
ON f.who_id = t1.given_id
LEFT JOIN
(
select count(t.rev_id) TotalRev, t.rev_id
from Transactions t
group by t.rev_id
) t2
ON f.who_id = t2.rev_id
where f.whom_id = 2;
See SQL Fiddle with Demo.
Or you can just use a join with no subquery:
select f.who_id,
f.who_name,
count(t1.given_id) TotalGiven,
count(t2.rev_id) TotalRev
FROM friends f
LEFT JOIN Transactions t1
ON f.who_id = t1.given_id
LEFT JOIN Transactions t2
ON f.who_id = t2.rev_id
where f.whom_id = 2
group by f.who_id, f.who_name
See SQL Fiddle with Demo
Finally I got the answer,
SELECT
p.who_id, p.who_name, COUNT( r1.give_id ) , COUNT( r2.rec_id )
FROM
friends p
LEFT JOIN Transaction r1 ON p.who_id = r1.give_id
LEFT JOIN Transaction r2 on p.who_id = r2.rec_id
WHERE
p.whom_id = 1
GROUP BY p.who_id

sql Group by and max() doesn't get the right results

I have the following table
mes_id | user_a | user_b | message | room | time
------------------------------------------------------
1 | 23 | 24 | hello | 1 | 5676766767
user_a sent a message for user_b in room 1
I want to get the all the last messages received for a specific user (for example user_b) per room, I have tried few queries but I didn't get the right one.
This solution didn't work for me: sql_group_by_max
An update
I am using 5.5.18 MySQL Server
ok
this gave me the result thanks
SELECT *
FROM messeges C
JOIN (
SELECT room, user_a, MAX( messeges.date ) AS max_time
FROM messeges
GROUP BY room, user_a
)a
ON a.room = c.room
AND a.user_a = c.user_a
AND a.max_time = c.date
WHERE c.user_b = '396'
try this:
select * from chat C join
(select room,user_b,MAX([time]) as max_time
from chat
group by room,user_b)a
on a.room=c.room
and a.user_b=c.user_b
and a.max_time=c.[time]
If you want multiple rows, (In sql server)
with cte as (select *,ROW_NUMBER ()
over (partition by room,user_b order by [time] desc) as rownum from chat)
select * from cte order by rownum
try this:
Select *
FROM chat a
INNER JOIN ( Select user_a, user_b , room ,MAX(time) as max_time
FROM chat
group by user_a, user_b , room) b on a.user_a=b.user_a and a.user_b=b.user_b and a.room=b.room