Maybe I'm not clear on the topic for what I'm trying to ask, so here is my question:
I have three tables for now, say, User, User Role, Role, which make reference to each other:
User
========
USER_ID | USER_NAME
1 | User A
2 | User B
User Role
========
USER_ID | ROLE_ID
1 | 1
1 | 2
2 | 1
Role
========
ROLE_ID | ROLE_NAME
1 | Admin
2 | Supervisor
As you can see, User A has two role for now: Admin and Supervisor
What I'm trying to do now is selecting all users with role Admin, then the sql result should display all other roles related to that user also.
Say, the sql is executed like this:
select A.*, C.ROLE_NAME from User A, User Role B, Role C where A.USER_ID = B.USER_ID and B.ROLE_ID = C.ROLE_ID and B.ROLE_ID = '1'
The above sql should only display User A with only one role, Admin:
USER_ID | USER_NAME | ROLE_NAME
1 | User A | Admin
2 | User B | Admin
But what I want is, beside Admin, the sql result should also display User A with ROLE_NAME = Supervisor:
USER_ID | USER_NAME | ROLE_NAME
1 | User A | Admin
1 | User A | Supervisor
2 | User B | Admin
Any ideas?
Use exists:
with ur as (
select u.*, C.ROLE_NAME
from User u join
User_Role ur
on u.user_id = ur.user_id join
Role r
on r.role_id = ur.role_id
)
select ur.*
from ur
where exists (select 1
from ur ur2
where ur2.user_id = ur.user_id and ur2.role_name = 'admin'
);
Also, never use commas in the FROM clause. Always use proper, explicit JOIN syntax with the condition in the ON clause.
Second, use meaningful table aliases rather than arbitrary letters such as "a", "b", and "c".
Related
I'm trying to query a user from my user table, while also getting a list of user roles from the user_role table. Here's my schema:
user
---------------------
|uuid | username |
---------------------
|xxx-xx-x| Joe |
---------------------
user_role
----------------------
|user_uuid| role_uuid |
----------------------
|xxx-xx-x | yyy-yy-y |
----------------------
|xxx-xx-x | zzz-zz-z |
----------------------
role
----------------------
|role_uuid| name |
----------------------
|yyy-yy-y | Moderator |
----------------------
|zzz-zz-z | Tester |
----------------------
Here's my current query that only gets user info:
SELECT u.uuid, u.username FROM user AS u WHERE u.uuid = ($1)
How do I modify this to also get a list (array) of the roles a user has? I'd like to be able to do this in one query.
One option is aggregation in a lateral join:
SELECT u.uuid, u.username, r.role_names
FROM users AS u
CROSS JOIN LATERAL (
SELECT array_agg(r.name) role_names
FROM role r
INNER JOIN user_role ur on ur.role_uuid = r.role_uuid
WHERE ur.user_uuid = u.uuid
) r
WHERE u.uuid = $1
Notes:
parentheses are not needed around the scalar parameter
user is a SQL keyword, hence not a good choice for a table name - I renamed it to users in the query
I am trying to write select that filters users by containing at least one permission with display = 1. My tables looks like the following:
User:
-id
-user_email_id
UserGroupRelation:
-id
-user_id
-user_email_id
-user_group_id
UserGroupPermission
-id
-permission_id
-display(bool)
-user_group_id
One user, can have multiple groups and permissions that are assigned to group. I would like to select all users that are in groups with at least one permission that have display set to 1. How would I do it?
That is what I tried and it didn't work.
SELECT *
FROM User as u
LEFT OUTER JOIN
UserGroupRelation as ugr on ugr.user_id = u.id and ugr.user_email_id = u.user_email_id
LEFT OUTER JOIN
UserGroupPermission as ugp on ugp.user_group_id = ugr.user_group_id
WHERE
ugp.display = 1
I think the problem is that User has multiple group relations and groups have multiple permissions. Maybe should I use another select in FROM clause?
Sample data:
User:
{id=1, user_email_id=1},
{id=2, user_email_id=2}
UserGroupRelation:
{id-1, user_group_id=1, user_id=1, user_email_id=1},
{id=2, usergroup_id=2, user_id=2, user_email_id=2}
UsergroupPermission:
{id=1, permission_id=1, display=1, user_group_id=1},
{id=2, permission_id=1, display=0, user_group_id=2}
Result should return only user that has id=1 because he is connected to group_id = 1 which has permission with display=1, user with id 2 is not in result because he has permission that has display= 0. It is simple example because user can have multiple groups and group can have multiple permissions but only one that h as display = 1 is required to be in the result
I believe your SELECT is simply badly formatted. Try running the following in place of what you have shared above:
SELECT * FROM #User AS u
INNER JOIN UserGroupRelation AS ugr
ON ugr.[user_id] = u.id AND ugr.user_email_id = u.user_email_id
LEFT OUTER JOIN UserGroupPermission AS ugp
ON ugp.user_group_id = ugr.user_group_id
WHERE
ugp.display = 1;
If I run this in my test environment against your sample data, I recevie the following resultset:
+----+---------------+----+---------+---------------+---------------+----+---------------+---------+---------------+
| id | user_email_id | id | user_id | user_email_id | user_group_id | id | permission_id | display | user_group_id |
+----+---------------+----+---------+---------------+---------------+----+---------------+---------+---------------+
| 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
+----+---------------+----+---------+---------------+---------------+----+---------------+---------+---------------+
On a side note, I would change LEFT OUTER JOIN UserGroupPermission AS ugp to INNER JOIN UserGroupPermission AS ugp given that "display" is required in your expected resultset.
What's a query that I can use to get a list of all logins associated with each user in SQL Azure?
So far I've found the following two queries to get all users and all logins, but I haven't found any way to see which user goes with which login:
SELECT * from sys.sql_logins -- get all logins
SELECT * from sys.sysusers -- get all users
In case you find it helpful, here's the documentation for the structures of those the tables:
sys.sql_logins:
https://msdn.microsoft.com/en-us/library/ms174355.aspx?f=255&MSPPError=-2147217396
Column names: name, principal_id, sid, type, type_desc, is_disabled, create_date, modify_date, default_database_name, default_language_name, credential_id, is_policy_checked, is_expiration_checked, password_hash
sys.sysusers: https://msdn.microsoft.com/en-us/library/ms179871.aspx
Column names: uid, status, name, sid, roles, createdate, updatedate, altuid, password, gid, environ, hasdbaccess, islogin, isntname, isntgroup, isntuser, issqluser, isaliased, issqlrole, isapprole
It's hard to tell you your correct answer b/c we don't know the structure of your tables. If you share that we can help more. But below should get you to where you need to go.
They way to do it is by a MySQL JOIN. In this case you should use a INNER or OUTER JOIN depending on how your database is structured.
If you have 2 tables that are structured below you can do an FULL OUTER JOIN
[sys.sql_logins]
| sid| userID | name |
| 1 | 1 | ssmith |
| 2 | 2 | bbob |
[sys.sysusers]
| sid| name |
| 1 | Sam Smith |
| 2 | Billy Bob |
You can use the following query to do it
SELECT A.name as userName, B.name as login
FROM sys.sysusers A
FULL OUTER JOIN sys.sql_logins B
ON A.sid = B.sid
This will result in :
| userName | logins |
| Same Smith | ssmith |
| Billy Bob | bbob |
Here is a link to more types of MySQL Joins
https://www.sitepoint.com/understanding-sql-joins-mysql-database/
http://dev.mysql.com/doc/refman/5.7/en/join.html
http://www.w3schools.com/sql/sql_join.asp
I think you can join on the sid, try this (but maybe just select whatever columns you want):
select l.*, u.*
from sys.sql_logins l
join sys.sysusers u on l.sid = u.sid
Sorry for the confusing title. The situation really isn't that complicated but I'm having trouble verbalizing it well! Imagine a model with users, roles, and resources. A user can belong to many roles, and a resource can belong to many roles as well. If I query for a specific resource / userId (or, more importantly, a generic query for resources), I only want that resource to return if the user is in ALL the same roles as that resource. I am able to get results if there are ANY matches, but I can't figure out the sql to do exactly what I want.
For example, a users table with a couple of users in it:
Table "public.users"
Column | Type | Modifiers
----------+--------+-----------
id | bigint |
username | text |
postgres=# select * from users;
id | username
----+----------
1 | foo
2 | bar
(2 rows)
And a couple of roles:
postgres=# \d roles;
Table "public.roles"
Column | Type | Modifiers
-----------+--------+-----------
id | bigint |
role_name | text |
postgres=# select * from roles;
id | role_name
----+-----------
1 | roleOne
2 | roleTwo
(2 rows)
User one is in both roles:
postgres=# select * from user_role_join where user_id = 1;
user_id | role_id
---------+---------
1 | 1
1 | 2
(2 rows)
While user two is in only the first role:
postgres=# select * from user_role_join where user_id = 2;
user_id | role_id
---------+---------
2 | 1
(1 row)
Lastly, I have a resource which is assigned to two roles..
postgres=# \d resource_role_join
Table "public.resource_role_join"
Column | Type | Modifiers
-------------+--------+-----------
resource_id | bigint |
role_id | bigint |
postgres=#
postgres=# SELECT resource.name, resource.id, resource_role_join.role_id FROM resource INNER JOIN resource_role_join ON resource.id = resource_role_join.resource_id;
name | id | role_id
---------+----+---------
special | 1 | 1
special | 1 | 2
(2 rows)
postgres=#
This query, of course, will return my resource:
SELECT DISTINCT resource.id, resource.name
FROM resource
INNER JOIN resource_role_join ON resource.id = resource_role_join.resource_id
INNER JOIN user_role_join ON resource_role_join.role_id = user_role_join.role_id
WHERE user_role_join.user_id = 2 AND resource_id = 1;
But I'm trying to figure out how to structure my query so that when I use a user_id that is not in ALL roles that the resource belongs to, the resource will not come back in query results:
SELECT DISTINCT resource.id, resource.name
FROM resource
INNER JOIN resource_role_join ON resource.id = resource_role_join.resource_id
INNER JOIN user_role_join ON resource_role_join.role_id = user_role_join.role_id
WHERE user_role_join.user_id = 2 AND resource_id = 1;
I tried adding the users table itself to the join:
SELECT DISTINCT resource.id, resource.name
FROM resource
INNER JOIN resource_role_join ON resource.id = resource_role_join.resource_id
INNER JOIN user_role_join ON resource_role_join.role_id = user_role_join.role_id
INNER JOIN users ON users.id = user_role_join.user_id
WHERE users.id = 2 AND resource_id = 1;
I'm starting to suspect I'll need some kind of subquery. Any help is greatly appreciated! I understand I could also check role membership after the results come back, but I'd like to be able to limit it through the query for when the user is querying for many resources at once.
Using not exists to also cover the special case when a resource has no role (= it is public). Then we left outer join everything and see if we get a role of the resource for which the user doesn't have one.
SELECT DISTINCT
r.id,
r.name
FROM
resource r
WHERE
NOT EXISTS(
SELECT
*
FROM
resource_role_join rrj
LEFT OUTER JOIN
user_role_join urj
ON
urj.role_id = rrj.role_id AND
urj.user_id = :USER_ID
WHERE
r.id = rrj.resource_id AND
urj.role_id IS NULL
)
;
related to this answered question I asked yesterday Recursive query where anchor and member have unions
I have a junction table that contains
menuItem | roleID | userID
1 | 2 | NULL
1 | 5 | NULL
2 | NULL | 81
I have a roles table
roleID | roleName
1 | admin
3 | super admin
5 | basic user
What I would like is when querying for the USERS who have access to this menu item to also include to the admin users (roleIDs 1 and 3). In the linked question I was able to include the admin users by including the IDs in the join clause. But because the roleID is null when including the individual users, I can't ever get the results to produce:
userName | menuItem
admin1 | 2
admin2 | 2
realUser | 2
At best i've been able to get
userName | menuItem
realUser | 2
realUser | 2
realUser | 2
How can I write this so the admin users are included as part of the result? thanks.
It sounds like you want to join a table Users to a table Menu
when
The user's Role ID is 1 or 3 and the menu role id is null
Or the userid in the user table matches the userid
Or the users's role id matches the menu's role id
So that looks like this
From
users u
INNER JOIN Menu m
ON (u.RoleID in (1,3) and m.roleID is Null)
or u.UserId = m.userId
or u.RoleId = m.RoleId