List all friends for a userid SQL query - sql

I have 2 tables a user table (user_id, fname, lname, dob, etc) and a are_friends table
(userA_id, userB_id). I have been trying to do this query for a while now, I need it to list all friends for a user_id.
What I have got so far,
SELECT
U.user_id,
U.fname,
U.lname
FROM are_friends A, user U
WHERE
A.user_id = U.user_id
AND (
A.user_id = 1
OR A.user_id IN (SELECT userB_id FROM are_friends WHERE userA_id = 1)
);
Any help will be much appreciated.

Try using an INNER JOIN like this:
SELECT u2.user_id, u2.fname, u2.lname
FROM user u
INNER JOIN are_friends f ON f.userA_id = u.user_id
INNER JOIN user u2 ON u2.user_id = f.userB_id
WHERE u.user_id = 1
You can change the WHERE clause to specifically get the friends of another user id.

Related

Join Equivalent For User Session Subquery

I have a user table with id column and request table with user_id and session_id columns.
I want to get the number of sessions each user has.
Here is the query I wrote using a subquery:
select user.id,
(
select count(distinct session_id)
from request
where request.user_id = user.id
) as session_count
from user
What would be the join equivalent of this?
What about this?
select u.id, count(distinct r.session_id)
from user u
join request r on r.user_id = u.id
group by u.id
You can use GROUP BY without need a subquery such as
SELECT u.id, COUNT(DISTINCT r.session_id)
FROM user u
JOIN request r
ON u.id = r.user_id
GROUP BY u.id

Distinct Count with data from another table

I have 4 tables
All ID related things are ints and the rest are texts.
I want to count the number of albums the user is tagged at so if a user is tagged in album1 once album2 once and album3 once it will show 3 and if more in any of them it will still show 3.
I tried to do:
SELECT COUNT(DISTINCT ALBUM_ID) FROM PICTURES WHERE ID=(SELECT PICTURE_ID FROM TAGS WHERE USER_ID=userId);
But this returned 1 although it was supposed to return 3 and the same happened without DISTINCT.
How can I get the amount?
EDIT:
I want to check only one user(I have the user's ID and name)
You must join users with LEFT joins to tags and pictures and aggregate:
SELECT u.id, u.name, COUNT(DISTINCT p.album_id) counter
FROM users u
LEFT JOIN tags t ON t.user_id = u.id
LEFT JOIN pictures p ON p.id = t.picture_id
GROUP BY u.id, u.name
If you want the result for a specific user only:
SELECT u.id, u.name, COUNT(DISTINCT p.album_id) counter
FROM users u
LEFT JOIN tags t ON t.user_id = u.id
LEFT JOIN pictures p ON p.id = t.picture_id
WHERE u.id = ?
GROUP BY u.id, u.name -- you may omit this line, because SQLite allows it
Or with a correlated subquery:
SELECT u.id, u.name,
(
SELECT COUNT(DISTINCT p.album_id)
FROM tags t INNER JOIN pictures p
ON p.id = t.picture_id
WHERE t.user_id = u.id
) counter
FROM users u
WHERE u.id = ?
Replace ? with the id of the user that you want.

Find records combining two joins

I have three tables:
User, House and HouseEvent
A House has a foreign_key (user_id) to User and an HouseEvent has a foreign_key (house_id) to House
You can see the schema here: http://sqlfiddle.com/#!9/f08db/5 -
I know how I can get all the users which do not have a house:
SELECT * FROM User
LEFT OUTER JOIN House u ON u.user_id = user.id
WHERE u.user_id IS NULL
But how could I get in a single query, all the users who do not have a house and those users who have a house with at (least) a suspended event.
So, in the example, I would get Lee because he does not have a house, and I would also get John, because even though he has houses, one of its houses has an associated suspended event.
UNION the 2 queries together?
SELECT u.id, u.name FROM User u
JOIN House h ON h.user_id = u.id
JOIN HouseEvent he ON he.house_id = h.id AND he.name = 'suspended'
UNION
SELECT u.id, u.name FROM user u
LEFT JOIN house h ON h.user_id = u.id
WHERE h.user_id IS NULL
Fiddle
You can do that without using SUB QUERY, just use LEFT JOIN here is the query:
SELECT User.name FROM User
LEFT JOIN House h on h.user_id = user.id
LEFT JOIN HouseEvent e on h.user_id = e.house_id
WHERE h.user_id IS NULL OR e.name = 'suspended'
GROUP BY User.name
I modified your SQL Fiddle
SELECT * FROM User
Where id not in (SELECT user_id FROM House) OR
id in (select user_id from House h, HouseEvent he where he.house_id = h.id AND he.id = 2)
http://sqlfiddle.com/#!9/f08db/41

SQL combining 3 tables and get just the row with the latest date

I have 3 tables user, session and log. The user table stores all user relevant information while the session just connects the user with the log. And i want to get a list of all users with the latest log entry. The table design looks like this:
user (id, name, ...)
session (id, user_id)
log (id, session_id, time, type, ...)
My current query looks like this
SELECT *
FROM USER AS u
INNER JOIN session AS s
ON u.id = s.user_id
INNER JOIN log AS l
ON l.session_id = s.id
ORDER BY l.time DESC
But it's not hard to imagine that this just returns the data of all 3 tables sorted by date. How do i achieve a result that i just get every user just once with the data from the latest log entry ordered by the time of log (desc)?
Thanks in advance for your help.
You can use DISTINCT ON in conjunction with ORDER BY to get the latest row per user by log date. This will allow you to select the additional fields you need:
SELECT DISTINCT ON (u.id)
u.id,
u.Name,
l.type,
l.time
FROM user AS u
INNER JOIN session AS s ON u.id = s.user_id
INNER JOIN log AS l ON l.session_id = s.id
ORDER BY u.id, l.time DESC;
N.B. I don't know exactly what columns you need, but I have added a couple in to demonstrate as I don't like to advocate the use of SELECT *
For completeness there are a couple of other ways to achieve this, the first is to select the max in a subquery and join back to the outer query on both user_id and time:
SELECT u.id,
u.Name,
l.type,
l.time
FROM user AS u
INNER JOIN session AS s
ON u.id = s.user_id
INNER JOIN log AS l
ON l.session_id = s.id
INNER JOIN
( SELECT s.user_id, MAX(l.time) AS time
FROM session AS s
INNER JOIN log AS l
ON l.session_id = s.id
GROUP BY s.user_id
) AS MaxLog
ON MaxLog.user_id = u.id
AND MaxLog.time = l.time
ORDER BY l.time DESC;
Or you can use ROW_NUMBER():
SELECT id, Name, type, time
FROM ( SELECT u.id,
u.Name,
l.type,
l.time,
ROW_NUMBER() OVER(PARTITION BY u.id ORDER BY l.time DESC) AS RowNumber
FROM user AS u
INNER JOIN session AS s
ON u.id = s.user_id
INNER JOIN log AS l
ON l.session_id = s.id
) u
WHERE RowNumber = 1;
I've assumed some schema (user.user_name?), but you can do this by grouping and an aggregate like Max:
SELECT u.user_id,
u.user_name,
Max(l.time) AS LastLogTime
FROM USER AS u
LEFT JOIN session AS s
ON u.id = s.user_id
INNER JOIN log AS l
ON l.session_id = s.id
GROUP BY u.user_id,
u.user_name;
You won't be able to select * as we need to use GROUP BY
Similarly, ORDER BY l.time isn't applicable any more - you could still order by e.g. user_name
I've also LEFT JOINED - this way, if the user has no sessions, it will still return a record for the user, possibly with a LastLogTime of NULL.

How do I match against multiple conditions on a table join?

I have two tables:
users attributes
id|name id|name|user_id
------- ---------------
1 |foo 1 |bla | 1
2 |bar 1 |blub| 1
1 |bla | 2
How do I create a query gives users with both the "bla" AND "blub" attributes?
In this case it should only return the user "foo".
I know that the data is not normalized.
SELECT u.*, a.id, b.Id, a.name, b.name FROM users u
JOIN attributes a
ON a.User_id = u.User_id AND a.name = 'bla'
JOIN attributes b
ON u.User_Id = b.User_id AND b.name = 'blub'
Assuming an attribute association to a user is unique...
if you need 3 conditions to be true add the conditions to the in and adjust count up 1.
SELECT u.name
FROM users u
INNER JOIN attributes a on A.user_Id = u.id
WHERE a.name in ('bla','blub')
GROUP by u.name
HAVING count(*)=2
and if you don't have an unique association, or you need to join to another table you could always do...
SELECT u.name
FROM users u
INNER JOIN attributes a on A.user_Id = u.id
WHERE a.name in ('bla','blub')
GROUP by u.name
HAVING count(distinct A.name)=2
for a slight performance hit. but this allows you to join and get back additional fields which others have indicated was a detriment to this method.
This allows for scaling of the solution instead of incurring the cost of joining each time to different tables. In addition, if you needed thirty-something values to associate, you may run into restrictions on the number of allowed joins.
SELECT U.NAME
FROM USERS U
INNER JOIN
ATTRIBUTES A1
ON U.ID = A1.USER_ID
INNER JOIN
ATTRIBUTES A2
ON U.ID = A2.USER_ID
WHERE A1.NAME = 'bla'
AND A2.NAME = 'blub'
You can use the INTERSECT operator
SELECT
u.id
,u.name
FROM users AS u
INNER JOIN attributes AS a
ON u.id = a.user_id
WHERE a.name = 'bla'
INTERSECT
SELECT
u.id
,u.name
FROM users AS u
INNER JOIN attributes AS a
ON u.id = a.user_id
WHERE a.name = 'blub'
;
Here is a demo on SQL Fiddle: http://sqlfiddle.com/#!6/68986/5
More info on SET operations in SQL: http://en.wikipedia.org/wiki/Set_operations_(SQL)
SELECT u.name
FROM attributes a
JOIN users u
ON u.id = a.user_id
WHERE a.name IN ('bla','bulb')