Stop getting same result twice in mySQL - 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.

Related

Excluding results from a MySQL query and trying to avoid an O(n^2) query?

Say I have a MySQL table photo with two columns: "url" and "owner_id." I have another table unfollowed_user with two columnts: "unfollower" and "recipient."
I'm trying to create an O(n) MySQL query that selects all of the photos from the photo table for a particular site user but excludes any photos with by owners that the user has "unfollowed."
Here's what I've tried so far:
SELECT p.url FROM photo p LEFT JOIN unfollowed_user u ON
p.owner_id = u.recipient WHERE recipient IS NULL;
This does indeed return any photos and exclude any by an owner that has been unfollowed, but it excludes all photos by an owner that any user has "unfollowed," not just when a specific user has unfollowed.
So how about:
SELECT p.url FROM photo p LEFT JOIN unfollowed_user u ON
p.owner_id = u.recipient WHERE recipient (IS NULL OR unfollower != ?);
(supplying a particular user for the ?)
This returns duplicate results, because it creates a new row if other users (not our particular user) has unfollowed the photo owner. If users 1 and 2 have unfollowed user 10, user 3 will get any of user 10's photo twice because there are two rows in which the unfollower != 3.
So how about adding a SELECT DISTINCT:
SELECT DISTINCT p.url FROM photo p LEFT JOIN unfollowed_user u ON
p.owner_id = u.recipient WHERE recipient (IS NULL OR unfollower != ?);
This fixes the duplicate entry problem, but raises another problem. Say users 3 and 4 have both unfollowed user 10. Any photos by user 10 will be left out the first time around because user 3 has unfollowed user 10. However those photos will also be included because someone who is not user 3 has unfollowed user 10.
OK, OK. I know the obvious choice is
SELECT p.url FROM photo p WHERE owner_id NOT IN
(SELECT recipient FROM unfollowed_user u WHERE unfollower = ?);
But for large data sets, this has O(n^2), right? MySQL is subquerying every photo's owner?
Any help, pointers, helpful articles or blog posts, etc, would be appreciated.

Getting the number of posts that a user has posted?

How can I get the number of posts that a user has posted using one MySQL query?
I can't really think of anything but this, but there is no aggregate function on the join. So I'm not sure how to proceed. I am positive that joins will not accomplish what I need.
select a1.username as Username
from `logs` as a1
left join `logs` as a2
on a1.username = a2.username
For example, my logs table is filled with information about posts people have made. I want to find how many posts each user has made, i.e.
Username, Posts
User1 100
User2 200
etc
EDIT: Sorry for not providing enough information.
I have a table called logs and it has two columns. One column is called username and another column is called msg. It basically holds information about posts that people have posted.
For example, let's say someone named Red posts Hello world. It will be saved to the table logs and a new row will be created. username will be Red, and msg will be Hello world
I basically want to get the number of messages that EVERY SINGLE user has posted by their username. I.e. here is an example of what I want
Username Posts
Red 1
Blue 10
Sally 30
try this
SELECT Username, count(Posts)
FROM `logs`
GROUP BY Username;
Good luck.
I'm assuming that when you say you "can't use count(*) in a join", you mean that you tried and saw that it didn't work, rather than you can't use COUNT at all. So I'm using it here.
You're right that a JOIN is the wrong place for a COUNT. You want it up in the SELECT column list, and a GROUP BY down below. Aggregate by Username, and count the number of entries in each aggregate.
SELECT Username, COUNT(*) AS Count
FROM logs
GROUP BY Username
This query may help you, change this query as for your requirement
SELECT users.*, count( posts.user_id )
FROM posts LEFT JOIN users ON users.id=posts.user_id
GROUP BY posts.user_id
may be like
SELECT users.username, count( logs.username ) as posts
FROM users LEFT JOIN logs ON users.username=posts.username
GROUP BY users.username

Get Data From 2 Tables in One Query

I am trying to retrieve data for my notification system. I have three tables. One table (notifications) holds the actual information for the notification itself. I have two other tables to keep track of who gets certain notifications. There are two types of notifications, user and global. A global notification goes to all users while a user notification only goes to specific users. For this I have two tables, notifications_users and notifications_global. The table structures are below:
notifications (id, title, url, start, end)
notifications_users (notification_id, user_id, viewed)
notifications_global (notification_id, user_id, viewed)
What I want to do is to grab the notification title and url (from notifications table) along with the viewed value for all notifications that go to a specific user from both notifications_users and notifications_global tables. Would a UNION query be the best option here? I thought about just separating the queries but then I have two different arrays to loop through in my PHP script which I do not want. There has to be a way to grab all of this data with one query into one array. The following gives me an empty set:
SELECT notification.title, notification.url
FROM notifications
RIGHT JOIN notifications_users ON notifications.id = notifications_users.notification_id
RIGHT JOIN notifications_global ON notifications.id = notifications_global.notification_id
WHERE notifications_users.user_id = 11508 AND notifications_global.user_id = 11508;
SELECT a.title, a.url, b.viewed as 'User View', c.viewed as 'Global View'
FROM Notifications a
INNER JOIN Notifications_Users b
ON a.id = b.notification_id
INNER JOIN Notifications_Global c
ON b.notification_id = c.notification_id
WHERE b.user_id = 11508 and c.user_id = 11508
I think I might have been over-complicating this a bit. I just wrote the two queries for each table separate and then put UNION between them. If anyone is interested, here is what I ended up going with.
(SELECT notification_id, notification_title, notification_url, tbl_notifications_users_lookup.viewed
FROM tbl_notifications
INNER JOIN tbl_notifications_users_lookup ON tbl_notifications.id = tbl_notifications_users_lookup.notification_id
WHERE tbl_notifications_users_lookup.user_id = 11508)
UNION
(SELECT notification_id, notification_title, notification_url, tbl_notifications_global_lookup.viewed
FROM tbl_notifications
INNER JOIN tbl_notifications_global_lookup ON tbl_notifications.id = tbl_notifications_global_lookup.notification_id
WHERE tbl_notifications_global_lookup.user_id = 11508);

MySQL query to find a slightly complex relationship

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

MYSQL PHP Query: Joining on more than one field

My table scheme for a social network site is as follows:
users
id|username
1|bob
2|mary
3|ken
userinfo
id|userid|handle|description
1|1|bobbie|student
2|2|Mary1|programmer
network
id|inviterid|invitedid|status
1|1|2|2
2|1|3|2
When one person invites another to join his/her network, an entry is made into the network table with status 1. If the request is accepted, the status goes to 2. So far so good.
However, I'm having trouble listing out members of someone's network while joining with other tables since in effect I have to join on more than one field since the person can either be the inviter or the invited.
Here is query to get members of network
//$id = id of logged in user
$sql = "SELECT * FROM `network` n
LEFT JOIN `userinfo` ui
on n.invitedid= ui.userid OR n.inviterid = ui.userid
LEFT JOIN`users` u
on n.invitedid= u.id OR n.inviterid = n.id
WHERE status='2'
Not all users have userinfo records as not everyone has filled out a profile. But I want to get these as well.
Results of Query for Bob's network ie $id=1:
Bob student
Mary student
Bob programmer
Mary programmer
Note it includes bob even though he should be excluded, uses Bob's info for Mary and excludes Ken cause he has no userinfo record.
Above query gives multiple records for users with profiles, records for the logged in user and excludes any without profile. I want to exclude logged in user and then one record for each member of his/her network including members without profiles.
Update:
changing WHERE to following excludes user's own profiles which helps a bit but still excludes members without profiles:
WHERE status = '2' AND ui.userid <> '$id'
Thanks for any suggestions.
Try this:
$sql = "SELECT * FROM `network` n
LEFT JOIN `userinfo` ui
on n.invitedid= ui.userid OR n.inviterid = ui.userid
RIGHT JOIN`users` u
on n.invitedid= u.id OR n.inviterid = n.id
WHERE status='2'