Column level role access in SQL Server 2014 using Entity Framework - sql-server

So a friend of mine is on a new project. The customer wants to use SQL Server roles (admin, poweruser, superior, general, guest) and wants to have restriction on which columns to a table can be pulled back when the user has this role.
Imagine a table below
ID Make Model Type ProdCost ROI Frequency RecallDate
-------------------------------------------------------------------
1 70 This 1 $12 2 2
2 71 That 2 $12 3 2
3 72 Sparrow 3 $12 2 3
4 72 Duck 4 $12 2 N/a
5 76 Fellon 5 $12 4
Admin role can retrieve all columns
Poweruser can retrieve all columns except ID
Guest can get ID MAKE Model columns
General gets ID, ProdCost, ROI, Frequency, RecallDate
How do we make this work with Entity Framework? Ideas? The client wants to use views but it all seems messy

Related

Create a view with multiple foreign key referencing a single field

How can I create a view using a table which has multiple foreign key referencing the same table and a single field. I have product table and Reference table I have around 5 foreign key in product table referencing to the RefCodeKey Field in reference table. How can I create a view which shows product reference Code joining product and Reference Code
I have a product table as follows
PK PTK PC PN RCKey PSKey PCKey PCAKey
1 1 500000 Prod A 5 12 14 98
2 1 500001 Prod B 5 12 14 98
3 1 500002 Prod C 5 11 13 145
4 4 500002 Prod C 10 11 13 76
5 3 500002 Prod C 10 11 13 95
6 1 500005 Prod D 5 12 14 137
I have Reference Code Table as follows
RefCodeKey RefCodeType Code Label Status
1 ParentTypeKey assembly assembly Active
2 ParentTypeKey WHL WHL Active
3 ParentTypeKey TIRE TIRE Active
4 ParentTypeKey TIRE TIRE Active
5 RegionCodeKey 1 COMP 1 Active
6 RegionCodeKey 2 COMP 2 Active
7 RegionCodeKey 3 COMP 3 Active
8 RegionCodeKey 4 COMP 4 Active
9 RegionCodeKey 9 COMP 5 Active
10 RegionCodeKey 0 COMP 6 Active
11 ProductStatusKey CLOSED CLOSED Active
12 ProductStatusKey ACTIVE ACTIVE Active
13 ProductClassificationKey DropShip DropShipActive
14 ProductClassificationKey INFO NA INFO NA Active
How can i create a view display a result as show below?
PC PN RCKey PSKey PCKey
500000 Prod A COMP 1 ACTIVE INFO NA
500001 Prod B COMP 1 ACTIVE INFO NA
500002 Prod C COMP 1 CLOSED DropShip
500002 Prod C COMP 6 CLOSED DropShip
500002 Prod C COMP 6 CLOSED DropShip
500005 Prod D COMP 1 ACTIVE INFO NA
This is a common reporting pattern wherever the database architect has employed the "one true lookup table" model. I'm not going to get bogged down in the merits of that design. People like Celko and Phil Factor are far more erudite than me at commenting on these things. All I'll say is that having reported off over sixty enterprise databases in the last 15 years, that design is pervasive. Rightly or wrongly, you're probably going to see it over and over again.
There is currently insufficient information to definitively answer your question. The answer below makes assumptions on what I think is the most likely missing information is.
I'll assume your product table is named PRODUCT
I'll assume your all-powerful lookup table is call REFS
I'll assume RefCodeKey in REFS has a unique constraint on it, or it is the a primary key
I'll assume the REFS table is relatively small (say < 100,000 rows). I'll come back to this point later.
I'll assume that the foreign keys in the PRODUCT table are nullable. This affects whether we INNER JOIN or LEFT JOIN.
SELECT prod.PC
,prod.PN
,reg_code.label as RCKey
,prod_stat.label as PSKey
,prod_clas.label as PCKey
FROM PRODUCT prod
LEFT JOIN REFS reg_code ON prod.RCKey = reg_code.RefCodeKey
LEFT JOIN REFS prod_stat ON prod.PSKey = prod_stat.RefCodeKey
LEFT JOIN REFS prod_clas ON prod.PCKey = prod_clas.RefCodeKey
;
The trick is that you can refer to the REFS table as many times as you like. You just need to give it a different alias and join it to the relevant FK each time. For example reg_code is an alias. Give your aliases meaningful names to keep your code readable.
Note: Those RCKey/PSKey/PCKey names are really not good names. They'll come back to bite you. They don't represent the key. They represent a description of the thing in question. If it's a region code, call it region_code
The reason I'm assuming the REFS table is relatively small, is that if it's really large (I've seen one with 6 million lookup values across hundreds of codesets) and indexed to take RefCodeType into consideration, you might get better performance by adding a filter for RefCodeType to each of your LEFT JOINs. For example:
LEFT JOIN REFS prod_clas ON prod.PCKey = prod_clas.RefCodeKey
AND prod_clas.RefCodeType = 'ProductClassificationKey'

Redundant info in new table?

A TASK is linked to a TASK_ROLE that associates parties + roles to a specific TASK. I need another table, TASK_RATE, that stores the rate of every party that participoates in a specific TASK. I could get those associations with a FK to TASK_ROLE, but the problem is that a party can have more than one role and can be listed more than once.
TASK:
PKIDTASK
1
2
3
TASK_ROLE:
PKIDTASK_ROLE | IDTASK | IDPARTY | IDROLE
1 1 8 3
2 1 8 2
3 1 5 2
4 1 4 2
I tought about creating the table like this
TASK_RATE:
PKIDTASK_RATE | IDTASK | IDPARTY
1 1 8
2 1 5
3 1 4
IDTASK + IDPARTY are unique, unlike in TASK_ROLE. But wouldn't be this redundant info since PARTY-TASK associations are already defined in TASK_ROLE?
How do I solve this?
If you want to fully normalize your data model, you probably need something like this:
Since parties are assigned to tasks, and these assignments have various other related data, you should start by intersecting the many-to-many between PARTY and TASK.
Other things that you are tracking are offshoots of this intersection. For example, each party is assigned to one (or more?) roles out of the available party task assignments.
Similarly, For a given party task assignment there will be multiple rates (over time). The Party Task Rate table will need an effective and expiry date (+time, if appropriate).

how to see a difference between entity and a column

Sometimes I am having a hard time seeing a difference between an entity and a column when I am starting to make a diagram. I don't know when it is supposed to be a entity or a column. For example, in some game if you have a user and that user can play by itself or it can play in the group. Would you make that two different entities User and GroupUser ?
Also, for example if the User has levels, status and badges they earn which is part of the game. Would these be entities also or they would just be in one entity which would be part of the User ?
Entity could be a Person (e.g. Student), Place (e.g. Room Name), Object (e.g. Books), Abstract Concept (e.g. Course, Order) that could be represented in your database and normally could become a Table in your Database.
Column(s) on the other hand is/are the attribute(s) of your Entity.
So, in your case you have a User entity and the possible columns or attributes (or fields) are
UserID, UserLevel, UserStatus, Badges, PlayStatus (values could be individual or group).
Your Badges although is a column could turn into Entity if it violates the Normalization rules.
For example if you have this Table for User:
Table: Users
UserID UserName UserStatus PlayStatus Badges
------ -------- ---------- ---------- ------
1 Surefire Active Single Private, Warrior, Platoon Leader
2 FastMachine Active Group Private, Warrior
3 BeatTheGeek Inactive Group Private
The Badges here violates the 1NF (1st Normal Form) in Normalization rules which says that there should be no repeating groups or in this case no Multi-valued columns. So, this could be normalized like:
Table: Users
UserID UserName UserStatus PlayStatus
------ -------- ---------- ----------
1 Surefire Active Single
2 FastMachine Active Group
3 BeatTheGeek Inactive Group
Table: Badges
BadgeID BadgeName
------ --------
1 Private
2 Indie
3 Warrior
4 Platoon Leader
5 Colonel
6 1 Star General
7 2 Star General
8 3 Star General
9 4 Star General
10 5 Star General
11 Hero
Table: UserBadgesHistory
UserID BadgeID ReceiveDate
------ -------- -----------
1 1 12/01/2013
1 3 12/05/2013
1 4 1/5/2014
2 1 2/5/2014
2 3 2/10/2014
3 2 11/10/2013
In general, an entity has multiple columns (i.e. attributes) of its own, and a column (or attribute) does not.
In your example, if the only data you're interested in storing is a User's current level, then level is unlikely to be an entity. This is because it would have only a single attribute of name/number. If you wanted to find all Users currently at level 4, you would simply do a query with level = 4.
On the other hand, if you had a reason to add additional data about the level, such as what abilities are associated with that level or the date a given User achieved the level, then you would want to make Level a separate entity.
A Level entity would have an ID, a number or name, and whatever other attributes you need as data.
ID | Prerequisite | Ability
----+--------------+--------------
1 | NULL | May gain foos
2 | Gain 10 foos | May gain bars
3 | Gain 20 bars | 30 free foos
In a fully normalized state, you would have another entity called UserLevel in which you would store data about, for example, when a certain User gained a level.
The UserLevel entity would contain the LevelID and the UserID as foreign keys (links back to the other entities), and a DateAchieved column for when the User achieved the level.
LevelID | UserID | DateAchieved
---------+--------+-------------
1 | 1 | 2014-02-01
1 | 2 | 2014-02-01
2 | 1 | 2014-02-05
3 | 1 | 2014-02-09
2 | 2 | 2014-02-11
4 | 1 | 2014-02-13
This shows User 1 and User 2 starting at Level 1 on the same day and leveling up at different rates.

Query a Lookup Table in Excel or Access

I've got a mental block about what I'm sure is a common scenario:
I have some data in a csv file that I need to do some very basic reporting from.
The data is essentially a table with Resources as column headings and People as row headings, the rest of the table consists of Y/N flag, "Y" if the person has access to the resource, "N" if they don't. Both the resources and the people have unique names.
Sample data:
Res1 Res2 Res3
Bob Y Y N
Tom N N N
Jim Y N Y
The table is too large to simply view it as whole in Excel(say 300 resources and 600 people), so I need a way to easily query and display (A simple list would be ok) what resources a person has access to, given the person's name.
The person that will need to use this has MS Office, and not much else on their PC.
So, the question is: What is the best way to manipulate this data to get the report I need? My gut says MS Access would be the best, but I can't figure out to automatically import data like this into a normal relational database. If not Access, perhaps there are some functions in Excel that could help me out?
You should normalize your data. This will make it easier to query against. For example:
table users:
UserID UserName
1 Bob
2 Tim
3 Jim
table resources:
ResourceID ResourceDesc
1 Printer #1
2 Fax Machine
3 Bowling Ball Wax
table users_resources:
LinkID UserID ResourceID
1 1 1
2 1 2
3 3 1
4 3 3
SELECT ResourceID
FROM users_resources, users
WHERE users.UserName="Bob"

db optimization: computing rank

This question asks how to select a user's rank by his id.
id name points
1 john 4635
3 tom 7364
4 bob 234
6 harry 9857
The accepted answer is
SELECT uo.*,
(
SELECT COUNT(*)
FROM users ui
WHERE (ui.points, ui.id) >= (uo.points, uo.id)
) AS rank
FROM users uo
WHERE id = #id
which makes sense. I'd like to understand what the performance tradeoffs would be, between this approach, or by modifying the db structure to store a calculated rank (I guess that would require massive changes every time there's a rank change), or any other approaches that I'm too newb to think of. I'm a db noob.
The performance tradeoff would basically be what you described:
If you modified the structure to store a rank, queries would be very, very simple and fast. However, this would require some overhead any time "points" changed, as you'd have to verify that the rank hasn't changed. If the ranking had changed, you'd have to do multiple updates.
This causes more work (with the potential for bugs) at every update/insert. The tradeoff is very fast reads. If you're typical usage is very few modifications compared to millions of reads, AND you found this query to be a bottleneck, it might be worth considering reworking this. However, I would avoid the added complexity and maintainability headaches unless you truly found this to be a problem, since the current solution requires less storage, and is very flexible.
The link you reference is a MySQL question. If the original database had been Oracle the accepted answer would be to use an analytic function, which does scale, very nicely:
SQL> select id, name, points from users order by id
2 /
ID NAME POINTS
---------- ---------- ----------
1 john 4635
3 tom 7364
4 bob 234
6 harry 9857
8 algernon 1
9 sebastian 234
10 charles 888
7 rows selected.
SQL> select name, id, points, rank() over (order by points)
2 from users
3 /
NAME ID POINTS RANK()OVER(ORDERBYPOINTS)
---------- ---------- ---------- -------------------------
algernon 8 1 1
bob 4 234 2
sebastian 9 234 2
charles 10 888 4
john 1 4635 5
tom 3 7364 6
harry 6 9857 7
7 rows selected.
SQL> select name, id, points, dense_rank() over (order by points desc)
2 from users
3 /
NAME ID POINTS DENSE_RANK()OVER(ORDERBYPOINTSDESC)
---------- ---------- ---------- -----------------------------------
harry 6 9857 1
tom 3 7364 2
john 1 4635 3
charles 10 888 4
bob 4 234 5
sebastian 9 234 5
algernon 8 1 6
7 rows selected.
SQL>
Does not the 'where' portion of that query internally require reading the entire table? I understand about premature optimization. Academically, it seems that this wouldn't scale further than a few thousand rows.

Resources