I'm sorry, I missed a JOIN on the second variation. It is: SELECT u.id, u.name, u.imageURL, u.bio, CASE WHEN f.friend_id IS NOT NULL THEN 'isFriend' WHEN s.to_id IS NOT NULL THEN 'hasSentRequest' WHEN r.to_id IS NOT NULL THEN 'hasReceivedRequest' ELSE 'none' END AS 'friendStatus', (SELECT COUNT(1) AS d FROM friends f1 JOIN friends f2 ON f1.fiend_id = f2.friend_id WHERE f1.user_id = 33 AND f2.user_id = u.id) FROM users u *LEFT OUTER JOIN friends f ON f.user_id = 33 AND f.friend_id = u.id <http://u.id>* LEFT OUTER JOIN friend_requests s ON s.to_id = 33 AND s.from_id = u.id LEFT OUTER JOIN friend_requests r ON r.to_id = u.id AND r.from_id = 33 WHERE u.id != 33 AND u.name LIKE '%John%' ORDER BY u.name;
On Tue, Dec 9, 2014 at 10:15 AM, Robert DiFalco <robert.difa...@gmail.com> wrote: > I have users, friends, and friend_requests. I need a query that > essentially returns a summary containing: > > * user (name, imageURL, bio, ...) > * Friend status (relative to an active user) > * Is the user a friend of the active user? > * Has the user sent a friend request to the active user? > * Has the user received a friend request from the active user? > * # of mutualFriends > * Exclude the active user from the result set. > > So I have mocked this up two ways but both have complicated query plans > that will be problematic with large data sets. I'm thinking that my lack of > deep SQL knowledge is making me miss the obvious choice. > > Here's my two query examples: > > SELECT u.id, u.name, u.imageURL, u.bio, > CASE > WHEN EXISTS(SELECT 1 FROM friends f WHERE f.user_id = 33 AND > f.friend_id = u.id) THEN 'isFriend' > WHEN EXISTS(SELECT 1 FROM friend_requests s WHERE s.to_id = 33 AND > s.from_id = u.id) THEN 'hasSentRequest' > WHEN EXISTS(SELECT 1 FROM friend_requests r WHERE r.to_id = u.id > AND r.from_id = 33) THEN 'hasReceivedRequest' > ELSE 'none' > END AS "friendStatus", > (SELECT COUNT(1) > FROM friends f1 > JOIN friends f2 ON f1.friend_id = f2.friend_id > WHERE f1.user_id = 33 AND f2.user_id = u.id) AS mutualFriends > FROM users u > WHERE u.id != 33 AND u.name LIKE 'John%' ORDER BY u.name; > > SELECT u.id, u.name, u.imageURL, u.bio, > CASE > WHEN f.friend_id IS NOT NULL THEN 'isFriend' > WHEN s.to_id IS NOT NULL THEN 'hasSentRequest' > WHEN r.to_id IS NOT NULL THEN 'hasReceivedRequest' > ELSE 'none' > END AS 'friendStatus', > (SELECT COUNT(1) AS d > FROM friends f1 > JOIN friends f2 ON f1.fiend_id = f2.friend_id > WHERE f1.user_id = 33 AND f2.user_id = u.id) > FROM users u > LEFT OUTER JOIN friend_requests s ON s.to_id = 33 AND s.from_id = u.id > LEFT OUTER JOIN friend_requests r ON r.to_id = u.id AND r.from_id = 33 > WHERE u.id != 33 AND u.name LIKE 'John%' ORDER BY u.name; > > 33 is just the id of the active user I am using for testing. The WHERE > clause could be anything. I'm just using "u.name" here but I'm more > concerned about the construction of the result set than the WHERE clause. > These have more or less similar query plans, nothing that would change > things factorially. Is this the best I can do or am I missing the obvious? > > Here are the tables: > > > CREATE TABLE users ( > id BIGINT, > name VARCHAR, > imageURL VARCHAR > created TIMESTAMP DEFAULT CURRENT_TIMESTAMP, > phone_natl BIGINT, /* National Phone Number */ > country_e164 SMALLINT, /* E164 country code */ > email VARCHAR(255), > PRIMARY KEY (id), > UNIQUE (email), > UNIQUE (phone_natl, country_e164) > ); > > > CREATE TABLE friends ( > user_id BIGINT, > friend_id BIGINT, > PRIMARY KEY (user_id, user_id), > FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE, > FOREIGN KEY (friend_id) REFERENCES users(id) ON DELETE CASCADE > ); > CREATE INDEX idx_friends_friend ON friends(friend_id); > > CREATE TABLE friend_requests ( > from_id BIGINT, > to_id BIGINT, > created TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP, > PRIMARY KEY (from_id, user_id), > FOREIGN KEY (from_id) REFERENCES users(id) ON DELETE CASCADE, > FOREIGN KEY (to_id) REFERENCES users(id) ON DELETE CASCADE > ); > CREATE INDEX idx_friend_requests_to ON friend_requests(to_id); > > Let me know if you guys need anything else. > >