I am trying to design an efficient database schema for user settings in SQL Server 2008 R2. The wrinkle here is that we need multiple levels of granularity, and I'm not sure how to efficiently represent that.
We have a handful of settings that can be applied to a full Account, a single Module, or a specific Feature. Currently the way the table has been set up is something to the effect of:
AccountId int
ModuleId int
FeatureId int
SettingData string
(please don't get hung up on what SettingData is or isn't, I just made it a string here in the example to distinguish it from the other Ids).
Problem: Many customers have access to many modules, and these modules have access to many features. A single Account making a change to SettingData can modify 4000 records. This is absolutely not tenable for obvious reasons, and I'm determined to fix it.
The solution is obviously to have a few different tables that, by their usage, override eachother and allow some account wide settings and granular preferences. However, I've never done this before and my attempts at designing it end up looking disturbingly similar to the inefficient table structure we currently have.
Thanks in advance, any help is appreciated.
It sounds as though settings can currently be specified at the following levels:
Account
Module
Feature
Given that there are probably already tables set up for each of Account, Module and Feature, it would appear to make sense to:
Remove the existing table.
Set up a new field for setting data on each of the existing Account, Module and Feature tables.
Since the general principle is that the specific should override the general, a Module-level setting should override an Account-level setting, and a Feature-level setting should override a Module-level setting.
The advantage of this approach is that any time a specific setting was updated, only a single record would need to be updated.
The disadvantage is that to determine which setting should apply to a specific feature (for a specific account) in a specific module, 3 tables would have to be queried instead of one.
Related
A PDF about Maximo formulas mentions MAXVARS:
Maximo Formulas are the logical next step in Maximo customization
after Maximo Scripting. Maximo formulas follow Excel-like grammar to
define expressions that use input from variables to calculate a value.
Unlike scripting, where most of the variables need to get predefined
and bound to some Maximo attributes/properties/MAXVARS, the formula
expression can use any of those Maximo attributes/properties/MAXVARS
inside the expression without ever needing to predefine or bind them.
I assume that MAXVARS are some sort of global variable.
But when I search the docs, I don't see anything that explains them in detail.
What are MAXVARS and how are they used?
Generally, they are system level configuration elements. They are used in special cases in the code to determine how the system should behave. It contains things like whether Admin Mode is on for the system, or whether to automatically close completed POs when an invoice comes in, or what status to put a work order in when assignments are completed. It's nature is really just a generic key-value pairing table at the ORG level, so it can be used for any kind of system variable one might want to store, though generally there isn't much of a use-case for it in customizations.
MaxVars Variables
As others have mentioned previously, MAXVARS has its origins in early versions of Maximo (e.g. 3.x, 4.x) prior to it becoming a Java application and prior to multitenancy so initially all MAXVARS values applied at the System level as there were no Organisations and Sites in the system. I don't recall specifically which version introduced MaxVars entries with ORG and SITE scope in addition to SYSTEM but those are available in Maximo 7.6.x.
The following explains how to query the maxvartype database table for an explanation of what each of the entries in the maxvars table does:
https://www.ibm.com/support/pages/checking-purpose-maxvars-variables
https://developer.ibm.com/static/site-id/155/maximodev/7609/maximocore/businessobjects/psdi/app/system/MaxVars.html
Usage
One example is the MaxVars values that are used for Inventory. Each Organisation in the system has 6 MaxVars entries:
A_BREAKPOINT : 0.8
B_BREAKPOINT : 0.15
C_BREAKPOINT : 0.05
A_CCF : 30
B_CCF : 60
C_CCF : 90
The first 3 values dictate what percentage of type A, B and C inventory items make up the items being cycle counted for the organisation. The latter 3 values dicate the cycle count frequency for type A, B and C inventory items in days. In short the MaxVars entries allow some flexibility in the Cycle Count functionality rather than hard-coding these values. More details of these specific MaxVars entries are provided here:
https://developer.ibm.com/static/site-id/155/maximodev/7609/maximocore/businessobjects/psdi/app/inventory/Inventory.html
System Properties
System properties were introduced later but perform a similar role with a list of property names and values. System properties apply to either the instance or globally to all instances using the same database server. An added benefit of System Properties over MaxVars variables is that some system properties can be live-refreshed and the new property value is used immediatley rather than for example having to restart the application server.
Usage
One common example is the property name mxe.adminmode.logoutmin which records the number of minutes users have to log out before Admin Mode is enabled. This is usually modified in Database Configuration from More Actions -> Manage Admin Mode. Before enabling Admin Mode you can edit the "Number of Minutes for User Logout" and click the "Update Properties" to update the mxe.adminmode.logoutmin property value in System Properties.
https://www.ibm.com/support/knowledgecenter/en/SSLKT6_7.6.0/com.ibm.mbs.doc/propmaint/r_ctr_sysprops_overview.html
https://developer.ibm.com/static/site-id/155/maximodev/7609/maximocore/businessobjects/index.html?index-all.html
Why Are There Two Methods For Largely The Same Functionality?
I would guess that there is probably a lot of legacy code that still references the MaxVars variables rather than the newer System Properties and refactoring the code to use System Properties instead may not be a high priority but it's possible MaxVars may be phased out over time.
Maximo Customisation
When creating Maximo customisations either MaxVars variables or System Properties can be useful (with a preference to the latter) in order to avoid hard-coding values to provide reusability and flexibility. For example say you have a workflow that routes purchase orders over a particular value to the CEO for approval. Rather than hard-code the currency value in the workflow you could create a custom system property to store the threshold value amount and use a custom condition automation script to compare the PO total cost to the system property value and return a true or false accordingly. Therefore if the value threshold for CEO approval changes in future you only need modify the system property not the workflow.
Formulas
Whilst the link to the pdf in your question no longer works so I haven't viewed that document, from the excerpt you provided I would expect System Properties and MaxVars variables to be used in formulas in a similar fashion, to avoid hardcoding a value which would require us to modify the formula if it changes in future when a property can be used instead.
MAXVARS is a database table which identifies a number of system properties within the MAXIMO environment. Its a hang over from older versions and was around in version 3 as I remember (current version is 7.6).
Some MAXVARS entries can be amended through the MAXIMO UI (e.g. Organisations application - PM Options), others have to be amended through appropriate SQL (e.g. if Admin Mode becomes stuck you need to update the relevant MAXVARS entry via a SQL update)
New System values are now defined as 'system properties' (defined within MAXPROP and MAXPROPVALUE tables), and are visible and can be amended within the System Properties application.
:)
How can I make sure that specific data in the database isn't altered anymore.
We are working with TSQL. Inside the database we store contract revisions. These have a status: draft / active. When the status has become active, the revision may never be altered anymore. A revision can have 8 active modules (each with its own table), each with their own settings and sub-tables. This creates a whole tree of tables with records that may never change anymore when the contract revision has been set to active.
Ideally I would simply mark those records as read-only. But such thing does not exists as of today. The next thing that comes to mind are triggers. Thus I have to add those triggers to a lot of tables, all which are related to the contract revision.
Now maybe there are other approaches, like a database only for archiving on which the user only has insert rights. Thus when a contract revision has become active, it is moved from one DB to the archive DB (insert is allowed). And can never be altered anymore (DENY UPDATE|DELETE).
But maybe there are other more ingenious options I haven't thought of, and you did. Maybe including the CLR or what not.
So how can I make a tree-structure of records inside our TSQL database effectively readonly that is the most maintenance free, easy to understand, quickly to setup, and can be applied in a most generic way?
What ever you do (triggers, granted rights...) might be overcome by a user with higher rights, this you know for sure...
Is this just to archive this data?
One idea coming into my mind was to create a nested XML with all data within on big structure and put this somewhere into a side table. Create a INSTEAD OF UPDATE,DELETE TRIGGER where you just do nothing. Let these tables be 1:1-related.
You can still work with this data, but not quite as fast as being read from physical tables.
If you want, you even might convert the XML to a string and calculate some Hash-Code, which you store in a different place to check for manipulations.
The whole process might be done in one single Stored Procedure call.
I have just started using RavenDB on a personal project and so far inserting, updating and querying have all been very easy to implement. However, I have come across a situation where I need a GetOrCreate method and I'm wondering what the best way to achieve this is.
Specifically I am integrating with OpenID and once authentication has taken place the user is redirected to my site. At this point I'd either like to retrieve their user record from Raven (by querying on the ClaimsIdentifier property) or create a new record. The user's ID is currently being set by Raven.
Obviously I can write this in two statements but without some sort of transaction around the select and the create I could potentially end up with two user records in the database with the same claims identifier.
Is there anyway to achieve this kind of functionality? Possibly even more importantly is do you think I'm going down the wrong path. I'm assuming even if I could create a transaction it would make scaling out to multiple servers difficult and in anycase could add a performance bottle-neck.
Would a better approach be to have the Query and Create operations as separate statements and check for duplicates when the user is retrieved and merge at that point. Or do something similar but on a scheduled task?
I can't help but feel I'm missing something obvious here so any advice on this problem would be greatly appreciated.
Note: while scaling out to multiple servers may seem unnessecary for a personal project, I'm using it as an evaluation of Raven before using it in work.
Dan, although RavenDB has support for transactions, I wouldn't go that way in your case. Instead, you could just use the users ClaimsIdentifier as the user documents id, because they are granted to be unique.
Alternatively, you can also stay with user ids being generated by Raven (HiLo btw) and use the new UniqueConstraintsBundle, which lets you attribute certain properties to be unique. Internally it will create an additional document that has the value of your unique property as its id.
I am working on a design where I can have flexible attributes for users and I am confused how to continue the design of the schema.
I made a table where I kept system needed information:
Table name: users
id
username
password
Now, I wish to create a profile table and have one to one relation where all the other attributes in profile table such as email, first name, last name, etc. My question is: is there a way to add a third table in which profiles will be flexible? In other words, if my clients need to create a new attribute he/she won't need any customization to the code.
You're looking for a normalized table. That is a table that has user_id, key, value columns which produce a 1:N relationship between User & this new table. Look into http://en.wikipedia.org/wiki/Database_normalization for a little more information. Performance isn't amazing with normalized tables and it can take some interesting planning for optimization of your code but it's a very standard practice.
Keep the fixed parts of the profile in a standard table to make it easy to query, add constraints, etc.
For the configurable parts it sounds like you are looking for an entity-attribute-value model. The extra configurability comes at a high cost though: everything will have to be stored as strings and you will have to do any data validation in the application, not in the database.
How will these attributes be used? Are they simply a bag of data or would the user expect that the system would do something with these values? Are there ever going to be any reports against them?
If the system must do something with these attributes then you should make them columns since code will have to be written anyway that does something special with the values. However, if the customers just want them to store data then an EAV might be the ticket.
If you are going to implement an EAV, I would suggest adding a DataType column to your attributes table. This enables you to do some rudimentary validation on the entered data and dynamically change the control used for entry.
If you are going to use an EAV, then the one rule you must follow is to never write any code where you specify a particular attribute. If these custom attributes are nothing more than a wad of data, then an EAV for this one portion of your system will work. You could even consider creating an XML column to store these attributes. SQL Server actually has an XML data type but all databases have some form of large text data type that will also work. On reports, the data would only ever be spit out. You would never place specific values in specific places on reports nor would you ever do any kind of numerical operation against the data.
The price of an EAV is vigilence and discipline. You have to have discipline amongst yourself and the other developers and especially report writers to never filter on a specific attribute no matter how much pressure you get from management. The moment a client wants to filter or do operations on a specific attribute, it must become a first class attribute as a column. If you feel that this kind of discipline cannot be maintained, then I would simply create columns for each attribute which would mean an adjustment to code but it will create less of mess down the road.
Does anyone know of some good resources related to setting up heirarchical user account systems? I'm currently setting one up and am struggling with some of the more complex logic (especially with determining permissions). I was hoping I might be able to find some resources to help me along.
Some Background:
I'm building a user account system for a web CMS that allows for a nested group hierarchy. Each group can be allowed/denied access to read, write, add, and delete (either explicitly for that group, or implicitly by one of its parents). As if that weren't complicated enough, the system also allows for users to be members of multiple groups. -- This is where I'm stuck. I've got everything set up, but I'm struggling with the actual logic for determining pemissions for a given user.
The manual for CakePHP has an excellent description of how Access Control Lists work.
http://book.cakephp.org/2.0/en/core-libraries/components/access-control-lists.html
Represent the permissions set for a given group as a bit mask. OR-ing the bit masks together will give you the resultant permission set.
Update for #Alex:
I wrote this answer 3 years ago, but I believe I was alluding to the following...
From the question
a nested group hierarchy. Each group can be allowed/denied access to
read, write, add, and delete (either explicitly for that group, or
implicitly by one of its parents). As if that weren't complicated
enough, the system also allows for users to be members of multiple
groups. -- This is where I'm stuck. I've got everything set up, but
I'm struggling with the actual logic for determining pemissions for a
given user.
Assign a bitmask matching the total permission set of a group (or role) in the system:
e.g. 00 (using two bits keeps it simple here!)
The first bit confers Permission A and the second Permission B.
Now say Group A confers the following permission set: 01.
... and say Group B confers the following permission set: 10.
To get the resultant permission set for a user in an arbitrary set of groups you could perform a logical OR on the permission set bit masks:
Permission set for Group A 01
Permission set for Group B 10 OR
----
Resultant permission set 11 (i.e. both permission A and B are conferred)
I do not know the details of the questioner's system, but the system outlined here could be augmented to achieve different group-composition behaviors using different logical operators.
Look at the permissions in the Andrew File System. It allows users to create and administer groups of their own, while selectively assigning admin rights and ACLs. You might find that many of the pesky details are already worked out for you in their model.
Edit: here's a better link to AFS documentation:
http://www.cs.cmu.edu/~help/afs/index.html
Here's the section on groups:
http://www.cs.cmu.edu/~help/afs/afs_groups.html
I've done exactly this before and its no trivial implementation. You're going to want to look at the SecurityPermission class.
[http://msdn.microsoft.com/en-us/library/system.security.permissions.securitypermission.aspx][1]
I have done this before by utilizing XML (which I'm not sure I'd do again) and storing that XML as permission list inside of SQL server in an XML column through a CLR stored proc. The XML would have an element called a "permission" and then the permission would actually be a ENUM inside of the code. Each permission was a new implementation of the SecurityPermission class (linked above) Users were tied to groups which were defined in SQL server and then as the user was added/removed to groups, the XML doc would get updated to reflect which groups they were apart of.
As soon as the user logged in, the users credentials would be loaded into the application store (session) and then would be accessed accordingly. When authorization needed to take place the XMl in the application store would be pulled down loaded into the SecurityPermission via the "FromXML" method. At that point I would use the following methods to determine if the user had permission:
Demand
Intersect
Union
IsUnrestricted
IsSubSetOf
etc., etc, etc.
At that point after performing the Demand I was able to determine if the caller had access according to how I implemented my security routines in the SecurityPermissions.
Again, this is leaving out a TON of detail, but this should get you going down the right path.
Take a look at this name space as well: [2]: http://msdn.microsoft.com/en-us/library/system.security.permissions.aspx "System.Security.Permissions"