tags:

views:

126

answers:

5

This is for the friends module on my site, where users can make friends with each other. These are stored in a "friends:" table, with person who initiated the friendship being the friendship_inviter and the person on the approve/deny end of things is the friendhsip_accepter

SELECT user_id, user_name, user_gender
FROM friends
LEFT JOIN users
    ON (   users.user_id = friends.friendship_inviter 
        OR users.user_id = friends.friendship_accepter)
WHERE (friendship_inviter = '125' OR friendship_accepter = '125') 
AND    user_id !='125' 
AND    friendship_level = 1;

This does a full table scan, and even though the tables are not large (15,000 users, 3000 friendships), it takes 1-1.5 seconds on average.

How can I output this list of current friends in a way that would be less taxing n the server?

+1  A: 

Add indexes to the columns/combinations used in the WHERE clause and the JOIN.

duffymo
I have a UNIQUE index on the inviter/accepter rows, so to ensure that you can become friends once with each other, and an individual index on each column appearing in the WHERE clause.
A: 

Make sure you have the user_id, friendship_accepter, friendship_inviter and friendship_level indexed.

Also is that time you are posting just the raw query time or does it include the time to output the results? If it also includes the output it may not be the query that is slow but the output.

Another thing that could be slowing down the results is if it is over a network that is slow (ie. the Internet). The slow time may be because of this and not because of the query.

MitMaro
The time is straight from the command line. It also appears in the slow query log, every time the page with that code is executed.
Right, table scan isn't a network problem.
duffymo
+5  A: 
SELECT user_id, user_name, user_gender
FROM friends
LEFT JOIN users
    ON users.user_id = friends.friendship_accepter
WHERE  friendship_accepter = '125' 
AND    user_id !='125' 
AND    friendship_level = 1
UNION 
SELECT user_id, user_name, user_gender
FROM friends
LEFT JOIN users
    ON users.user_id = friends.friendship_inviter
WHERE friendship_inviter = '125'  
AND    user_id !='125' 
AND    friendship_level = 1;

Of course, it looks like you aren't selecting anything from the "friends" table, AND you have extra columns in your WHERE clause, so I would actually write INNER JOINs instead, which are equivalent to the LEFT JOINs in the case you posted:

SELECT user_id, user_name, user_gender
FROM friends
INNER JOIN users
    ON users.user_id = friends.friendship_accepter
WHERE  friendship_accepter = '125' 
AND    user_id !='125' 
AND    friendship_level = 1
UNION 
SELECT user_id, user_name, user_gender
FROM friends
INNER JOIN users
    ON users.user_id = friends.friendship_inviter
WHERE friendship_inviter = '125'  
AND    user_id !='125' 
AND    friendship_level = 1;
Dave Markle
Your example returns no records.
I got it. Needed to switched accepter/inviter around between the 2 parts of the query.
Did it become faster?
Dave Markle
Yes, much faster. 0.01 seconds.
The little rule here with queries like this is that optimizers generally get angry with OR conditions, especially in the join predicate. They often consider such constructs to be "non-sargable" and therefore often (but not always) ignore your index.
Dave Markle
+1  A: 

Part of the problem might be that friendship_level is of low cardinality. Sometimes, on low-cardinality columns, the query optimizer will decide to do a table scan anyway.

Do an EXPLAIN, and make sure that your other conditions are executing FIRST, before the friendship_level condition is considered in the query. A scan of the records remaining after the first two conditions are satisfied would take far less time.

Robert Harvey
+1 for `EXPLAIN`
MitMaro
A: 

I do something very very similar on my social network site however mine is a little different, I insert 2 records for each friendship, this allows me to show a user;

pending friend request sent friend request and confirmed friends

I't also eliminates the use of UNION, something to consider. I currently have around 50,000 users and over 1 millions rows in the friend table and this works pretty good for me

jasondavis