Normalizing Foreign Keys with Junction & Lookup Tables - database

I've been scratching my head lately over the relationship between database normalization and foreign keys with respect to junction tables and lookup tables.
I’ve currently have the following tables: Users, UserTypes, Roles, UsersInRoles, and Permissions. UserTypes is simply a lookup table providing the name of the type, with description, via a foreign key in the Users table. Roles are the various roles with associated Permissions linked to each User via the UsersInRoles table.
I need to come up with a structure that allows me to provide multiple Roles for each User, in addition to special permissions for each respective User that may not be covered in the fixed Roles of which they are a member.
I had a foreign key to my UsersInRoles table from the Users table, but decided that it just didn’t make sense. Conversely, it seems to make perfect sense to use a foreign key from the Users table to the UserTypes table. Is this the rule of thumb? That junction tables have foreign keys linking to the primary keys of the tables it joins, while master tables have foreign keys linking to the primary key of associated lookup tables?
Parameters:
Each User can have one or multiple Roles
Each Role has a fixed set of Permissions
Each User can have additional Permissions not provided by their Roles
I suspect I may also need a PermissionsInRoles junction table as well as one for PermissionsInUsers? But this is just ridiculous isn't it? There just must be a better way. I'm thoroughly convinced that I'm losing my mind here, lol. Any help would be greatly appreciated. This has got my head spinning :P
UPDATE
Is this basically how it would be setup? I might get rid of the UsersInRoles table so each user can only be in one role, and additional permissions can be added via the SpecialPermissions junction table. From a UI standpoint, I was thinking it might be good when assigning permissions to a user, selecting a "Role" would simply check the appropriate boxes associated with that role, then you customize that and submit. That way I think I would only need a junction table between the Users and Permissions tables perhaps? Ugh. This is quite daunting for a first time database designer haha. Remember when you were just starting out? Or maybe you guys are more of a genius than I am, lol.
Schema Image link (can't post images yet)
Here's a neat scholarly article (albeit 10 years old) on query-driven database design titled: "Robust Database Design for Diverse Query Patterns". The Conclusion section has an interesting approach.

I suspect I may also need a PermissionsInRoles junction table as well
as one for PermissionsInUsers?
Well, you already said one of the requirements was "Each Role has a fixed set of Permissions". So to fulfill that requirement, you need to store permissions that apply to each role.
Table: role_permissions
PK: (Role, Permission)
Role Permission
--
User Create
User Update
Admin Create
Admin Update
Admin Delete
You don't need two different tables to implement that requirement.
By the same token, you've already said "Each User can have additional Permissions not provided by their Roles". To fulfill that requirement, you have to store user-specific permissions.
Table: user_permissions
PK: (username, permission)
username permission
--
user1 Rename
user1 Leak to News of the World
user2 Randomly corrupt data
So, again, you don't need two different tables to implement that requirement. Both those tables are in 5NF.
But this is just ridiculous isn't it?
What's ridiculous?
That you have very elaborate requirements for permissions?
That you store business data (like permissions) in tables?
That it takes more than one table to model your permission requirements?
Something else?
If you want specific advice about your actual tables, edit your question and insert DDL for your tables.
Later
I looked at your diagram. Not every table needs an id number; id numbers have nothing to do with normalization.
If I were designing your system, I probably wouldn't use id numbers in the tables Roles, Permissions, and UserTypes until I saw a performance problem that id numbers could fix. (In most systems over the last 30 years, that means, well, almost never.) Before I used an id number, I'd also consider and test using a human-readable code instead. Human-readable codes often don't require joins; id numbers always require joins.
In most SQL dbms, you can combine a data type and check constraint in a CREATE DOMAIN statement. In PostgreSQL, you might use something like this to reduce the number of tables.
CREATE DOMAIN role AS VARCHAR(7) NOT NULL
CHECK (VALUE in ('Admin', 'User', 'Guest'));
Followed by
CREATE TABLE user_roles (
user_id integer not null references users (id),
role_name role
);
Replacing a table with a CREATE DOMAIN statement is most useful when the number of rows is stable and relatively small. Personally, I'd rather have the tables.
If you stick with id numbers, you also need UNIQUE constraints on Roles.RoleName, Permissions.Description, and UserTypes.UserType.
Otherwise, you seem to be doing fine.

If your securables are just a products table (or set of tables), and you're using SSMS (SQL-Server Management Studio) then you should not be inventing your own security schema from scratch.
What I would recommend is that you setup your users and roles in SSMS -- expand the Database, then --> Security --> Users, etc. Right-click a user, look for securables, and then you can assign the user to roles, or also, just objects (tables, etc.) directly. Or right-click roles and you have similar options. Whatever you do, stay away from creating your own security schema, if you can help it.
If you need your web app to have access to the database, then look into "utility accounts" (these are like users, created at the server level instead of the database level, but then you can bring them into your database from there.); or look into impersonation if you're able to pass users' creds from your internal network when they login to the database. Utility accounts or users can be assigned to roles, or granted direct access to database objects without roles -- whatever you need.

One thing I've done before, in a similar-sounding scenario:
Keep Roles and users in the same table.
Have an objects table (the tables/queries/forms/etc. you will be granting permissions to)
Have a permission table -- this is where you will link roles/users to objects (i.e., John can SELECT on table 1)
Finally, have an Inheritance table -- this is where you will link roles/users to each other (i.e., John has permission to do whatever Role1 can do)
For example -- a structure like this:
For example:
Users Table:
UserID -- User -- UserTypeID
1 ------- John Smith --- 1
2 ------- Sally Fort --- 1
3 ------- Public Role -- 2
4 ------- Special Role - 2
UserType Table:
UserTypeID -- Description
1 ----------- Human Being
2 ----------- Role
Objects Table:
1 -- Data-Entry Form 1
2 -- Data-Entry Form 2
3 -- Query 1
4 -- Table 1
Permissions Table
UserID -- ObjectID -- Permission
1 -- 1 -- Update (This says John can Update Data-Entry Form 1 (via direct permission))
3 -- 1 -- Update (This says that the Public Role can Update Data-Entry Form 1)
3 -- 2 -- Update (...as well as Data-Entry Form 2)
4 -- 3 -- Select (This says that the special role can Select from Query1)
4 -- 4 -- Insert (...the special role can Insert into Table1)
Permission Inheritance Table
UserID -- UserID_ToInheritFrom
1 -- 3 (this says John can do whatever the Public Role can do)
1 -- 4 (this says John can do whatever the Special Role can do)
2 -- 3 (this says Sally can do whatever the Public Role can do)
So then if you wanted to query, "What can John do?", you'd do something like this:
SELECT
ObjectID,
Permission
FROM
PermissionsTable
WHERE
UserID = 1 -- John has direct permission
OR UserID IN (SELECT UserID_ToInheritFrom FROM PermissionInheritanceTable WHERE UserID = 1)
-- John has indirect permission via the Permission Inheritance Table (or you can call it the "role
-- membership table" if that's easier for you to think of that way.)
This implementation works well. If you want to see a similar implementation, look at SQL-Server's implementation (or better yet, USE it, instead of re-creating the wheel from scratch.)

Related

Database Design Advice for a Social Network App Needed

I am very new into backend stuffs like databases. That being said, I lack the database design knowledge.
I am wondering how and what is the best design for my scenario.
I am creating a social network app where the users can create groups and join other groups. Those groups have places in it. Those places are created by the User in the group
User
Group
Place
Rules:
One User can create and join many Groups
One Group can contain many Users
One Group can have many Places
Each Group have an admin User
I currently have 3 separate tables
1. USER TABLE
ID, EMAIL, USERNAME, PASSWORD, PROFILE PICTURE
2. GROUP TABLE
ID, NAME
3. PLACE TABLE
ID, NAME, COORDINATE, RADIUS
I am extremely confused in designing the proper database for it it.
Question:
How should I design the relation of the table.
I would like to have one User (maybe more) in a Group that has the permission to do certain stuff that normal User cant do. Such as Creating New Place, Deleting A Place
How should I define my table structure?
Any thoughts please? Any help is greatly appreciated!!
Thank you
You will need to have one more table in order to create what is known as a many to many relationship between the users and the groups.
Since you didn't specify the rdbms you are working with, I'll use SQL Server for my code:
CREATE TABLE TblUserToGroup
(
UserToGroup_UserId int FOREIGN KEY REFERENCES TblUser(UserId),
UserToGroup_GroupId int FOREIGN KEY REFERENCES TblGroup(GroupId),
UserToGroup_IsAdmin bit DEFAULT 0
CONSTRAINT UC_UserToGroup UNIQUE(UserToGroup_UserId, UserToGroup_GroupId)
)
As you can see, there is also an IsAdmin column that can take values of 0 or 1. If you only want one admin user for each team, you can add a check constraint to prevent having more then one admin for each group.

One to two (1:2) relation between two tables

I'm working on some asp.net application, I got stuck in following business.
Suppose we have a person, and he can open both types of accounts and each account has some transaction history. Person table has some primary key say PPK, and customer table has some PK as PIN.
My question is how to solve/present this scenario in database, relation of person table to customer table is 1:2 as no person can have more than two account. and what about that transaction table? that holds all transaction history for some specific account? shall I make two transaction table (which is really a bad idea because as account type exceeds transaction tables exceeds).
Can I build their relation as 1:* (then I may need another table for that. it holds Fk of both table. )
or Can make pin as unique key and always open database for like checking limit of accounts (i.e. two).
I really need some suggestions.
All suggestions are welcome.
Thanks!
P.S: If you dont understand the question please ask me before reporting it away please!
You can either do something like this:
Or something like this:
The first model allows adding more accounts by just changing the CHECK (in case requirements change in the future), while the second would require adding another column in such case.
NOTE: The above assumes both account types are structurally identical, so they "fit" into same table. If that's not the case, you can use inheritance.
Ok you have a person table and an account table with a foreign key relationship between the two which is 1 person to many accounts. Then you have a transaction table that is related to the account id in the account table which is also 1 account to many transacations.
Now to enforce the limit of only two accounts being allowed, for that you want a trigger that checks when a record is being inserted or updated to amek sure the person currently has no more than one other record.

Roles and permissions , store permission sets in role table or in multiple rolepermission records?

Which database design would be most efficient for an SaaS product, and why?
Design1: three tables (standard way to design your role permission tables)
ROLE
Id
Name
Description
PERMISSION
Id
Name
ROLEPERMISSION
Id
RoleId
PermissionId
Design2: two tables
ROLE
Id
Name
Description
PermissionSetData
PERMISSION
Id
Name
Where role.permissionsetdata column contains a CVS of permissionsets stored in the role record. (Save database storage space, may help with performance?)
Solution 1 is the only appropriate design.
Saving database storage space is nearly always a moot point here I think. When you can buy TERA bytes of space at a reasonable price, why would you try saving a few kilo bytes? (the space needed for each solution would be approximately the same).
The multi-value column in the second design doesn't follow proper database design.
From a practical point of view, retrieving data in a muli-value column is massively more complex and expensive than in a properly designed db. For instance, you can't index a CSV column properly, you can't join easily.

Users, Customers, Tenants, Employees - All in the same table?

In this case let me be more specific about the problem
I've got a peoples table(with customer & supplier) and I've got a users table(for users who can login).
Currently I have this DB structure
Customers -> Organisations -> linked through rel_customer_addresses to address table.
(as 1 customer could have delivery_address, invoice address etc.)
Users -> Tenants -> linked through rel_users_addresses to the address table
(as 1 user could have delivery_address, invoice address etc.)
Now I've got in the invoice table customer_key. The problem is when the user himself is the customer and the invoice is from one of his customers. How do I indicate my web app to look up user and not the customer?
Since you're looking at 2 separate entities (customers and users), I would go ahead and use 2 separate tables and have them share a unique identifier (i.e., username, SID).
That way there's no chance of one seeing information from the other without the appropriate permissions.
There are several ways to control this, but the logic is something like this.
If userID exists in table user, do this.
If userID exists in table customer, do this.
If userID exists in table user AND table customer, do this.
That way you can control the situation completely either independently or together. In other words, you could grant special permissions to userID that is found in table customer, or just make it completely separate (similar to say, how facebook makes a separate 'identity' for pages vs the account it's registered to).
Hope that helps!

How to model this one-to-one relation?

I have several entities which respresent different types of users who need to be able to log in to a particular system. Additionally, they have different types of information associated with them.
For example: a "general user", which has an e-mail address and "admin user", which has a workstation number (note that this a hypothetical case). Both entities also share common properties like first name, surname, address and telephone number. Finally, they naturally need to have a (unique) user name and a password to log in.
In the application, the user just has to fill in his user name and password, and the functionality of the application changes slightly according to the type of the user. You can imagine that the username needs to be unique for this work.
How should I model this effectively?
I can't just create two tables, because then I can't force a unique constaint on the user name.
I also can't put them all in just one table, because they have different types of specific information associated to them.
I think I might need 3 seperate tables, one for "users" (with user name and password), one for the "general users" and another one for the "admin users", but how would the relations between these work? Or is there another solution?
(By the way, the target DBMS is MySQL, so I don't think generalization is supported in the database system itself).
Your 3 tables approach seems Ok.
In users table have only ID, username, password,usertype.
In general users table have ID, UserID (from users table), other fields.
Same thing for admin users.
Usertype field will tell you from what table to search for additional info
if(usertype==admin)
select * from admins where userid=:id;
else
select * from general where userid=:id;
Two tables. USERS with user names, first, last, etc. ROLES with roles, and a link back to the user name (or user id or whatever). Put a unique constraint on the user name. Put workstation nbr, email, phone, whatever else you need, in the user table. Put 2 columns in the ROLES table -- USERID and ROLE.
You should decide how much specific information is being stored (or likely to be stored in the future) and make the decision based on that. If there are only a handful of fields for each user type then using a single table is alright.
USERS table (name, type, email, password, genfield1, genfield2, adminfield1, adminfield2)
Make sure to include the type (don't assume because some of the fields particular to that user are filled in that the user is of that type) field. Any queries will just need to include the "AND usertype = " clause.
If there are many fields or rules associated with each type then your idea of three tables is the best.
USERS table (ID, type, name, password)
GENUSERS (ID, genfield1, genfield2)
ADMINUSERS(ID, adminfield1, adminfield2)
The constraints between IDs on the table are all you need (and the main USERS table keeps the IDs unique). Works very well in most situations but reports that include both types of users with their specific fields have to be done in two parts (unioned SQL or subqueries or multiple left joins).
You can solve it with one 'general' users table containing the information thats available for all users and 1 table for every specific user type. In your example you will then need 3 tables.
Users: This table holds only information shared between all usertypes, ie. UserId, Name, Address, etc.
GeneralUsers: This table 'extends' the Users table by providing a foreing key UserId that references the Users table. In addition, information specific to general users are held here, fx. EmailAddress, etc.
AdminUsers: As with GeneralUsers, this table also 'extends' the Users table by providing a foreign key UserId referencing the Users table. In addition information specific to admin users are held here, fx. WorkstationId, etc.
With this approach you can add additional 'specializations' if the need arises by simply adding new tables that 'extends' the Users table using a foreign key reference. You can also create several levels of specialization. If for example admin users are general users as well as admin users then AdminUsers could 'extend' GeneralUsers instead of Users simply by using a foreing key to GeneralUsers instead of Users.
When you need to retreive data from this model you need to which type of user to query. If for example you need to query a GeneralUser you will need something similar to:
SELECT * FROM GeneralUsers
LEFT JOIN Users ON GeneralUsers.UserId = Users.UserId
Or if querying an admin user
SELECT * FROM AdminUsers
LEFT JOIN Users ON AdminUsers.UserId = Users.UserId
If you have additional levels of specialization, for example by having admin users also being general users you just join your way back.
SELECT * FROM AdminUsers
LEFT JOIN GeneralUsers ON AdminUsers.UserId = GeneralUsers.UserId
LEFT JOIN Users ON GeneralUsers.UsersId = Users.UserId
I most definitely would not do a model where you have separate tables as in GeneralUser, AdminUser and ReadOnlyUser.
In database design, a good rule of thumb is "Down beats across". Instead of multiple tables (one for each type), I would create a SystemUsers table, and a Roles table and define a join table to put SystemUsers in Roles. Also, I would define individual roles.
This way, a user can be added to and removed from multiple roles.
A role can have multiple permissions, which can be modified at any time.
Joins to other places do not need a GeneralUserId, AdminUserId and ReadOnlyUserId column - just a SystemUserId column.
This is very similar to the ASP.Net role based security model.
alt text http://img52.imageshack.us/img52/2861/rolebasedsecurity.jpg

Resources