How to do multiple encapsulated WHERE clauses - mysql

I am trying to create a chat app, and I wanted to pull up the conversation of user 1 and 2.
Table:
+----+---------------------+-----------+---------+
| id | message | from_user | to_user |
+----+---------------------+-----------+---------+
| 1 | hello trick | 1 | 2 |
| 2 | hi raf i am okay | 2 | 1 |
| 3 | how is jo doing | 1 | 2 |
| 4 | Hey | 2 | 3 |
| 5 | she is doing well | 2 | 1 |
| 6 | how is kc doing | 2 | 1 |
+----+---------------------+-----------+---------+
This is my failed query:
mysql> SELECT *
-> FROM Messages
-> WHERE from_user = 1
-> AND to_user = 2
-> AND to_user = 1
-> AND from_user = 2;
Empty set (0.00 sec)
How do I achieve selecting conversation of user 1 and 2. Is my design efficient for a chat application?
Expected Output:
+----+---------------------+-----------+---------+
| id | message | from_user | to_user |
+----+---------------------+-----------+---------+
| 1 | hello trick | 1 | 2 |
| 2 | hi raf i am okay | 2 | 1 |
| 3 | how is jo doing | 1 | 2 |
| 5 | she is doing well | 2 | 1 |
| 6 | how is kc doing | 2 | 1 |
+----+---------------------+-----------+---------+
ORDER BY id may be necessary

This should do it.
SELECT *
FROM Messages
WHERE (from_user = 1 AND to_user = 2)
OR (from_user = 2 AND to_user = 1);

mysql> SELECT *
-> FROM Messages
-> WHERE from_user in (1 , 2)
-> AND to_user in (1, 2);

A single row can't have both to_user=1 and to_user=2, so this query will return no rows. Instead, you need to use the logical or operator to separate between both "directions" of the conversation:
SELECT *
FROM messages
WHERE (from_user = 1 AND to_user = 2) OR (to_user = 1 AND from_user = 2)

Related

Fetching records from 3 different tables with a condition in SQL

I am having an issues with fetching a particular kind of record from the database.
I have three different tables
Friends
Followers
PictureGalleries
Here is a sample of what the table looks like
Friends:
|id | senderId | receiverId | accepted |
|---|----------| -----------| ---------|
| 1 | 1 | 12 | 1 |
| 2 | 12 | 2 | 1 |
| 2 | 12 | 2 | 1 |
Followers:
| id | userId | UserIsFollowing |
| -- | ------ | --------------- |
| 1 | 12 | 63 |
| 2 | 22 | 12 |
PictureGalleries:
| id | UserId |
| -- | ------ |
| 1 | 13 |
| 2 | 12 |
| 3 | 1 |
| 4 | 10 |
| 5 | 2 |
| 6 | 63 |
So now here is the Issue!
I want to select all from the Picture Galleries
Where the userid has a friendship relationship with userId 12 where accepted is 1
And
Where the userID 12 is following a particular user
So Basically the result I want to see is the picture gallery of the following users ID: 1,2, and 63 which will look like this:
| id | UserID |
| -- | ------ |
| 3 | 1 |
| 5 | 2 |
| 6 | 6 |
use union and sub-query to get desired result
select p.id,p.UserId from
( select UserIsFollowing as id from
Followers fl where userId =12
union select senderId from friends f
where f.receiverId =12 AND accepted=1
union select receiverId from friends f
where f.senderId =12 AND accepted=1
) as t join PictureGalleries p on t.id=p.UserId
I think this query shows what you want:
select * from PictureGalleries
join Followers on Followers.userId = PictureGalleries.UserId
where exists (select 1 from Friends where (Friends.senderId = PictureGalleries.UserId or Friends.receiverId = PictureGalleries.UserId) and accepted = 1)
and Followers.UserIsFollowing = :user_id
But I think your model can be improved
EDIT:
Maybe you said it wrong first when you've said:
"Where the userid has a friendship relationship with userId 12 where accepted is 1
AND Where the userID 12 is following a particular user"
I think you mean OR, so the SQL should be something like:
select * from PictureGalleries
where exists (select 1 from Friends where (Friends.senderId = PictureGalleries.UserId or Friends.receiverId = PictureGalleries.UserId) and accepted = 1)
OR exists (select 1 from Followers where Followers.userId = PictureGalleries.UserId and Followers.UserIsFollowing = :user_id

How to fetch two different type of user in SQL

I have a table in which i have four columns
id
user_type
user_role
org_id
Now if the user_type is 1 (i.e individual) then the user_role and org_id will be null,
but if the user_type is 2 (i.e organisation) then it will have org_id (id of the organisation) and user_role (his role in the organisation).
There are three type of role in an organisation
admin (only one person could be the admin).
finance secrearty (could be more than one).
approvers (also could be more than one).
What I want
I want to fetch only one person (probably admin of the organisation) if the user_type is 2 from each organisation and all the users having user_type 1.
My table
-------------------------------------
| tbl_user |
-------------------------------------
| id | user_Type | user_role | org_id|
--------------------------------------
| 1 | 1 | null | null |
| 2 | 2 | 1 | 1 |
| 3 | 2 | 2 | 1 |
| 4 | 2 | 3 | 1 |
| 5 | 2 | 3 | 1 |
| 6 | 1 | null | null |
| 7 | 2 | 1 | 2 |
| 8 | 2 | 2 | 2 |
| 9 | 2 | 3 | 2 |
| 10 | 2 | 3 | 2 |
Expected result
-------------------------------------
| tbl_user |
-------------------------------------
| id | user_Type | user_role | org_id|
--------------------------------------
| 1 | 1 | null | null |
| 2 | 2 | 1 | 1 |
| 7 | 2 | 1 | 2 |
| 6 | 1 | null | null |
Thanks.
Is this what you need?
SELECT * FROM YourTable t
WHERE t.user_type = 1
OR t.user_role = 1
This will give you the expected result but will also include ID = 6
SELECT *
FROM tbl_user
WHERE (user_Type = 1 AND user_role IS NULL)
OR (user_Type = 2 AND user_role = 1)
You can avoid it to be shown by placing a condition AND id != 6, there are no other condition that can avoid it to be returned
SELECT *
FROM tbl_user
WHERE ((user_Type = 1 AND user_role IS NULL)
OR (user_Type = 2 AND user_role = 1))
AND id != 6
Here you can look at live fiddle
select t1.*
from table t1
inner join (
select min(id)
from table
group by org_id
) t2
on (t1.id = t2.id
and t1.user_type = 2)
or t1.user_type = 1;

SQL: How to aggregate multiple rows according to commutable columns?

Table MESSAGES (example of log messages from one user to the other):
| id | from_user_id | to_user_id | message_body |
| 1 | 7 | 10 | ... |
| 2 | 3 | 1 | ... |
| 3 | 95 | 14 | ... |
| 4 | 95 | 3 | ... |
| 5 | 1 | 3 | ... |
| 6 | 1 | 3 | ... |
| 7 | 10 | 7 | ... |
| 8 | 3 | 95 | ... |
...
I would like to COUNT (or execute any other aggregate function) how many users' conversation I have in this table.
A conversation is defined as a message from A to B OR from B to A. Notice that the order doesn't count as the ids are commutable one to each other.
So, the result I would like to get is:
Table CONVERSATIONS:
| id | user_id_1 | user_id_2 | messages_count |
| 1 | 7 | 10 | 2 |
| 2 | 3 | 1 | 3 |
| 3 | 95 | 14 | 1 |
| 4 | 95 | 3 | 2 |
...
How would I be able to do this using a SQL Query?
One option is to use least and greatest:
select least(from_user_id, to_user_id) user_id_1,
greatest(from_user_id, to_user_id) user_id_2,
count(*)
from messages
group by 1, 2
SQL Fiddle Demo
SELECT user1,
user2,
COUNT(*) as messageCount
FROM
(
SELECT from_user_id as user1,
to_user_id as user2
FROM T
WHERE from_user_id<= to_user_id
UNION ALL
SELECT to_user_id as user1,
from_user_id as user2
FROM T
WHERE from_user_id> to_user_id
) as T1
GROUP BY user1,user2
SQLfiddle demo

Mysql : how i can get 3 columns from multiple tables?

I have 2 tables:
Forums:
+-----------+----------------+-----------------+-------------+---------------+
| forum_id |user_profile_id |profile_forum_id |profile_role |profile_status |
+-----------+----------------+-----------------+-------------+---------------+
| 9 | 7 | 2 | moderator | active |
| 10 | 1 | 2 | admin | active |
| 40 | 1 | 1 | admin | active |
+-----------+----------------+-----------------+-------------+---------------+
DDX:
+----+-----------+-------------+---------------+-----------------+
| id |profile_id |profile_name | DDX_content |profile_forum_id |
+----+-----------+-------------+---------------+-----------------+
| 2 | 1 | user1 | 874420 | 1 |
| 3 | 1 | user2 | 124500 | 2 |
+----+-----------+-------------+---------------+-----------------+
Now i want to get list of all users(with 3 columns: profile_name, DDX_content, profile_role) that have 'admin' or 'moderator' role for specific forum id. How i can do that?
Try this request :
SELECT
d.profile_name,
d.DDD_content,
f.profile_role
FROM
DDX AS d
INNER JOIN
Forums AS f
ON f.profile_forum_id = d.profile_forum_id
WHERE
(profile_role = 'admin' OR profile_role = 'moderator')
AND forum_id = 9;
select ddx.profile_name, ddx.DDX_content, f.profile_role
from ddx
join forums f on f.user_profile_id = ddx.profile_id
where profile_role in ('admin','moderator')
and forum_id = 123

Advanced MySQL: Find correlations between poll responses

I've got four MySQL tables:
users (id, name)
polls (id, text)
options (id, poll_id, text)
responses (id, poll_id, option_id, user_id)
Given a particular poll and a particular option, I'd like to generate a table that shows which options from other polls are most strongly correlated.
Suppose this is our data set:
TABLE users:
+------+-------+
| id | name |
+------+-------+
| 1 | Abe |
| 2 | Bob |
| 3 | Che |
| 4 | Den |
+------+-------+
TABLE polls:
+------+-----------------------+
| id | text |
+------+-----------------------+
| 1 | Do you like apples? |
| 2 | What is your gender? |
| 3 | What is your height? |
| 4 | Do you like polls? |
+------+-----------------------+
TABLE options:
+------+----------+---------+
| id | poll_id | text |
+------+----------+---------+
| 1 | 1 | Yes |
| 2 | 1 | No |
| 3 | 2 | Male |
| 4 | 2 | Female |
| 5 | 3 | Short |
| 6 | 3 | Tall |
| 7 | 4 | Yes |
| 8 | 4 | No |
+------+----------+---------+
TABLE responses:
+------+----------+------------+----------+
| id | poll_id | option_id | user_id |
+------+----------+------------+----------+
| 1 | 1 | 1 | 1 |
| 2 | 1 | 2 | 2 |
| 3 | 1 | 2 | 3 |
| 4 | 1 | 2 | 4 |
| 5 | 2 | 3 | 1 |
| 6 | 2 | 3 | 2 |
| 7 | 2 | 3 | 3 |
| 8 | 2 | 4 | 4 |
| 9 | 3 | 5 | 1 |
| 10 | 3 | 6 | 2 |
| 10 | 3 | 5 | 3 |
| 10 | 3 | 6 | 4 |
| 10 | 4 | 7 | 1 |
| 10 | 4 | 7 | 2 |
| 10 | 4 | 7 | 3 |
| 10 | 4 | 7 | 4 |
+------+----------+------------+----------+
Given the poll ID 1 and the option ID 2, the generated table should be something like this:
+----------+------------+-----------------------+
| poll_id | option_id | percent_correlated |
+----------+------------+-----------------------+
| 4 | 7 | 100 |
| 2 | 3 | 66.66 |
| 3 | 6 | 66.66 |
| 2 | 4 | 33.33 |
| 3 | 5 | 33.33 |
| 4 | 8 | 0 |
+----------+------------+-----------------------+
So basically, we're identifying all of the users who responded to poll ID 1 and selected option ID 2, and we're looking through all the other polls to see what percentage of them also selected each other option.
Don't have an instance handy to test, can you see if this gets proper results:
select
poll_id,
option_id,
((psum - (sum1 * sum2 / n)) / sqrt((sum1sq - pow(sum1, 2.0) / n) * (sum2sq - pow(sum2, 2.0) / n))) AS r,
n
from
(
select
poll_id,
option_id,
SUM(score) AS sum1,
SUM(score_rev) AS sum2,
SUM(score * score) AS sum1sq,
SUM(score_rev * score_rev) AS sum2sq,
SUM(score * score_rev) AS psum,
COUNT(*) AS n
from
(
select
responses.poll_id,
responses.option_id,
CASE
WHEN user_resp.user_id IS NULL THEN SELECT 0
ELSE SELECT 1
END CASE as score,
CASE
WHEN user_resp.user_id IS NULL THEN SELECT 1
ELSE SELECT 0
END CASE as score_rev,
from responses left outer join
(
select
user_id
from
responses
where
poll_id = 1 and
option_id = 2
)user_resp
ON (user_resp.user_id = responses.user_id)
) temp1
group by
poll_id,
option_id
)components
After a few hours of trial and error, I managed to put together a query that works correctly:
SELECT poll_id AS p_id,
option_id AS o_id,
COUNT(*) AS optCount,
(SELECT COUNT(*) FROM response WHERE option_id = o_id AND user_id IN
(SELECT user_id FROM response WHERE poll_id = '1' AND option_id = '2')) /
(SELECT COUNT(*) FROM response WHERE poll_id = p_id AND user_id IN
(SELECT user_id FROM response WHERE poll_id = '1' AND option_id = '2'))
AS percentage
FROM response
INNER JOIN
(SELECT user_id FROM response WHERE poll_id = '1' AND option_id = '2') AS user_ids
ON response.user_id = user_ids.user_id
WHERE poll_id != '1'
GROUP BY option_id DESC
ORDER BY percentage DESC, optCount DESC
Based on a tests with a small data set, this query looks to be reasonably fast, but I'd like to modify it so the "IN" subquery is not repeated three times. Any suggestions?
This seems to give the right results for me:
select poll_stats.poll_id,
option_stats.option_id,
(100 * option_responses / poll_responses) as percent_correlated
from (select response.poll_id,
count(*) as poll_responses
from response selecting_response
join response on response.user_id = selecting_response.user_id
where selecting_response.poll_id = 1 and selecting_response.option_id = 2
group by response.poll_id) poll_stats
join (select options.poll_id,
options.id as option_id,
count(response.id) as option_responses
from options
left join response on response.poll_id = options.poll_id
and response.option_id = options.id
and exists (
select 1 from response selecting_response
where selecting_response.user_id = response.user_id
and selecting_response.poll_id = 1
and selecting_response.option_id = 2)
group by options.poll_id, options.id
) as option_stats
on option_stats.poll_id = poll_stats.poll_id
where poll_stats.poll_id <> 1
order by 3 desc, option_responses desc