Okay I'm having a problem. I have a social networking site and have some code that will call statuses from only friends. I want it to call both statuses from friends as well as the user logged in. Kind of the way facebook does.
Tables: members, friends, status
members - Handle
friends - Username, Friend
status - Handle, Type(public or friends)
Here is code that will show the most recent status from a user's friends.
$shawing = "SELECT *
FROM status
JOIN friends
ON status.Handle = friends.Friend where friends.Username='$members[Handle]' and status.Type='friends' ORDER by status.Id DESC" or print mysql_error();
$members[Handle] calls from an include that identifies the user who is logged in.
Someone told me a Union would work, but I've only been able to make one status show up and the code I have now only has one result.
Basically, I want to take the list of usernames from the person's friends list and add the user logged in's username and show only statuses from the friends section with those usernames.
Also, it would be great if I could get a structure for the code because I'm new to the whole JOIN and UNION. I understand it, sure, I just don't understand how it's properly setup.
The more UNION and JOIN's you use the more overhead you start to accumulate. Once you try to scale this by 10,000 records you'll start seeing this query in your servers slow query log.
I suggest keeping it simple and run two queries.
1st query: Get the users status
2nd query: Get the users friends status'
The first query should be very fast as you're most likely using the users userid (primary key on the table, yes?). Some times the best way is to just keep things simple.
Related
Let's assume a user is following thousands of other people,
These people send news regularly, and in his/her page, our user wants to see the recent news (paginated) from these people only.
What is the most efficient way to do this?
This is what I'm doing currently:
Create a table called following in database, each follow is added here, id, user_id, following_user_id
Get a list of user's following_user_ids
fetch all news WHERE user_id (news poster id) is IN(...following_user_ids...)
For example if our user's id is 1:
SELECT `following_user_id` FROM `following` WHERE `user_id` = 1; /* This is used in the IN() below */
SELECT * FROM `news` WHERE `user_id` IN (4,11,7,...following_user_ids....) ORDER BY `id` DESC limit 50 offset 0
/* Of course the `user_id` is indexed in the `news` table */
But if the user is following thousands of people and the news table is huge, I'm assuming the IN (... thousands of IDs ...) will be very slow?
So, is there a more efficient way to do this?
EDIT:
In case any one also has this issue, just stick with the IN method, it is a lot faster than JOIN in my case.
select
news.*
from
news
join following on news.user_id=following.following_user_id
where
following.user_id=1
Pagination
OFFSET has a problem. As he pages forward/backward and others are inserting new rows, he will miss stories or see the same story twice on consecutive pages.
The solution is to "remember where you left off". More: http://mysql.rjweb.org/doc.php/pagination
JOIN
The JOIN approach is cleaner, but not necessarily faster. In either case, the end result is a large list of stories, of which he is only interested in a page's worth. Shoveling the rest around is costly.
The fix for this is to find only the ids of the stories while finding the page's worth. Then look up (via another JOIN) the rest of the data for each story.
Prebuilt list
Still, if there are thousands of followed people (or millions of followers, in the case of Trump), it gets quite costly. There is a technique for making the SELECT faster at the cost of INSERTs needing to run around and store information.
Have a new 3-column table: (1) follower_id, (2) timestamp, (3) story_id. Whenever a story is posted, one row per follower is added to this table. When a follower wants the latest stories, it is sitting right in this table (or at least the ids are).
More: http://mysql.rjweb.org/doc.php/lists
You can limit your search by using the 'LIMIT' function, that will need to be updated everytime the user want more information:
LIMIT [offset,] row_count;
Putting it in your example would be something like this, saving this select in a temporary table variable:
SELECT * FROM `following_user_ids` ORDER BY `id` DESC limit rowcount offset offset_variable;
If you want to put in the example of a social media, you can update the limit everytime the user asks for more posts, so that the user will be able to see the posts of several of the he follows.
I'm new to MySQL, and I'm trying to do the following and hoping I can do it all with a single PHP query to MySQL call rather than having to call, store in php, call, store in php etc.
Here's my database setup. 1. table of users, including email address. 2. table of 'invitees' by email address and user who invited the invitee 3. table of 'friends'
I want to join the table of users with the table of invitees to determine which invitees have registered as users and who invited them (keeping in mind they might have been invited by more than 1 person). From there I'll insert the newly registered invitee along with the person who invited him into the friends table. Finally I will delete that invitee from the invited table so that he won't keep receiving invitations.
I see how to do this with 3 separate php calls to mysql: 1. a JOIN to find the users who are in the user table and the invitee table (the join will also identify who invited them) 2. an INSERT to put the pair into the friends table 3. a DELETE to remove the user from the invitee list.
But this will be higher traffic/less optimized with multiple calls to the MySQL server and also a lot of data stored by my php script that I really just need for the next MySQL call. I have a feeling this should be possible in just 1 call with no data returned to the php script.
I see there is an INSERT...SELECT MySQL call (http://dev.mysql.com/doc/refman/5.0/en/insert-select.html), but I have not been able to find a way to combine steps 1 and 2 above, which would be a SELECT...INSERT option. Does this exist? I tried the following, but it didn't work...something like this would be my ideal.
SELECT users.user as user1, invited2.user as user2, invited2.email as email2 FROM users RIGHT JOIN invited2 ON users.email=invited2.invitee;
INSERT INTO friends (user, friend) VALUES (user1, user2), (user2, user1);
DELETE FROM invited2 WHERE invited2.email = email2;
The part before INSERT INTO works on its own, but like I said, if I just return that to my php script, I'll just have to store all the results and then call the second myself. Is there a way to do this in one call? And if so, is there a way to also tack on a delete statement (DELETE from invited2 WHERE invitee=users.email) afterwards?
Any advice would be appreciated. Thanks in advance.
MySQL does provide a INSERT...SELECT (http://dev.mysql.com/doc/refman/5.1/en/insert-select.html)
Your SQL can be edited to:
INSERT INTO friends (user, friend)
SELECT users.user as user1, invited2.user as user2
FROM users
RIGHT JOIN invited2 ON users.email=invited2.invitee;
DELETE invited2
FROM users
RIGHT JOIN invited2 ON users.email=invited2.invitee;
In this case, INNER JOIN seems to make more sense than RIGHT JOIN.
I have no idea if that is possible, but i have search for users and i want to bring results on after every typed letter, so I'm trying to make that search work as fast as possible, but my ambitious a too high for my knowledge.
I have users db, relations (id, user_id, follower_id, status) which one i including using LEFT JOIN twice, first as followers and than as following.
So what i would like to do, i to bring result in order, like first i want to get people which user is following and are followed back, than just people which user is following, than people who's following user and finally others. And i want to limit all search to max 10 rows. If that is possible to do somehow only in mysql? i already done that in php, but that involves way to many queries to database.
Also im interesting if there is any way to set know if from what contidion that row is.
UPDATE
what i need to get and in what order:
users under following conditions:
by search query & user following and they following back.
by search query & user following.
by search query & people who's following user.
by search query
Order:
user following and they following back.
user following.
people who's following user.
others.
Limit: 0, 10
I believe there is no way i can simply order result in that particular way, so i think about using subquery or something more complex, but my mysql knowledge is too low, and im not sure if that is possible to do in one query, or i just have to use separate query under each condition.
I didn't test it, but I would try something like that:
select distinct u.* from users u, relations r where u.id = r.user_id or u.id = r.follower_id order by u.id = r.user_id
On a social network I am working on in PHP/MySQL, I have a friends page, it will show all friends a user has, like most networks do. I have a friend table in MySQL, it only has a few fields. auto_ID, from_user_ID, to_friend_ID, date
I would like to make the friends page have a few different options for sorting the results,
By auto_ID which is basically in the order a friend was added. It is just an auto increment id
new friends by date, will use the date field
By friends name, will have a list in alphabetical order.
The alphabetical is where I need some advice. I will have a list of the alphabet A-Z, when a user clicks on K it will show all the user's name starting with K and so on. The trick is it needs to be fast so doing a JOIN on the user's table is not an option, even though most will argue it is fast, it is not the performance I want for this action. One idea I had is to add an extra field to my friendship table and store the first letter of the users name in it. User's can change there name at anytime so I would have to make sure this is updated on possible thousands of records, anytime a user changes there name.
Is there a better way to do this?
Well if you don't want to do a join, then storing the user's name or initials on the friendships table is really your only other viable option. You mention the problem of having to update thousands of records every time a name changes, but is this really a problem? Unless you're talking about a major social networking site like Facebook, or maybe MySpace, does the average user really have enough friends to make this problematic? And then you have to multiply that by the probability that a user will change their name, which I would imagine isn't something that happens very often for each user.
If those updates are in fact non-trivial, you could always background or delay that to happen during non-peak times. Sure you would sacrifice up-to-the-second accuracy, but really, would most users even notice? Probably not.
Edit: Note, my answer above really only applies if you already have those levels of users. If you are still basically developing your site, just worry about getting it working, and worry about scaling problems when they become real problems.
You could also look at a caching solution like memcached. You can have a background process that is always updating a memcached hash and then when you want this data it is already in memory.
I'd just join on the table that contains the name and then sort on the name. Assuming a pretty normal table layout:
Table Person:
ID,
FirstName,
LastName
Table Friend:
auto_ID,
from_user_ID,
to_friend_ID,
date
You could do things like:
Select person.id, person.firstname, person.lastname, friend.auto_id
from Friend
left join on person where person.id = friend.to_friend_ID
where friend.from_user_ID = 1
order by person.lastname, person.firstname
or
Select person.id, person.firstname, person.lastname, friend.auto_id
from Friend
left join on person where person.id = friend.to_friend_ID
where friend.from_user_ID = 1
order by friend.date desc
I'd really recommend adding a column in the friend table to keep the first letter around, no need to duplicate data like that (and have to worry about keeping it in sync), that's what joins are for.
This may be a hairy question but. Say I have
Followers:
-user_id
-follower_id
Activities:
-id
-user_id
-activity_type
-node_id
Pulling a users activity is fairly easy. But what is the best way to get a followers activity? A subselect? It seems like it is incredibly slow as users get more and more followers. Any ideas to speed this up?
Also, on a more conceptual level. How does the grouping work. Is it all done with a single query? Or is all the activity data pulled in and then sorted and grouped on the PHP side?
Users X, Y and Z did Activity A
User J did 3 of Activity B
Subselects are often slower than JOINs, but it really depends on what exactly you're doing with them. To answer you main question, I would get follower data with a JOIN:
SELECT * FROM followers f
LEFT JOIN activities a ON f.follower_id=a.user_id
WHERE f.user_id=$followedPerson
That's assuming that the followers table represents a user with user_id, and someone who is following them with a follower_id that happens to be a user_id in the users table as well.
It won't ever be incredibly slow as long as you have an index on followers.user_id. However, the amount of data such a query could return could become larger than you really want to deal with. You need to determine what kinds of activity your application is going to want to show, and try to filter it accordingly so that you aren't making huge queries all the time but only using a tiny fraction of the returned results.
Pulling data out and grouping it PHP side is fine, but if you can avoid selecting it in the first place, you're better off. In this case, I would probably add an ORDER BY f.follower_id,activity_date DESC, assuming a date exists, and try to come up with some more filtering criteria for the activity table. Then I'd iterate through the rows in PHP, outputting data grouped by follower.
An activity log has the potential for a very large number of records since it usually has a mix of the current user's activity and all their friends. If you are joining various tables and a user has 100s of friends that's potentially a lot of data being pulled out.
One approach is to denormalise the data and treat it as one big log where all entries that should appear on a user's activity log page to be stored in the activity log table against that user. For example if User A has two friends, User B and User C, when User A does something three activity log records are created:
record 1: "I did this" log for user A
record 2: "My friend did this" log for user B
record 3: "My friend did this" log for user C
You'll get duplicates, but it doesn't really matter. It's fast to select since it's from one table and indexed on just the user ID. And it's likely you'll housekeep an activity log table (i.e. delete entries over 1 month old).
The activity log table could be something like:
-id
-user_id (user who's activity log this is)
-action_user_id (user who took the action, or null if same as user_id)
-activity_type
-date
To select all recent activity logs for a single user is then easy:
SELECT * from activity_log WHERE user_id = ? ORDER by date DESC LIMIT 0,50
To make this approach really efficient you need to have enough information in the single activity log table to not need any further selects. For example you may store the raw log message, rather than build it on the fly.
I dont know if I understood correctly what you need but
I would try this select, if I'm right you should get all activity for all followers of #USERID#
SELECT a.* FROM Activities AS a
INNER JOIN Followers AS f1
ON a.user_id = f1.follower_id
WHERE f1.user_id = #USERID#