Compare 2 tables with results as a column - sql

I have 2 tables, Users and Admins. Users has userID, userName, and fullName. Admins has storeID and userID.
What I'm trying to accomplish is a query that would return results that list all userIDs, userNames, fullNames, and a user defined column isAdmin that would be 0 or 1 if that particular userID is found in the Admins table.
I believe it should be possible, but I'm just drawing a blank at the moment. This will be in a stored procedure so I considered just creating temporary tables for each possibility of just a regular user or only Admins and go from there. But I would really prefer if it were all in 1 temporary table.
Any help would be greatly appreciated.

select u.*, case when a.userid is null then 0 else 1 end as isadmin
from users u
left join admins a on a.userid = u.userid

Related

SQL Query: User/Group/Role and UserGroup/GroupRole Connecting User to Role

Good evening,
I have been struggling with what I think should be a fairly basic SQL query, but I can't seem to figure it out. Particularly because it seems as if there are multiple approaches (joins/ sub-queries).
I have three primary tables, and two tables connecting them.
USER
uID - PK
name, email, ...
GROUP
gID - PK
name
ROLE
rID - PK
permission
USERGROUP
uID - FK
gID - FK
GROUPROLE
gID - FK
rID - FK
These are many to many relationships, such that a user can be in multiple groups, and a group can have multiple roles.
I would like to have a query that ultimately gives me all of the roles that are connected to a given user through the groups that he belongs. Thus the uID is known, but nothing else. The output could have as few as one column with the rID, as that is all I really need for the program logic.
Is it possible to perform a query on the result of a previous query? I can easily find all the groups a user is in, and given each group, I can easily find the roles associated, but I get tripped up by the fact that there are many groups associated with a user, not just a single one which would be easier.
I would appreciate any help!
Thanks in advance.
This is just joins and aggregation:
select array_agg(distinct r.permission) as permissions
from usergroups ug join
grouproles gr
on ug.gid = gr.gid join
roles r
on gr.rid = r.id
where ug.uid = ?;
If you want the permissions in separate rows, then:
select distinct r.permission
from usergroups ug join
grouproles gr
on ug.gid = gr.gid join
roles r
on gr.rid = r.id
where ug.uid = ?;
I am leaving the distinct because different groups could share the same permissions.

SQL report duplicated rows into count into count

First of all let me tell you I have no experience in SQL whatsoever, however I changed my positions lately and given the situation it'd be easier for me to run some script then check each record in the application individually. Here's the scenario:
I have two tables:
Users with userID, username, email etc.. and
Documents with DocumentID and UserID, document name and again some other columns.
I want to create a report that will help me check if users have documents attached to their profile.
What I am doing now is
SELECT UsersTable.UserID,
DocumentsTable.DocumentID,
DocumentsTable.UserID
FROM UserTable
LEFT JOIN DocumentTable ON UserTable.UserID = DocumentTable.UserID
The problem I am having is that, some users already have 2 or more documents attached to their profile, this is causing a duplication.
For example, in the report I see such rows
User1 DocumentA
User2 DocumentA
User2 DocumentB
User2 DocumentC
User3 DocumentA
etc.
Is there a way to somehow convert those document to values count based on the UserID? so instead I'd like to see
User1 1
User2 3
User3 1
You are looking for GROUP BY. I would recommend writing the query as:
SELECT ut.UserID, COUNT(dt.UserID)
FROM UserTable ut LEFT JOIN
DocumentTable dt
ON ut.UserID = dt.UserID
GROUP BY ut.UserID
ORDER BY ut.UserID;
Notes:
The use of table aliases makes the query easier to write and to read.
The ORDER BY guarantees that the results are ordered by the user id.
The COUNT() is based on the second table, because there might not be a match.

Setting a column value for only one user depending on results

I'm currently playing around with SQL and trying to find the best way to accomplish this:
I currently have a user table that has a user_id, organisation_id, registered_datetime. There are
a number of users in this table with different organisations. There may be 3 different users in
1 organisation, or 1 in 1 organisation, etc.
I have added a new column called admin_user and I am trying to string up an SQL statement together
to update the admin user column. There can only be one admin user per organisation, and I want
the user who registered the earliest for that organisation to be the admin.
I could do this manually but it would take time if I had a lot of users. What would be the best
way to accomplish this?
EDIT:
So I have a number of users like this with the columns. The ones highlighted are the users that has registered the earliest. I want to be able to set those users as an admin user. The only admin user within their organisation and set the rest to 0. 1 (Admin) 0 (Not Admin)
This SQL query will mark users which registered_datetime are lowest in its organisation_id as admin.
UPDATE users SET admin_user = 1
WHERE user_id IN (
SELECT u.user_id FROM users u
WHERE u.registered_datetime IS NOT NULL AND NOT EXISTS(
SELECT 1 FROM users iu
WHERE iu.organisation_id = u.organisation_id AND iu.registered_datetime < u.registered_datetime
)
)
You might want to update all users to admin_user = 0 before this code, so all your users will have their values set.
One caveat here, if two users in one organisation were registered in exact same time, then both of them will be marked as administrators.
Update
I have added u.registered_datetime IS NOT NULL into the WHERE clause to filter out users with NULL in registered_datetime.
MSSQL
In MsSql server I usually solve this problem a in another way, by using ROW_NUMBER():
WITH base AS (
SELECT user_id, ROW_NUMBER() OVER ( PARTITION BY organisation_id ORDER BY registered_datetime ASC ) AS rn
FROM user
WHERE registered_datetime IS NOT NULL
)
UPDATE user SET is_admin = 1
WHERE user_id IN (
SELECT base.user_id FROM base WHERE rn = 1
)
This is too long for a comment.
You are describing three different tables:
Users
Organizations
UserOrganizations
The last has one row per user and per organization. This provides the mapping between the two. This can be called a "mapping" table, "junction" table, or "association" table.
How you implement one admin per organization depends on the database you are using.
You do not need the admin_user column. You need a column isadmin.
When a user is registered, if he is the first in the organization, then the isadmin column has the value 1, otherwise 0
Also, you can use the AAA query to find the administrator
SELECT `table`.`user_id`, MIN(`table`.`registered_datetime`) WHERE `organisation_id`=...

Select a user by their username and then select data from another table using their UID

Sorry if that title is a bit convoluted... I'm spoiled by an ORM usually and my raw SQL skills are really poor, apparently.
I'm writing an application that links to a vBulletin forum. Users authenticate with their forum username, and the query for that is simple (selecting by username from the users table). The next half of it is more complex. There's also a subscriptions table that has a timestamp in it, but the primary key for these is a user id, not a username.
This is what I've worked out so far:
SELECT
forum.user.userid,
forum.user.usergroupid,
forum.user.password,
forum.user.salt,
forum.user.pmunread,
forum.subscriptionlog.expirydate
FROM
forum.user
JOIN forum.subscriptionlog
WHERE
forum.user.username LIKE 'SomeUSER'
Unfortunately this returns the entirety of the subscriptionlog table, which makes sense because there's no username field in it. Is it possible to grab the subscriptionlog row using the userid I get from forum.user.userid, or does this need to be split into two queries?
Thanks!
The issue is that you are blindly joining the two tables. You need to specify what column they are related by.
I think you want something like:
SELECT * FROM user u
INNER JOIN subscriptionlog sl ON u.id = sl.userid
WHERE u.username LIKE 'SomeUSER'
select * from user u JOIN subscriptions s ON u.id = s.id where u.username = 'someuser'
The bit in bold is what you want to add, it combines the 2 tables into one that you return results from.
try this
SELECT
forum.user.userid,
forum.user.usergroupid,
forum.user.password,
forum.user.salt,
forum.user.pmunread,
forum.subscriptionlog.expirydate
FROM
forum.user
INNER JOIN forum.subscriptionlog
ON forum.subscriptionlog.userid = forum.user.userid
WHERE
forum.user.username LIKE 'SomeUSER'

SQL Insert based on role

I have a database with 3 tables:
Table 1 (Department) - This is a table with columns for departments and departmentID's
Table 2 (SecurityMap) - This is a table that maps rolenames to department ID's
Table 3 (customer info) - this is the info that is displayed to users based on their role memberships
I have all of the SELECT based on role functions working.
What I need is to figure out how to insert a specific value into the DepartmentID column within Table 3 by default, based on the users role membership. For instance, when someone is adding a new row to the database - in addition to the data they are supplying within the "add" form, I need a default value inserted into this column. If they are a member of the Marketing role, it should be a 1, if they are a member of the IT role, it should be a 2, etc...
Ideally, this would be done without any knowledge to the user that it is even happening. I would assume that I need to use an "Instead Of" trigger, but I have no idea how to proceed....
Shouldn't be too difficult:
In your app, keep track of the logged-in user and their role.
When your app saves the customer data, make sure it passes the database the role ID as well as the user-entered data (a stored procedure would be ideal here)
When the database processes the supplied data, it saves the role ID into the appropriate column.
this should work provided the rolename-column (or id) is unique in the securityMap-table otherwise the select could return more than one value, maybe you need to select the departmentId differently then.
insert into customer_info(otherdata, departmentId) values('data', (select departmentId from securityMap where rolename = 'userrole'))
Edit:
since you mentioned db_owner maybe this can help you (from http://www.sqlservercentral.com/Forums/Topic411310-338-1.aspx)
WITH CTE_Roles (role_principal_id)
AS
(
SELECT role_principal_id
FROM sys.database_role_members
WHERE member_principal_id = USER_ID()
UNION ALL
SELECT drm.role_principal_id
FROM sys.database_role_members drm
INNER JOIN CTE_Roles CR
ON drm.member_principal_id = CR.role_principal_id
)
SELECT USER_NAME(role_principal_id) RoleName
FROM CTE_Roles
ORDER BY RoleName;
you could join that with the SecurityMap-table to filter out roles like db_owner