views:

40

answers:

1

I suppose this is quite a common SP present in socialnetworks and community type websites. I have this SP that returns all of a user's friends on their 'friends' page order by those currently online, then alphabetically. It's taking quite a while to load and I am looking to speed it up.

I remember reading somewhere on SO that breaking up multiple joins into smaller result sets might speed it up. I haven't tried this yet but I am curious to see what other recommendations SO might have on this procedure.

DECLARE @userID INT -- This variable is parsed in
DECLARE @lastActivityMinutes INT

SET @lastActivitytMinutes = '15'

SELECT 
    Active = CASE WHEN DATEDIFF("n", b.LastActivityDate ,GETDATE()) < @lastActivityMinutes THEN 1 ELSE 0 END, 
    a.DisplayName, a.ImageFile, a.UserId, b.LastActivityDate
FROM 
    Profile AS a 
     INNER JOIN aspnet_Users as b on b.userId = a.UserId  
     LEFT JOIN Friend AS x ON x.UserID = a.UserID
     LEFT JOIN Friend AS z ON z.FriendID = a.UserID
WHERE   ((x.FriendId = @userID AND x.status = 1) -- Status = 1 means friendship accepted
     OR (z.UserID = @userID AND z.Status = 1))
GROUP BY a.userID, a.DisplayName, a.ImageFile, a.UserId, b.LastActivityDate
ORDER BY Active DESC, DisplayName ASC

I am not sure how to clip in my execution plan but the main bottle neck seems to be occurring on a MERGE JOIN (Right Outer Join) that's costing me 29%. At various stages, Parallelism is also costing 9%, 6%, 5% and 9% for a total of 29% as well.

My initial thoughts are to first return the JOINED results from the Profile and aspnet tables with a CTE and then do LEFT JOINS to the Friends table.

+3  A: 

You are joining Friend twice, using a LEFT JOIN, then you are removing the NULL's returned by the LEFT JOIN by WHERE condition, then using GROUP BY to get rid on distincts.

This is not the best query possible.

Why don't you just use this:

SELECT  Active = CASE WHEN DATEDIFF("n", b.LastActivityDate ,GETDATE()) < @lastActivityMinutes THEN 1 ELSE 0 END,
        a.DisplayName, a.ImageFile, a.UserId, b.LastActivityDate
FROM    (
        SELECT  FriendID
        FROM    Friends
        WHERE   UserID = @UserId
                AND status = 1
        UNION
        SELECT  UserID
        FROM    Friends
        WHERE   FriendID = @UserId
                AND status = 1
        ) x
INNER JOIN
        Profile AS a 
ON      a.UserID = x.FriendID
INNER JOIN
        aspnet_Users as b
ON      b.userId = a.UserId
ORDER BY
        Active DESC, DisplayName ASC
Quassnoi
Yes this brings us to my first problems. Sometimes a user makes or receives only 1 friend request. This means the ID could exist in either the UserID or FriendID column of the Friend table. Doing INNER JOIN's like the one you have stated will not return her friendship. Using your script, my return selection went from 395 (mine) to 362 rows.
Nai
@Nai: is it for the new query with `UNION ALL`?
Quassnoi
This is for the older one. With this one, it's not returning any rows. I am currently trying to determine why
Nai
`@Nai`: sorry, my fault. Just remove the outer `WHERE`. See the update.
Quassnoi
Yes I just removed that as well. I think you might have nailed it here as most of the work is now on the Index Scans (which is my bad and can be optimised with proper indexes).
Nai