简体   繁体   中英

Modeling a hierarchical data structure

20120304 - Streamlined question

Suppose we have entities R, D and E and this relational cardinalities

  • R < n:m > D
  • D < 1:n > E

The mapping of this specification is straight forward, but we have another requirement:

  • R < n:m > E

Side condition: E1 might only get 'assigned' to an R1, if E1 is related to some D1 and this D1 is related to the R1.

Unfortunately, even if E2 is related to a D2, which is related to an R2 - E2 might not be related to R2.

  • I'm in search of a relational DB model.
  • A model, which doesn't require multiple updates if a D gets detached from an Ra and reattached to another Rb. In this case, all Es of D need to get detached from Ra and attached to Rb too.

20120305 - Workaround?

A friend propose to create an entity DxR which links its D and its R by means of a tuple (D,R). Then create a relation

  • DxR < n:m > E

Hm...

20120302 - Original question

System is composed of top level zones (Z) . A zone may have several regions (R) .

So called departments (D) may be assigned to regions . One department may get assigned to more than one region, only if each region belongs to a different zone.

Finally, employees (E) belong to one and only one department .

Employees may get assigned to a region only if, employee's department belongs to the region.

Important: An employee need not belong to all regions its department belongs to.

Assume, that in the following graphics E1 belongs to D1. E1 should also belong to R1, but not belong to R2 - although D1 belongs to R1 and R2:

-     Z            Z
-   __|___      ___|___
-   R1   R      R2    R
-    \_________/
-     D1         

Q: Please propose the relations DB's table structure which models the above specification?

This question is very specific in one sense and some people might argue that it is too localized. There is, however, one more generally applicable idea that might be useful to other people in the future, so it isn't necessarily true that the question is too specific.

The really interesting part of these business rules is this one: (my emphasis added)

One department may get assigned to more than one region, only if each region belongs to a different zone .

Here is a schema that captures almost all of the stated business rules declaratively without having to resort to any triggers.

create table ZONE
( ID int not null
, NAME varchar(50) not null
, constraint PK_ZONE primary key clustered (ID)
)

create table REGION
( ZONE_ID int not null
, REGION_ID int not null
, NAME varchar(50) not null
, constraint PK_REGION primary key clustered (ZONE_ID, REGION_ID)
, conttraint FK_REGION__ZONE foreign key (ZONE_ID) 
    references ZONE (ID)
)

create table DEPARTMENT
( ID int not null
, NAME varchar(50) not null
, constraint PK_DEPARTMENT primary key clustered (ID)
)

create table EMPLOYEE
( ID int not null
, NAME varchar(50) not null
, DEPT_ID int not null
, constraint PK_EMPLOYEE primary key clustered (ID)
, constraint FK_EMPLOYEE__DEPARTMENT foreign key (DEPT_ID) 
    references DEPARTMENT (ID)
)

The above tables are pretty obvious. However, there is one particular quirk: The REGION table has a compound primary key that includes the FK to ZONE . This is useful for propagating the constraint about departments having to be distinct within a zone.

Assigninging departments to regions requires an intersection table:

create table DEPT_ASGT -- Department Assignment
( REGION_ID int not null
, DEPT_ID int not null
, ZONE_ID int not null
, constraint PK_DEPT_ASGT (REGION_ID, DEPT_ID)
, constraint FK_DEPT_ASGT__REGION foreign key (ZONE_ID, REGION_ID) 
    references REGION (ZONE_ID, ID)
, constraint FK_DEPT_ASGT__DEPARTMENT foreign key (DEPT_ID) 
    references DEPARTMENT (ID)
, constraint UN_DEPT_ASGT__ZONES unique nonclustered (ZONE_ID, DEPT_ID)
)

This intersection table is pretty normal insofar as it has a foreign key to each of the tables that it links. What is special about this intersection table is the unique constraint. This is what enforces the rule that a department can't be in two different regions within the same zone.

Lastly, we need to map employees into departments and into regions. This requires another intersection table:

create table EMP_ASGT -- Employee Assignment
( REGION_ID int not null
, DEPT_ID int not null
, EMPLOYEE_ID int not null
, constraint PK_EMP_ASGT (REGION_ID, DEPT_ID, EMPLOYEE_ID)
, constraint FK_EMP_ASGT__DEPT_ASGT (REGION_ID, DEPT_ID) 
    references DEPT_ASGT (REGION_ID, DEPT_ID)
, constraint FK_EMP_ASGT__EMPLOYEE (EMPLOYEE_ID) refernces EMPLOYEE (ID)
)

You will note that the EMPLOYEE table has a foreign key to DEPARTMENT - That enforces the rule that each employee can belong to only one department. The EMP_ASGT table adds the details about which regions the employee participates in. Since an employee may not be involved in every region that his or her department is assigned to, the EMP_ASGT table connects employees to just those regions where they have some involvement.

Here is the one place where a trigger or some other procedural logic is needed. You need to make sure that EMPLOYEE.department_id stays consistent with the records in EMP_ASGT. You could try to push this into the declarative referential integrity by making the PK of EMPLOYEE a compound of ID and DEPT_ID, but that would force you to decide whether you want to violate 3NF or make your employee department changes a procedurally ugly mess. At the end of the day, a little trigger to make sure that EMP_ASGT doesn't disagree with EMPLOYEE.DEPT_ID would be much less trouble.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM