MySQL query to find a slightly complex relationship - mysql

I am trying to devise a MySQL query to select a specific record based off 2 parameters. Lets say I have 3 tables. Users, Conversations, and UserConvo where UserConvo is a reference table that links the Many-to-Many relationship between a User and any Conversation that they are having. Think of it as a very basic web chat.
Now, if given an arbitrary number of UserIds (i.e. [1,2,3]), I want to find a Conversation that involves EXACTLY these Users. Therefore, for the case of UserIds 1, 2, and 3, I want to see if there exists a Conversation where only user 1, 2, and 3 are included.
Is there a purely MySQL way to do this? My first thoughts are to just query the Conversations in which UserId 1, 2 and 3 are present. Then, somehow check each record to see if all ConversationIds match, but I do not have a whole lot of MySQL experience and am not sure of its potential.
Thanks

Something like this should work. It uses count with case - the idea is to compare the overall count per conversation to those where the user id in (1,2,3):
select c.id
from conversations c
join userconversations uc on c.id = uc.conversationid
join users u on uc.userid = u.id
group by c.id
having count(u.id) = count(case when u.id in (1,2,3) then 1 end)
SQL Fiddle Demo

Related

How to properly use inner join in SQL when I have to join a table twice?

Note: The actual schema isn't male/female, but some other criteria. I'm just using male/female for this example to make it easier to understand.
I have a table "users", which contains a column user_name and user_gender. The gender can be "M" or "F".
The problem is that I have another table, "messages", that has a column for "sender" and "receiver". These columns contains user_name in each row.
How can I use INNER JOIN so that I can get messages where only males send to females?
I know easily how to specify it once, binding users.user_name to "sender" or "receiver" but not both.
To expand on my question, how do see which top 10 pairs where a male sent the most messages to a female? Note, this means unique A/B pairs, so I want to return cases where a guy sends a single female a ton of messages, not when a guy spams a lot of messages to different females.
Think of your messages table as a "cross table" connecting two rows in the users table. When you join to a table like that, give users two different aliases, and refer to them in your join conditions, like this:
select *
from messages msg
join users m on msg.sender = m.user_id AND m.user_gender='M'
join users f on msg.receiver = f.user_id AND f.user_gender='F'
With this skeleton in hand, you should be able to figure out the rest of your query:
Use GROUP BY to group by m.user_id, f.user_id, and count(*) to count
Order by COUNT(*) to get the highest sender+receiver pairs at the top
Use LIMIT to grab the top ten pairs.

Stop getting same result twice in mySQL

I'm doing a microblogging site (like Twitter) using a mySQL database. In that database I have three tables: one for users, one for messages and one to control who follows who. When the user enters the site I want to show his own messages and those from the people who he follows, so I use this query:
SELECT * FROM messages
INNER JOIN users ON users.id = messages.author
INNER JOIN followers ON followers.main_user = 2
WHERE messages.author = followers.followed_user OR messages.author = 2
ORDER BY date DESC;
Being '2' the user that's entering the site.
The thing is that I get the messages written by the person who enters (2, in this example) twice, and once from the people he follows. Does anyone have a clue about how to solve this? User 2 doesn't follow himself.

mysql: two tables but three select criteria

I have two tables: users and user_depts. Let's say (for this question) that users only has an 'id' column, and user_depts has 2: 'uid' and 'did'.
I am looking for an SQL line that will return all the user IDs for all the departments with which a given user ID (let's say 7, though this'll come dynamically from PHP) is associated.
What I've tried is:
SELECT id FROM users, user_depts
WHERE users.id = user_depts.uid
AND user_depts.uid = 7
But of course this does nothing but return 7. I think I might have to join the table to itself, but I only know the shortcut syntax for joining, and it doesn't seem to be sufficient. Any pointers would be greatly appreciated!
Use EXISTS:
SELECT uid FROM user_depts
WHERE EXISTS (
SELECT * FROM user_depts a
WHERE uid = 7 AND a.did = user_depts.did
)
I am looking for an SQL line that will return all the user IDs for all
the departments with which a given user ID (let's say 7, though
this'll come dynamically from PHP) is associated.
If this means you want to find all the users with the userid: 7 that has a user_departent connected to it this is your query:
select users.id from users
inner join user_depts.uid = users.id
where users.id = 7
select uid from user_depts where did
in (select did from user_depts where uid=7)
First select all the did with which a user is associated using subquery
Then select all the user_id for the selected departments.
It's the best way you can do it.
and if you want to remove repeated result then you can use distinct

Complex MySQL Query - Find duplicates PER user_id?

I have a database of Facebook Likes from several people. There are duplicate "like_id" fields across many "user_id"s. I want a query that will find the amount of "like_id"s person A has in common with person B.
This query is fantastic for comparing likes when only 2 "user_id"s are in the database, but as soon as I add a 3rd, it messes it up. Basically, I want to see who has the most "likes" in common with with person A.
SELECT *,
COUNT(*)
FROM likes
GROUP BY like_id
HAVING COUNT(*) > 1
Anyone have a query that might work?
This SQL should work. You just need to put in the User A's user_id and it should compare with all other users and show the top matching one. You can change it to show the top 5 or do whatever else you need to do.
Basically what it is doing is that it is doing a self join on the table, but making sure that when it does a join, it is a different user_id but the "like" is the same. Then it does a group by each of the other user_id's and sums the same amount of likes for that user_id.
SELECT all_other_likes.user_id, count(all_other_likes.like_id) AS num_similar_likes
FROM likes original_user_likes
JOIN likes all_other_likes
ON all_other_likes.user_id != original_user_likes.user_id
AND original_user_likes.like_id = all_other_likes.like_id
WHERE original_user_likes = USER_ID_YOU_WANT_TO_COMPARE
GROUP BY all_other_likes.user_id
ORDER BY count(all_other_likes.like_id) DESC
LIMIT 1;
Not sure what database you are using. You might need to do a SELECT TOP 1 if it is MS-SQL, but this is valid PostgreSQL and MySQL syntax.
I think this will do it:
SELECT
likes_a.user_id,
likes_b.user_id
FROM
likes as likes_a JOIN likes as likes_b
ON
likes_a.like_id = likes_b.like_id
WHERE
likes_a.user_id <> likes_b.user_id
And then post-process the results to count up who has the most in common.

Limit on Group by in Mysql

In my web application, I would like to show a list of my users. Users can have different statuses (FB-like). I want to display the last 3 statuses below each user's name.
Dinosaur
I'm a dino!
I eat meat!
I like cows!
Fish
Blub!
I don't like dinosaurs!
Going for a swim!
I have the following SQL query:
SELECT s.status, u.voornaam, u.achternaam FROM status AS s INNER JOIN sportjefit_user AS u ON s.user = u.id where u.begeleider='53' group by id desc limit 3
However, this returns only the top 3 of the results, in this case it would only show the Dinosaur's statuses. I want to show the top 3 statuses for every users though. Can I do this with a group by and put a limit on this or do I need multiple queries here? And, if I do need multiple queries, how would I go about implementing this? The number of users will keep increasing as my application grows.
Can someone help me out?
Thanks.
I'd use multiple queries like this:
select all users
for each user
select last 3 statuses
using
SELECT * FROM sportjefit_user
and
SELECT status FROM status WHERE userid = ? ORDER BY id DESC LIMIT 3
How many users do you anticipate? If you're trying to show all users on a page, I'd paginate them. Hope this helps.