views:

109

answers:

1

For a system I am currently building, the following two scenarios occur:

  1. My permissions system is the perennial favorite of attaching permissions to roles, and roles to users, but with a twist: permissions may be applied to any asset at any tier, and there are 4 "Tiers", numbered 0 through 3. As such, the role assignment table is 5 fields long: asset tier, asset ID, role ID, user ID, and a flag for "disabled." This "Tier/ID" system is also used in assigning users to a "Home Asset".

    The unfortunate side effect of this system is that it is generally far simpler to resolve my these asset references in the application itself, instead of in the SQL (I know it would be possible to do, but it would result in some seriously ugly queries). Is there a better way to represent this sort of relationship? Is this an acceptable way of representing this relationship?

    Side note: the tables for the assets themselves (they are hierarchal) are normalized, with foreign key references pointing from child to parent (e.g. Tier 3 contains info about Tier 2, etc), so once a single asset is pinned down it is very simple to locate its children and ancestors.

  2. Similar problem, but different data context: At the lowest tier, devices can be "attached." Devices are also allowed to be placed onto a graphical representation (like a map), and their arrangement information is stored in the database. Graphical references (also called "points") should point at a real device, but a real device may not point at a graphical reference.

    At present, there is a single reference table to all "points" (which represent devices) with information for their display layout, location, size, etc. I can identify three possible things to do from here:

    1. Create a series of normalized tables to represent individual devices, and use a similar "device type / device ID" system to look them up, and store this information in the "points" table; this will still require the application to do intermediary work in resolving those references to actual devices in their respective tables
    2. Create a series of normalized tables, and point them at the "points" table; this solution probably won't require the application to dereference the points data, but will require that all device tables be scanned for any Tier 3 object.
    3. Using the points table as a guideline, add all of the relevant display data to each respective normalized table. While this would prevent having to resolve any lookups at all, it will still require that I scan every device table, and is also extremely intolerant to any changes in how the points data must be stored compared to the other solutions.

    Is this problem already solved and I just didn't get the memo? Is there a standard design addressing this problem, or should I just assume that I will have to resolve these references myself?

Note: With respect to problem two, I have deemed the following two solutions to be horrible and I will not consider them (barring some crazy, amazing evidence that it is the best way, which I'm fairly sure it isn't):

  1. Store all the information about each device (in a serialized array or some such mechanism) in the points table itself. This has the obvious problem of being completely unsearchable and defying normal form
  2. Store all devices in a single, generic table. Unfortunately, these devices are all extremely different in the type of data they provide, as well as how they must be accessed, and doing so in one table would require, last time I tried working it out on paper, at least 30 (and probably more) columns, more than half of which would be null for every row.

Any solutions are requested and appreciated, though I am specifically looking for existing patterns and/or normalized schemas which address this issue, if any exist. If you know for a fact that it is the correct answer, "No" will suffice and I will continue to use the application layer to resolve these references. It has not been a problem yet, I just prefer to know I'm not forging a path in the wrong direction when someone has already solved this/these problem(s). Thanks in advance.

EDIT: Permissions/Asset Schema

Tier 0 is actually implied and not in the database, but some things are registered as Tier 0 (Asset ID 0)

Tier 1:

id int(5) unsigned not null primary key auto_increment,
name varchar(32) not null,
disabled tinyint(1) unsigned not null,

Tier 2:

id int(5) unsigned not null primary key auto_increment,
parentId int(5) unsigned not null,
name varchar(32) not null,
abbr varchar(16) not null,
disabled tinyint(1) unsigned not null,
foreign key (parentId) references tier1(id)

Tier 3:

id int(5) unsigned not null primary key auto_increment,
parentId int(5) unsigned not null,
name varchar(32) not null,
abbr varchar(16) not null,
disabled tinyint(1) unsigned not null,
foreign key (parentId) references tier2(id)

Permissions:

id int(5) unsigned not null primary key auto_increment,
permission_key varchar(16) not null,
permission_desc varchar(128) not null

Roles:

id int(5) unsigned not null primary key auto_increment,
name varchar(32) not null,
tier1_id int(5) unsigned not null,
disabled tinyint(1) unsigned not null,
foreign key (tier1_id) references tier1(id)

Role_Permissions:

role_id int(5) unsigned not null,
permission_id int(5) unsigned not null,
disabled tinyint(1) unsigned not null,
primary key (role_id, permission_id),
foreign key (role_id) references roles(id),
foreign key (permission_id) references permissions(id)

User_Role_Permissions:

tier_id tinyint(1) unsigned not null,
asset_id int(5) unsigned not null,
user_id int(5) unsigned not null,
role_id int(5) unsigned not null,
disabled tinyint(1) unsigned not null,
primary key (tier_id, asset_id, user_id, role_id),
foreign key (user_id) references users(id),
foreign key (role_id) references roles(id)
+3  A: 

Have you considered breaking up your permissions table into a set of tables that each apply to a separate table of objects, for instance tier_1_assets and tier_1_permissions, and tier_2_assets and tier_2_permissions. This will make it simpler to query the permissions for a tier of assets, but more complex to query for all the permissions of a user (this is a trade off).

I would also challenge the assumption that a relational database is the right storage medium for your data. It may indeed be, but may also not be. (PHP may limit freedom in that respect).

If your data set is small, why not save it to a single serialized file, and have a small server that keeps it in memory and provides a query interface? I am sure there are good reasons why not. (In addition to being in PHP)

Have you considered multiple point tables for your second problem? It may feel like a repetition, but it does make life simpler for some kinds of look ups, and is normalized when you consider each foreign key reference as a different type of object (you get to use foreign key checks on them when the tables are separated).

The multiplicity of the relationships between objects should tell you where to put your keys and references: one-to-many -> (key <- foreign key); many-to-many -> (key <- link-table -> key); one-to-optional -> (key <- foreign key).

I do not know of any patterns that match your problem as it was stated. But I have noticed that most generic solutions to avoid creating another table get really hard to manage very quickly.

Sarah Happy
Can you think of a significant advantage to one point table per device versus point info stored in each device's table on nullable columns?
Dereleased
You could also consider three tables, device, point, and device_point, that would let you search by points and by device, and not have your nullable columns. I view nullable columns as a sign that a table maybe should be broken up.
Sarah Happy
I have accepted your answer as we decided to challenge the assumption that a relational database was the correct storage medium for this project and have instead relegated the actual device data storage to a separate mechanism elsewhere in the project; this way, the points table need only be aware of the module that data is stored in, and the ID it is stored as in said module, and other than that can simply store the appropriate map-related data. Thus, the points table is now the only table needed. Sometimes, I guess I just need to put down the MySQL "Hammer".
Dereleased