I am currently designing an ERD for a project management system. I am unable to choose between two workable solutions as the best solution as I wish to embrace the best standards.
The two entities that can access the system are the clients and employees. Since they have different information, I decided to treat them as different entities. I figured that it is best to have a single table for users of the system rather having the username and password fields be found in both tables of clients and employees. Plus, the constraints I have to follow:
- All users are only limited to employees and clients.
- But not all employees are required to be users of the system.
Here are the following entities without relationship yet defined:
Now, there is only one-to-one relationship between users and clients, employees and users. My question is where should I place the foreign key? Should I place two foreign keys in users table namely employee_id and client_id which makes it easier to find the associated client and employee entity from the user entity and where I can define authorization rules more easily? Or should I place each foreign key on clients and employees referencing the users table since I am unsure if it is an acceptable practice to have two foreign keys in a table in which only one foreign key can be used at a time.
Best Answer
First of all, let me just say (as others have pointed out), that there is nothing wrong with having mutually-exclusive foreign keys!
Second, despite it's elegance and applicability to other scenarios, I don't think the superset solution posted by @Joel Brown is appropriate for your exact case. Although it makes it much easier to deal with sub-types of 'people' (entities, users, what-have-you), it doesn't sound like your project will ever expand beyond those two groups.
As I see it, there are three techniques you can employ here:
users
, or an FK in each of theClients
andemployees
tables.Role
anduser_role
table, to employ user roles from theusers
table (and then demoting 'client' and 'employee' to roles).Each of these options (as you might imagine) has it's own strengths and weaknesses.
Supersets:
Pros
Cons
Current Design
Pros
Clients
andemployees
) to have as many different fields as it needs.user_type
that is simultaneouslyClient
anduser
.Cons
Clients
andemployees
, but 20+ types - queries on this would get very slow (as each type needs a new FK in users).user
is easily (assuming you stay with FK inusers
, PK would inverse this).User Roles
Pros
Cons
So what's the answer?
Well, Roles allow massive amounts of scalability, but this comes at the cost of modelable complexity. Due to their simplicity, you can have a thousand roles, but each role will tell you very little (since a role cannot have fields of it's own). Therefore, I would say roles are inappropriate for your project, as you need to model different things for employees versus users.
Sub-types (a.k.a. superset) might work for your project. However, the added difficulty in querying to determine a users type probably isn't worth the benefit for a system that will only ever have two types. Additionally, the case of an employee also being a customer would probably never happen. It would be pretty safe to assume someone working internally and for a customer (if such a thing were even permitted by all involved), would have a separate email/logon/identity/username for each of their roles (positions) and hence would just login on the appropriate account.
Your current design (with the double FK in
users
) both permits the modelling of the fields you need for each different type, and allows you to have the (desirable?) mutual exclusivity to prevent a user from being both a customer and an employee. It does however maintain the issue that it is difficult to know (from just ausers
id
) which table to query for that user (i.e. is the user a customer or employee).To solve that problem (if you particularly care), I would suggest adding a field to
users
to signify the type. This could literally be something likeuser_kind
, with anenum(client,employee)
type. If you want to get even fancier, you could extract both that field anduser_type
to a fourth table, which has an FK back tousers
and the same dual-FK (onuser_type
) toClients
andemployees
.