Writing JOIN queries in PostgreSQL with multiple tables - sql

I've got 3 tables that look vaguely like this:
Users
----------
UserID
Name
Phone
User Groups
-----------
GroupID
GroupActivity
Group Membership
---------------
UserID
GroupID
I am trying to print the user name and phone number for all the groups with members undertaking a certain activity. For example, group 1 activity = knitting, group 2 activity = sewing and group 3 activity = knitting. I want to select all the members that are knitting but I am unsure how to join the three tables in one query. This is what I tried so far:
SELECT
users.name, users.phone, groups.activity
FROM users
INNER JOIN group_membership ON group_membership.userID = users.userID
WHERE groups.activity = 'Knitting';
This ends up throwing the following error:
ERROR: missing FROM-clause entry for table "groups"
To fix this I tried adding teams to the FROM clause like so:
FROM users,groups
However, this in turn gives me this error:
ERROR: invalid reference to FROM-clause entry for table "members"
Any help here would be appreciated.

Try This
SELECT
users.name, users.phone, user_groups.activity
FROM users
INNER JOIN group_membership ON group_membership.userID = users.userID
inner join user_groups on user_groups.groupid = group_membership.groupid
WHERE user_groups.activity = 'Knitting';

You can have that shorter with table aliases and the USING clause:
SELECT ug.groupid, u.name, u.phone
FROM user_groups ug
JOIN group_membership USING (groupid)
JOIN users u USING (userid)
WHERE ug.activity = 'knitting';
Details in the manual here.

Related

Select name from SQL column

I am trying to select the names (along with the rest of the information on the table such as data, time, lesson) of each user that is inside a users table. The names I'm trying to select are the ones that are on the same row as a particular teacherID. However when I run this code I'm getting the same name for all records, when I should be getting two different ones.
SELECT FName, SName, bookedLessons.bookedHour,
bookedLessons.bookedDate, bookedLessons.lesson
FROM users JOIN bookedLessons
WHERE teacherID = 11
AND users.userID = (SELECT userID FROM bookedLessons WHERE teacherID = 11)
Your join needs an ON clause but not the subquery in the WHERE clause:
SELECT u.FName, u.SName, b.bookedHour, b.bookedDate, b.lesson
FROM bookedLessons b INNER JOIN users u
ON u.userID = b.userID
WHERE b.teacherID = 11

Select creating a Column with results of another query as a JSON

I'm trying to create a query that will fetch results from table parties. This table contains two foreign keys and I'm having trouble "mapping" these foreign keys.
For the first foreign key I need to map my host_id column to the actual name of the person users.name.
I was able to solve this with:
SELECT parties.*, users.name as host_name
FROM parties
INNER JOIN users ON parties.host_id = users.id
My second foreign key is to a table called guests which has a FK named party_refer which refers to parties.id.
The following query includes my Guests as part of the results (by appending all of my guests table columns in the results)
SELECT parties.*, users.name as host_name, guests.*
FROM parties
INNER JOIN users ON parties.host_id = users.id
INNER JOIN guests ON parties.id = guests.party_refer
I would like to modify this second INNER JOIN so that the results of (select * from guests) are returned as a single Column called Guests with the results expressed as a JSON.
I believe I need to use array_to_json(array_agg(row_to_json())) but I've been trying for hours to get it working with no luck.
I think you are looking for
SELECT parties.*, users.name as host_name, json_agg(row_to_json(guests)) as guests
FROM parties
INNER JOIN users ON parties.host_id = users.id
INNER JOIN guests ON parties.id = guests.party_refer
GROUP BY parties.id, users.name
Although a subquery may be simpler than extensive grouping:
SELECT
parties.*,
users.name as host_name,
(SELECT json_agg(row_to_json(guests))
FROM guests
WHERE guests.party_refer = parties.id) as guests
FROM parties
INNER JOIN users ON parties.host_id = users.id
(online demo)
You might prefer an explicit json_build_object instead of the row_to_json, e.g.
json_agg(json_build_object('guestName', guests.name))

Ambiguous column name error for user_id

My users table has the columns
user_id, email
My invites table has the columns
invite_id request_id user_id sent_time
When I run the following query, I get the two tables joined into 1, which is expected.
'SELECT * FROM users INNER JOIN invites ON users.user_id = invites.user_id'
However, when I run the following query,
'SELECT user_id FROM users INNER JOIN invites ON users.user_id = invites.user_id'
I get the following error
OperationalError: (sqlite3.OperationalError) ambiguous column name: user_id [SQL: 'SELECT user_id FROM users INNER JOIN invites ON users.user_id = invites.user_id']
Any help appreciated.
I think the message is pretty clear. SQLite doesn't know what table user_id is coming from.
One simple solution is to qualify the column name usinga table alias:
SELECT u.user_id
FROM users u INNER JOIN
invites i
ON u.user_id = i.user_id;
Another method is to use USING rather than ON:
SELECT user_id
FROM users u INNER JOIN
invites i
USING (user_id);
You need to qualify the column name with table name like below cause both table involved in query have the same column name
SELECT `users`.user_id
FROM users
INNER JOIN invites ON `users`.user_id = invites.user_id
This means you need to be specific about which user_id column you want to display. Even though they're joined that doesn't guarantee they're identical. Some types of joins allow NULL values on one side of the match (e.g. LEFT JOIN), so you need to ask for a particular value:
SELECT users.user_id FROM users INNER JOIN invites ON users.user_id = invites.user_id

Using binary logic in PostgreSQL JOIN queries

I've got 3 tables that look vaguely like this:
Users
----------
UserID
Name
Phone
User Groups
-----------
GroupID
Activity
Group Membership
---------------
UserID
GroupID
Independent Actives
-------------------
UserID
Activity
The idea is that a user can perform an activity either as part of a group or on their own. What I want to do is return all the people that partake in a certain activity. What I have been able to write so far lets me return all the users which are in groups that undertake that activity. What I want to add to this is the ability to see the people that do the activity independently. This is what I have so far:
SELECT
users.name, users.phone, user_groups.activity
FROM users
INNER JOIN group_membership ON group_membership.userID = users.userID
INNER JOIN user_groups ON user_groups.groupID = group_membership.groupID
WHERE user_groups.activity = 'Knitting';
The above bit works fine and it shows all of the users that are part of groups that do knitting, but I also want it to show all the users that are knitting independently. This is what I have attempted to add:
SELECT
users.name, users.phone, user_groups.activity
FROM users
INNER JOIN group_membership ON group_membership.userID = users.userID
INNER JOIN user_groups ON user_groups.groupID = group_membership.groupID
INNER JOIN independent_activity ON independent_activity.userID = users.userID
WHERE user_groups.activity = 'Knitting' OR independent_activity.activity = 'Knitting';
The problem here is the syntax, I understand the algorithm that I'm trying to do but I don't know how to transfer it into sql and so any help is appreciated.
You could use a UNION in this case
SELECT users.NAME
,users.phone
,user_groups.activity
FROM users
INNER JOIN group_membership ON group_membership.userID = users.userID
INNER JOIN user_groups ON user_groups.groupID = group_membership.groupID
WHERE user_groups.activity = 'Knitting'
UNION
SELECT users.NAME
,users.phone
,independent_activity.activity
FROM users
INNER JOIN independent_activity ON independent_activity.userID = users.userID
WHERE independent_activity.activity = 'Knitting';
You also might want to lookup the differences between a UNION and a UNION ALL and decide the one that suites your requirement.
You've got a working answer from SoulTrain. However, for completeness sake I'd like to mention that you don't have to join all those tables. (You could use outer joins here and remove duplicate matches with DISTINCT, but that's not necessary. You don't have to query the users table twice either. And you don't need UNION for doing the distinct job.)
Simply select from the one table you want to display data from, i.e. the users table, and then use EXISTS or IN to get only those users that are either in one set or another.
select name, phone
from users
where userid in
(
select userid
from independent_actives
where activity = 'Knitting'
)
or userid
(
select userid
from group_membership
where groupid in (select groupid from user_groups where activity = 'Knitting')
)

Left Outer Join with subqueries?

----------
User
----------
user_ID(pk)
UserEmail
----------
Project_Account
----------
actno
actname
projno
projname
ProjEmpID
ProjEmpMGRID
Where ProjEmpID,ProjEmpMGRID is the user_id and ProjEmpMGRID can be null.
I need to look up the useremail and display the table project_account. I need to query with actNo which has duplicate values.
My query goes like this:
select projno,projname,actno,actname,
(select u.user_email as project_manager from project_account c left outer join users u
on u.user_id = c.ProjEmpID where actno='some no')as project_manager,
(select u.user_email as program_manager from project_account c left outer join users u
on u.user_id = c.ProjEmpMGRID where actno='someno') as program_manager
from project_account where actno='someno'
The error message I get in Oracle:
ora-01427 single row subquery returns
more than one row
As my subquery returns more than one email id, I get this error. As I said, act no is not unique. I could understand the error, but I couldn't figure out the solution. I am doing a left outer join in a subquery because there might be nulls in prog manager id.
Any help would be appreciated.
The error you are getting is that one of your subqueries (either for project_manager or program_manager) is giving you back more than one ID based on your conditions. This kind of makes sense, since multiple project accounts could have the same "actno" since you haven't specified that as a Primarky Key (pk)
furhter, rather than using subqueries, just join directly to the user tables to find the IDs
select projno,projname,actno,actname,
project_user.user_email as project_manager,
program_user.user_email as program_manager
from project_account
left join User as project_user
on project_account.ProjEmpID = project_user.user_id
left join User as program_user
on project_account.ProjEmpMGRID = program_user.user_id
where actno='someno'
What about something like:
select c.projno, c.projname, c.actno, c.actname, u.user_email as project_manager, us.user_email as program_manager
from project_account c
left outer join users u
on u.user_id = c.ProjEmpID
left outer join users us
on us.user_id = c.ProjEmpMGRID
WHERE actno = 'someno'
This way you aren't running subqueries and returning multiple results and trying to store them as one value.
Why don't you simply use this?
select projno, projname, actno, actname, (select user_email from users where user_id = pa.projempid), (select user_email from users where user_id = pa.projempmgrid)
from project_account pa