I am not sure what the best route to go on this is. I have a client who has 3 different locations for his business. Each locations employees can only access their locations data. The owner can access all... Then, different roles should be able to access their stuff only (finance can see finance but not sales, etc..).
What is the best way to go about this? The solutions I can think of are:
Create a user table, give a location ID and role ID and base the data off of that. This would require adding the location ID a lot though..
Create 3 separate databases and have the information display based off of a role ID. This doesn't seem ideal
Use functionality on the DB side, stored procedures, etc...
Retrofitting a multi-tenancy security model into an existing database isn't a simple task - IMO this should be designed into the model from the start.
An extremely simple model (One Role per user, One Location per User) would look like this:
-- You need to add simple lookup tables for Role, Location
CREATE TABLE User
(
UserId INT, -- PK
RoleId INT, -- FK
LocationId INT NULL -- FK
);
All sensitive tables would either directly need the LocationId classification, or need to be joinable to a table which has the LocationId classification, i.e.:
CREATE TABLE SomeTable -- with location-sensitive data
(
Col1 ... Col N,
LocationId INT
);
The hard part however is to adjust all of your system's queries on the sensitive data tables such that they now enforce the Location-specific restriction. This is commonly done as an additional predicate filter which is appended to the where clause of queries done on these tables, and then joining back to the user-location table:
SELECT Col1 ... ColN
FROM SomeTable
INNER JOIN User on SomeTable.LocationId = User.LocationId
WHERE -- Usual Filter Criteria
AND ((User.UserId = #UserIdExecutingThisQuery
AND User.RoleId = `Finance`) -- Well, the Id for Finance
OR User.RoleId = `Administrator`) -- Well, the Id for Admin
As a result of the redesign effort, as a short term solution, you might look at at instead maintaining 3 distinct regional databases (or 3 regional schemas in the same database), and then using replication or similar to then centralize all data to a master database for the owner role to use.
This will give you the time to redesign your database (and app(s)) to use a multi-tenancy design. I would suggest a more comprehensive model of allowing multiple roles per user, and multiple locations per user (i.e. many-many junction tables), and not the simplistic model shown here.
Related
Question about best db design practices for the following scenario:
I have a user table which stores information like password, the department user belongs to, Email e.t.c. I also have to create another table to store user access information. I have users with access as following:
userid 123 : [line of business: abc, xyz,e.t.c.], [Role: Team leader, Regular User e.t.c.], [Company: 1234, 54666, e.t.c.]
userid 345: [line of business: abc, xyz, 987, e.t.c.], [Role: Team leader, Maintenance e.t.c.], [Company: 1234, 54666, e.t.c.]
Basically, a user can have any combinations of different access for each of these categories.
The way i was thinking is in this table, create 3 columns for each type of access and store each category in the form of xml, otherwise i have to create 3 different tables for each categories, t_user_Lob, t_user_role, t_user_company.
what is the best way to do this?
I also thought about storing them as csv values in each column but i heard that it's goes against the relational normalization rules.
I also like json way to storing data but i am using sql server 2012 and it's hell to parse/compare values in json columns in 2012.
Thanks
I would store them in three separate tables.
Your question isn't clear about what relationships are needed, but basically you use three tables to create a many-to-many relationship.
Entity A
Entity B
Relationship (A - B)
Assuming the combinatorics aren't outlandish, I would create a table, call it user_access, with every combination of role and company and give each row a unique value. Then create a third table, call it user_access_people with a user ID and an associated user_access value. So user_access_people would have your many to many relationship between user_id and user_access_Id. It would have multiple rows per each user_ID when necessary.
You can then join these tables to check for whatever value from the user_access columns that you are looking for.
I've inherited a project that has a table called Users that stores a unique ID for each user that has permissions to use an application. That Table has the User_ID. This table also has a column called Mgr_User_ID which tracks who the individual reports to. I'd like to make the User_ID a foreign key of Column Mgr_user_ID so that I can access the managers information easily in the MVC project I'm working on that uses this table.
My question is basically, is this considered poor practice and are there any risks associated with doing something like this?
Self-referencing tables are a pretty common practice. It's how you model any hierarchical relationship.
The biggest "risk", which isn't really a risk, is that if you're trying to walk the entire hierarchy (e.g. you want to create an organizational chart) you will need to write a recursive method (which can sometimes be a brain-twister if you're not used to it).
Also, decide in advance how you're going to identify the top of the hierarchy (i.e. the manager who doesn't report to anyone). In some systems the Mgr_User_ID is set to null and in others they decide to make it the same as the User_ID. I've worked with both approaches and there isn't really an advantage of one over the other, just make sure everyone working in the data understands the rule, and if you have any other relationships like this use the same approach for all of them.
This is commonly called "self-join" of tables. It is really common for an employee table or an organization table. Whenever there is a hierarchy within in the rows of a table(worker -> manager, field office -> head quarter), self join can be used.
This is not a bad practice. In fact, breaking an employee table to a worker table and a manager table would be worse practice. As that would complicate things by managing essentially same objects in two tables.
However, querying a table using self join can be unintuitive and makes for a lengthy code. You can make things easier by using views, though. If you have an employee table that has Mgr_User_ID. Maybe you can write a query like this and make it a view. If you just want worker's name and manager's name in a single table:
CREATE VIEW dbo.DIMUser
AS
SELECT Users.UserId
, Users.UserName
, MUsers.UserId AS ManagerId
, MUsers.UserName AS ManagerName
FROM dbo.Users AS Users
LEFT JOIN dbo.Users AS MUsers
ON Users.Mgr_User_ID = MUsers.Id
I'm starting a project and unsure of the proper way to do this so I just need to be pointed in a general direction.
Essentially, each user stored in the database will have a large list of people (millions) that they want to connect with, and a large list of people they have connected with. The lists of people to connect with will be updated weekly, possibly monthly and duplicates will need to be checked for.
*It might be important to note that the lists of people to connect with won't be users in the system.
Should each list for each user be stored in separate tables and linked to or is there a more efficient structure for this?
Thanks!
One way would be
users table
-----------
user_id
user_name
...
lists table
-----------
list_id
user_id
list_name
...
list_persons table
----------------
list_id
person_name
...
I am starting a new Project for a website based on "Talents" - for example:
Models
Actors
Singers
Dancers
Musicians
The way I propose to do this is that each of these talents will have its own table and include a user_id field to map the record to a specific user.
Any user who signs up on the website can create a profile for one or more of these talents. A talent can have sub-talents, for example an actor can be a tv actor or a theatre actor or a voiceover actor.
So for example I have User A - he is a Model (Catwalk Model) and an Actor (TV actor, Theatre actor, Voiceover actor).
My questions are:
Do I need to create separate tables to store sub-talents of this user?
How should I perform the lookups of the top-level talents for this user? I.e. in the user table should there be fields for the ID of each talent? Or should I perform a lookup in each top-level talent table to see if that user_id exists in there?
Anything else I should be aware of?
before answering your questions... i think that user_id should not be in the Talents table... the main idea here is that "for 1 talent you have many users, and for one user you have multiple talent".. so the relation should be NxN, you'll need an intermediary table
see: many to many
now
Do I need to create seperate tables to store sub-talents of this
user?
if you want to do something dynamic (add or remove subtalents) you can use a recursive relationship. That is a table that is related to itself
TABLE TALENT
-------------
id PK
label
parent_id PK FK (a foreign key to table Talent)
see : recursive associations
How should I perform the lookups of the top-level talents for this
user? I.e. in the user table should
there be fields for the ID of each
talent? Or should I perform a lookup
in each top-level talent table to see
if that user_id exists in there?
if you're using the model before, it could be a nightmare to make queries, because your table Talents is now a TREE that can contain multiple levels.. you might want to restrict yourself to a certain number of levels that you want in your Talent's table i guess two is enough.. that way your queries will be easier
Anything else I should be aware of?
when using recursive relations... the foreign key should alow nulls because the top levels talents wont have a parent_id...
Good luck! :)
EDIT: ok.. i've created the model.. to explain it better
Edit Second model (in the shape of a Christmas tree =D ) Note that the relation between Model & Talent and Actor & Talent is a 1x1 relation, there are different ways to do that (the same link on the comments)
to find if user has talents.. join the three tables on the query =)
hope this helps
You should have one table that has everything about the user (name, dob, any other information about the user). You should have one table that has everything about talents (id, talentName, TopLevelTalentID (to store the "sub" talents put a reference to the "Parent" talent)). You should have a third table for the many to many relationship between users and talents: UserTalents which stores the UserID and the TalentID.
Here's an article that explains how to get to 3rd NF:
http://www.deeptraining.com/litwin/dbdesign/FundamentalsOfRelationalDatabaseDesign.aspx
This is a good question to show some of the differences and similarities between object oriented thinking and relational modelling.
First of all there are no strict rules regarding creating the tables, it depends on the problem space you are trying to model (however, having a field for each of the tables is not necessary at all and constitutes a design fault - mainly because it is inflexible and hard to query).
For example perfectly acceptable design in this case is to have tables
Names (Name, Email, Bio)
Talents (TalentType references TalentTypes, Email references Names)
TalentTypes (TalentType, Description, Parent references TalentTypes)
The above design would allow you to have hierarchical TalentTypes and also to keep track which names have which talents, you would have a single table from which you could get all names (to avoid registering duplicates), you have a single table from which you could get a list of talents and you can add new talent types and/or subtypes easily.
If you really need to store some special fileds on each of the talent types you can still add these as tables that reference general talents table.
As an illustration
Models (Email references Talents, ModelingSalary) -- with a check constraint that talents contain a record with modelling talent type
Do notice that this is only an illustration, it might be sensible to have Salary in the Talents table and not to have tables for specific talents.
If you do end up with tables for specific talents in a sense you can look at Talents table as sort of a class from which a particular talent or sub-talent inherits properties.
ok sorry for the incorrect answer.. this is a different approach.
The way i see it, a user can have multiple occupations (Actor, Model, Musician, etc.) Usually what i do is think in objects first then translate it into tables. In P.O.O. you'd have a class User and subclasses Actor, Model, etc. each one of them could also have subclasses like TvActor, VoiceOverActor... in a DB you'd have a table for each talent and subtalent, all of them share the same primary key (the id of the user) so if the user 4 is and Actor and a Model, you would have one registry on the Actor's Table and another on the Model Table, both with id=4
As you can see, storing is easy.. the complicated part is to retrieve the info. That's because databases dont have the notion of inheritance (i think mysql has but i haven't tried it).. so if you want to now the subclases of the user 4, i see three options:
multiple SELECTs for each talent and subtalent table that you have, asking if their id is 4.
SELECT * FROM Actor WHERE id=4;SELECT * FROM TvActor WHERE id=4;
Make a big query joining all talent and subtalent table on a left join
SELECT * from User LEFT JOIN Actor ON User.id=Actor.id LEFT JOIN TvActor ON User.id=TvActor.id LEFT JOIN... WHERE User.id=4;
create a Talents table in a NxN relation with User to store a reference of each talent and subtalents that the User has, so you wont have to ask all of the tables. You'd have to make a query on the Talents table to find out what tables you'll need to ask on a second query.
Each one of these three options have their pros and cons.. maybe there's another one =)
Good Luck
PS: ahh i found another option here or maybe it's just the second option improved
I am having a tough time with this design problem and would appreciate any insight.
I have a doctors office that is provided certain privileges currently there are only 5 privileges but more could be added. Each of these privileges has a status of Yes or No, but there could be a finer grained status in the future and each of these privileges are related to a location(er,inpatient, outpatient) and they too could expand in future.
So currently I have the tables OfficePrivileges, PrivilegeLocation, PrivilegeType, PrivilegeStatus.
OfficePrivilege is a Joining table between PrivilegeLocation and DoctorOffice. It has a double primary key of OfficeID and PrivilegeLocationID.
At one time I had Type and status joined to the OfficePrivileges Table then switched to have the type table be a child of Location and status be a child of type. They are all single primary key tables.
if you were designing this set of tables how would you do it? I am thinking that this is almost a hierarchy problem..and I hate them. I would like to lay out the edit screen as crosstab table having Location across the top, Type down the side and details being the statuses. That is how it is currently in the system I am trying to integrate to, but its a Cobol backend and handles hierarchies better than relational db....
EDIT:To Help clear up confusion:
For my example there are Admit Privileges, Attending Privileges,Consulting privileges and Surgery Privilges. And the locations are Inpatient, ER, OutPatient, and OP/Surgery. Currently the statuses are only Yes or NO. But they could change in the future depending on client need.
This information is stored in tables in my database.
I think the big thing here it to try to be as flexible as possible, since you're aware of "possible" changes, but you don't really want to code to those, yet.
Something like what you've got is really probably pretty good: I'd go this way: you'll actually have a 3-way relationship between Offices, PrivilegeLocations, and PrivilegeTypes
I would say your OfficePrivaleges table should have the following 6 columns: Id (its own PK), OfficeId, LocationId, PTypeId, StartDate, EndDate When a new Privilege is granted for an Office, you'll add a record to this table linking the three and adding the first date the office has the privilege in StartDate. If the Privilege is revoked, add that date to EndDate. If it's renabled, either add a new row, or reset the StartDate.
I would avoid making PrivilegeType a child of PrivilegeLocation, because then you either have to store every location n times (n = different privilege combinations). This way, you're only storing Offices, Locations, and Types once each.
So, for instance, Doctor A could have Inpatient Privileges at Location X, but only DX Imaging privileges at Location Y, while Doctor B could have Inpatient privileges at both Locations X and Y.