Query Across Join Exclusively - sql

I am trying to write a query to determine who, in my company, has roles that I specify, and no others.
The tables are User, UserRole, and Role. An (incorrect) example is below and I have tried a few different ways like this, but they all seem to return a user when they just contain the roles.
select U.Username from User U
join UserRole UR on U.UserID = UR.UserID
join Role R on UR.RoleID = R.RoleID
where R.RoleName in ('Role1', 'Role2', 'Role3')
Example User table
User ID
UserName
1
Joe
2
Bob
Example UserRole Table
UserID
RoleID
1
1
1
2
1
3
2
2
2
3
Example Role Table
RoleID
RoleName
1
Admin
2
SysAdmin
3
Manager
For example, I want to query for everyone that only has the SysAdmin, and manager roles. Although Joe has those roles I don't want him to be included in the result.
I feel like there is something simple that I am missing. However, after doing research online, I am unable to find a similar scenario.

If I understood your requirements:
select U.Username
from User U
join UserRole UR on U.UserID = UR.UserID
join Role R on UR.RoleID = R.RoleID
where R.RoleName in ('Role1', 'Role2', 'Role3')
GROUP BY U.Username
HAVING COUNT(R.RoleName)=3
The above is untested but should give you enough hints to solve your problem

Related

select a column from another table mulipletimes problem

User
USER_ID USERNAME
1 -
2 Chris
3 Dave
4 Vlad
Issue
Creator RESOLVER VERIFIER
2 3 4
2 3 1
3 1 1
expected output:
Creator RESOLVER VERIFIER
Chris Dave Vlad
Chris Dave -
Dave - -
current code I have:
SELECT creatorid.username, resolverid.username, verifierid.username
FROM issue
JOIN user creatorid ON issue.creator = creatorid.user_id
JOIN user resolverid ON issue.resolver = resolverid.user_id
JOIN user verifierid ON issue.verifier = verifierid.user_id
do i have to join the table 3 times to get the corresponding username of the user_id in issue table or is there is a simpler way of doing this? Asking as this is a simplified version of the tables, the User and Issue table contains a lot of other columns. Thanks
Because of the join, you will see each issue three times which is not what you want. You could use three scalar subqueries to get around that:
select i.id,
(select username from users u1 where u1.user_id = i.creator) as creator,
(select username from users u2 where u2.user_id = i.resolver) as resolver,
(select username from users u3 where u3.user_id = i.verifier) as verifier
from issue i;
This isn't going to be fast though.
Another option is to aggregate all user_id / username pairs into a JSON object, then use that in a sub-query:
select i.id, -- other columns from the issue table
u.names ->> i.creator::text as creator,
u.names ->> i.resolver::text as resolver,
u.names ->> i.verifier::text as verifier
from issue i
join lateral (
select jsonb_object_agg(user_id, username) as names
from users u
where u.user_id in (i.creator, i.resolver, i.verifier)
) u on true;
The traditional way to do this is:
select i.*, uc.username, ur.username, uv.username
from issue i left join
users uc
on uc.user_id = i.creator left join
users ur
on ur.user_id = i.resolver left join
users uv
on uv.user_id = i.verifier;

SQL query where record does not exist

Having trouble coming up with a sql query to identify records where a user is missing providers that should be listed in the userproviders table.
Providers
ProviderID
ProviderName
User
UserId
UserName
UserProviders
UserID
ProviderID
I'm trying to identify users that do not have any ProviderID assigned in the UserProvider table or do not have a specific provider listed in the userproviders table.
select *
from users u
left join UserProviders up on u.UserId = up.ClientId
where up.ProviderId is null
This gets me records where the user doesn't have any provider assigned, but its possible that the user could have a provider but is missing some other needed provider.
Thanks for any help!
For example
There are 3 providers in the provider table
Providers
1, A
2, B
3, C
User
1,Bill
2,Ted
User 1 has 2 records in UserProviders
Userproviders
1,1
1,2
2,1
2,3
So in this example I want to be able to determine that Bill doesn't have a record in Userproviders for provider 3 and that Ted doesn't have a record for provider 2.
If I understand correctly, It appears you can Use "IN" Like so:
select *
from users u
left join UserProviders up on u.UserId = up.ClientId
where up.ProviderId NOT IN ( select providerId from UserProviders Where UserId != u.UserId )
If you want all missing user/provider combinations, then use a cross join to get all combinations of users and providers. Then remove the ones that exist:
select u.userid, p.providerid
from users u cross join
providers p left join
userproviders up
on up.userid = u.userid and up.providerid = p.providerid
where up.userid is null;
If you just want users that have no provider at all, use not exists;
select u.*
from users u
where not exists (select 1
from userproviders up
where up.userid = u.userid
);
If you want users who don't have a specific provider, then use not exists:
select u.*
from users u
where not exists (select 1
from userproviders up
where up.userid = u.userid and up.providerid = ?
);

Select users that have n roles for reporting functions SQL

I have an application that uses a standard Role, User and UserRoleJoin architecture.
The application needs to send reports to users based on what group of roles they may be in. For example
Role:
ID Name
1 Lead
2 Mech
3 Elec
4 Auditor
5 Assigner
UserRole:
UserID RoleID
1 1
1 2
1 4
2 1
2 4
I want to be able to send one report to all users that are Leads, Mech and Auditors or a different report to all users that are leads and auditors.
The first report would only be sent to user 1 while the second report would be sent to user 2
Should I have a different table that handle roles to reports associations or should I deal with these groups by a select query?
There are several ways. I would be inclined to use group by and having:
select ur.UserId
from UserRole ur join
Role r
on ur.RoleID = r.Id
where name in ('Leads', 'Mech', 'Auditors')
group by ur.UserId
having count(*) = 3;
The advantage of this approach is that the logic is all in the having clause. You can make it more flexible, such as all Leads and Mechs who are not Auditors.
If those groups are stable enough they deserve a way to persist them in the DB, kind of Group(ID PK, Name), RoleGroup(GroupID , RoleID)
declare #grpName varchar(50) = 'my_group';
select ur.UserId
from UserRole ur
join RoleGroup rg on rg.RoleID = ur.RoleID
join Group g on rg.GroupID = g.ID and g.Name = #grpName
group by ur.UserId
having count(*) = (select count(*) n
from RoleGroup rg
join Group g on rg.GroupId = g.ID and g.Name = #grpName);

Select user details from a self relationship table

I have 3 tables (user, relationship and user_type) with these data (the relevant ones):
USER TABLE:
id, username, avatar, user_type_id
RELATIONSHIP TABLE:
id, user_id1, user_id2, relationship_points
USER_TYPE
id
I'm trying to create a single entry per relationship so, user with id "1" could be in user_id1 OR user_id2 in a relationship, so, I don't have to duplicate unnecessary data.
I already created (thanks to another StackOverflow answer) a query to select all relationship details from an user, but only if it's id is in "user_id1".
SELECT
r.id AS relationship_id,
r.relationship_points AS points,
u.username AS username,
u.avatar_url AS avatar
FROM
relationship AS r
INNER JOIN
user AS u
ON
r.user_id2 = u.id
INNER JOIN
user_type AS t
ON
u.user_type_id = t.id
WHERE
r.user_id1 = ?
But, as you can see, if the user is in "user_id2", it doesn't work.
I know I could make another query, but I think it's the "easy, lazy" way, and I would love to learn how to do this in a single query.
Let me know if this makes the trick:
SELECT
r.id AS relationship_id,
r.relationship_points AS points,
u.username AS username,
u.avatar_url AS avatar
FROM relationship AS r
INNER JOIN user u
ON r.user_id2 = u.id OR r.user_id1 = u.id
INNER JOIN user_type AS t
ON u.user_type_id = t.id
WHERE u.id = ?

SQL Self Join issues

I've got 2 tables that I need to get data from...my users table looks like this:
Users Table
-------------
UserID
FirstName
LastName
WebLogin
WebPassword
Active
UserAlternates Table
---------------------
UserAlternateID
UserID
AlternateUserID
Users Table Data
------------------
1, John, Brown, jbrown, jbrown, true
2, Mark, Smith, msmith, msmith, true
3, Tim, Stone, tstone, tstone, true
UsersAlternate Table Data
--------------------------
1, 1, 2
2, 1, 3
3, 2, 1
4, 3, 2
5, 3, 1
The UserID refers back to the UserID in the Users table and so does the AlternateUserID. This is a case where our program can have users that are "alternates" to other users. So in the above example, if John Brown would have Mark & Tim as Alternates, and Mark would have John as an alternate while Time would have Mark and John as alternates. I'm drawing a blank on how to write the SQL to show the alternate users for a given userid. So if I passed in UserID = 1, it would return:
2, Mark, Smith
3, Tim, Stone
I tried this but it returns 2 rows of the same user data (in this case, 2 John Brown's):
CREATE PROCEDURE [dbo].[GetUserAlternates]
#UserID int
AS
SELECT u.FirstName, u.LastName, ua.AlternateUserID
FROM Users u
INNER JOIN UserAlternates ua ON u.UserID = ua.AlternateUserID
WHERE u.UserID = #UserID
Any ideas?
CREATE PROCEDURE dbo.GetUserAlternates
#UserID INT
AS
BEGIN
SET NOCOUNT ON;
SELECT u.FirstName, u.LastName, u.UserID
FROM dbo.Users AS u
INNER JOIN dbo.UserAlternates AS au
ON u.UserID = ua.AlternateUserID
WHERE ua.UserID = #UserID; -- key difference here!
END
GO
How about something like
SELECT u.*
FROM UserAlternates ua INNER JOIN
Users u ON ua.AlternateUserID = u.UserID
WHERE ua.UserID = #UserID
It does not seem from your request that you need to join to the Users table twice, as the UserAlternates table already contains the original UserID.
You've got the wrong table alias:
WHERE ua.UserID = #UserID
Note: ua not u.
SELECT ua.AlternateUserID, U2.FirstName, U2.Lastname
FROM Users u
INNER JOIN UserAlternates ua ON u.UserID = ua.UserID
INNER JOIN Users U2 on U2.UserID = UA.AlternateUserID
WHERE u.UserID = #UserID