Find users not in an organization - sql

Given this data:
users
id name
== ====
1 Alice
2 Bob
3 Carl
organizations
id name
== ====
1 Aaa
2 Bbb
memberships
id organization_id user_id
== =============== =======
1 1 1
2 2 1
3 1 2
I want to find users that do not belong to a particular organization X:
users with no membership records at all
AND
users with membership records, but not for organization X.
For example, I want users that are not in organization 2. I am expecting:
users.id
========
2
3
Attempting with this join isn't working:
SELECT *
FROM users left join memberships on memberships.user_id = users.id
where memberships.id is null or memberships.organization_id <> 1
It's returning users 1,2,3 since 1 matches on the second where condition.
Is there an efficient way to do this?

Restricting your JOIN to organization of 2 and then testing for null is the one way to do what you're looking for e.g.
SELECT *
FROM users
LEFT JOIN memberships
ON memberships.user_id = users.id
AND memberships.organization_id = 2
WHERE memberships.id IS NULL
You can also use NOT IN
SELECT *
FROM users
WHERE id NOT IN (SELECT user_id from memberships where organization_id = 2 )

You can also use the minus set operator:
select "name" from users
minus
select "name" from users u inner join memberships m on u.id = m.user_id inner join organizations o on m.organization_id = o.id and o."name" = 'X'

select Users.*
from users
inner join memberships
on Users.id = memberships.id
left join organizations
on memberships.organization_id = organizations.id
where memberships.id is null AND memberships.organization_id = 2

Here's another option using the exists clause:
select * from users
where not exists
(select memberships.id from memberships
inner join organizations on memberships.organization_id = organizations.id
and memberships.user_id = users.id
where organizations.id = 2)

select u.*
from users u
left join memberships m
on u.id = m.user_id
left join organizations o
on m.organization_id = o.id
where m.organization_id not in (2)
EDIT: modified to include all users

Related

SQL select max value with join

I have two table:
USER (that can have multiple profiles)
id | name | profile | ...
BET
id | profile_id | date | amount | ...
I need to select name, and the date of the bet with maximum amount betted for every user from every profile.
So the output should be something like this:
name | max_amount | date_max_amount
USER (that can have multiple profiles)
It means that the user can have multiple ids and you would be identifying users on the basis of their names and have their max bids
SELECT u.name, MAX(b.amount)
FROM USER u
LEFT JOIN BET b ON (u.id = b.profile_id)
GROUP BY u.name
Then you can
SELECT u.name, d.max_amount, b.date AS date_max_amount
FROM USER u
LEFT JOIN (
SELECT u.name, MAX(b.amount) as max_amount
FROM USER u
LEFT JOIN BET b ON (u.id = b.profile_id)
GROUP BY u.name
)d ON (u.name = d.name)
LEFT JOIN BET b ON (u.id = b.profile_id AND b.amount = d.max_amount)

Selecting users who are in the same group with "name"

Users
id username
1 ryan
2 mike
3 annie
4 lisa
Groups
id name
1 football
2 hockey
Permissions
user_id group_id
1 1
1 2
2 1
4 2
So I'm looking for every username, who belong to at least one same group with username ryan. I'd also like to know everyone who is not in the same group.
SELECT Users.username
FROM Users
LEFT JOIN Permissions ON Users.id = Permissions.user_id
LEFT JOIN Groups.id = Permissions.group_id
WHERE Users.id;
So this is how I got it started, but have no idea how to continue.
Here is one method:
with ug as (
SELECT u.username, g.name
FROM Users u JOIN
Permissions p
ON u.id = p.user_id
)
SELECT DISTINCT ug.username
FROM ug
WHERE EXISTS (SELECT 1
FROM ug ug2
WHERE ug2.group_id = ug.group_id AND
ug2.username = 'Ryan'
);
The above assigns the group name to each user and then treats this as a single table. EXISTS is used to determine if the groups overlap.
Note that groups is not needed because you are not asking for the name of the group. The id is sufficient to answer the question.
I am not a big fan of using select distinct. It usually means that the database engine is doing more work than necessary -- creating duplicates and then removing them.
Here is an alternative solution that uses exists:
select u.*
from users u
where exists (select 1
from permissions p join
permissions pr
on pr.group_id = p.group_id join
users ur
on pr.user_id = ur.user_id and
ur.username = 'Ryan'
where p.user_id = u.id
);
You can do it with multiple joins of Users and Permissions:
select uu.username
from Users u
inner join Permissions p on p.user_id = u.id
inner join Permissions pp on pp.group_id = p.group_id
inner join Users uu on pp.user_id = uu.id and pp.user_id <> u.id
where u.username = 'ryan'
See the demo.
Results:
| username |
| -------- |
| mike |
| lisa |

Full outer join doesn't returns the results from right table

My application has a list of users who can be booked for a bookings. We need to pay the users for these bookings. I need to get the list of users who needs to be paid. A user can be paid if they have bookings or with payment created which has no bookings.
I tried the below:
SELECT
users.id as user_id,
user_bookings.id as user_booking_id,
user_bookings.payment_id as user_booking_payment_id,
payments.id as payment_id
FROM
users
LEFT OUTER JOIN user_bookings ON user_bookings.user_id = user.id
FULL JOIN payments ON payments.id = user_bookings.payment_id
Where payments.issued = false;
But it doesn't list the users with the payment with no bookings. It only lists the users with the bookings created. How can I get the users with payment which doesn't have any bookings?
EDIT
I tried the below query:
User with 37271 has the below payment records:
select payments.id, payments.issued from payments where payments.user_id=37271;
id | issued
--------+--------
133046 | f
133045 | t
(2 rows)
Below are the bookings I have:
select user_bookings.id, user_bookings.payment_issued, user_bookings.payment_id from user_bookings where user_id=37271;
id | payment_issued | payment_id
--------+-------------------+---------------
541136 | t | 133045
541137 | t | 133045
(2 rows)
While running the below query:
SELECT u.id as user_id, ub.id as user_booking_id, ub.payment_id as user_booking_payment_id,
p.id as payment_id, p.issued as payments_issued
FROM users u LEFT OUTER JOIN
user_bookings ub
ON ub.user_id = u.id FULL JOIN
(SELECT p.*
FROM payments p
WHERE NOT p.issued
) p
ON p.id = ub.payment_id
where users.id=37271;
It doesn't returns the payment with no bookings but it returns issued payments:
user_id | user_booking_id | user_booking_payment_id | payment_id | payments_issued
---------+-----------------+-------------------------+------------+-----------------
37271 | 541137 | 133045 | |
37271 | 541136 | 133045 | |
(2 rows)
SELECT
users.id as user_id,
user_bookings.id as user_booking_id,
user_bookings.payment_id as user_booking_payment_id,
payments.id as payment_id,
FROM
users
LEFT OUTER JOIN user_bookings ON user_bookings.user_id = user.id
FULL JOIN payments ON payments.id = linguist_bookings.payment_id
Where payments.issued = false
OR payments.issued IS NULL;
If you use an outer join, you turn it into an inner join if you use it in a WHERE clause; because NULL = [something] is never true.
You are turning the outer join into an inner join. Dealing with filtering with full outer join is tricky. I recommend a subquery:
SELECT u.id as user_id, ub.id as user_booking_id, ub.payment_id as user_booking_payment_id
p.id as payment_id,
FROM users u LEFT OUTER JOIN
user_bookings ub
ON ub.user_id = u.id FULL JOIN
(SELECT p.*
FROM payments p
WHERE NOT p.issued
) p
payments p
ON p.id = ub.payment_id;
I would, however, question why you want a FULL JOIN here. You could get payment ids with no other information from the other tables.
I would expect a LEFT JOIN to be sufficient:
SELECT u.id as user_id, ub.id as user_booking_id, ub.payment_id as user_booking_payment_id,
p.id as payment_id
FROM users u LEFT JOIN
user_bookings ub
ON ub.user_id = u.id LEFT JOIN
payments p
ON p.id = ub.payment_id AND NOT p.issued;

Inner Join of 3 Tables

In Table Approver:
No Userid
1 3
2 7
In Table Users:
No UserID RoleID
1 3 1
2 4 1
3 5 2
4 7 3
Table Roles
RoleID Name
1 ABC
2 BCD
3 CDE
I want to select rolename of users in table approver like:
Userid Name
3 ABC
7 CDE
I'm not 100% sure why approver.no is on the user and approver table.... I'm going to assume userId is unique in both situation..... if thats the case this should work:
select
u.userid,
r.name
from
Approver as a
inner join [Users] as u on a.userId = u.UserId
inner join [Roles] as r on u.roleId = r.roleId
if that is NOT the case and you need the approver.no user.UserId combo to be unique than the following should work:
select
u.userid,
r.name
from
Approver as a
inner join [Users] as u on a.userId = u.UserId
and a.No = u.No
inner join [Roles] as r on u.roleId = r.roleId
the differences between these two as far as the result set concerns can be found here: http://sqlfiddle.com/#!3/0daa9/4
Notice that the second query returns a single result against the provided data
select
a.Userid,
r.Name
from
Approver a
join Users u on a.no = u.no
join Roles r on u.RoleID = r.RoleID
SELECT U.UserID,
Name
FROM Approver A
JOIN Users U
ON A.UserId = U.UserId
JOIN Roles R
ON R.RoleId = U.RoleId
SQL FIDDLE DEMO
SELECT Approver.UserId, Name from Approver INNER JOIN Users
on Approver.No=Users.No
INNER JOIN Roles
on Users.RoleID=Roles.RoleID

How do I find what Ids are missing from a table when "those" ids are the result of a query?

These are my tables:
Customer Table
--------------
CUST_ID
SUPPLIER1
SUPPLIER2
Supplier Table
--------------
SUPPLIER_ID
USERID
User Table
----------
USER_ID
The rules:
Customers can have multiple suppliers but suppliers MUST be in the user table if they have a customer.
Because all suppliers have to be users, I need to find all used or assigned suppliers that are missing from the user table.
This query gets me all suppliers that are not users
SELECT
U.*
FROM
USER U
LEFT JOIN
SUPPLIER S ON S.USER_ID = U.USER_ID
WHERE
S.USER_ID IS NULL
But how do I get all of the customer's suppliers that are not users?
I tried this but it doesn't seem to be correct:
SELECT
*
FROM
USER U
LEFT JOIN
(SELECT C.SUPPLIER1, S.USER_ID FROM CUSTOMER C, SUPPLIER S WHERE
C.SUPPLIER1 = S.SUPPLIER_ID AND C.SUPPLIER1 IS NOT NULL)
S2 ON S2.USER_ID = U.USER_ID
WHERE
S2.USER_ID IS NULL
Thanks
SELECT
C.Supplier1
FROM Customer C
LEFT JOIN Supplier S
ON C.Supplier1 = S.supplier_id
WHERE S.user_id IS NULL
Is your problem that the Customer table is not normalized, i.e., you have repeated supplier columns in the customer table. You can join the suppliers table twice. e.g.
select C.Supplier1, U1.UserID, C1.Supplier2, U2.UserID
from Customer C
left join Supplier S1 on (S1.Supplier_id = C.Supplier1)
left join User U1 on (U1.UserID = C.Supplier1)
left join Supplier S2 on (SS.Supplier_id = C.Supplier2)
left join User U2 on (U2.UserID = C.Supplier2)
where
((not C1.Supplier1 is null) and (U1.UserID is null))
or ((not C2.Supplier1 is null) and (U2.UserID is null))
This could easily be more useful to use as the union of a supplier1 missing with supplier2 missing.