mysqldatabase-designdatabase-integrity

Integrity constraint issue in an inheritance relational model with MySQL 5.5


I have the following database relational schema that purports to model an EMP supertype together with two FULL_TIME_EMP and PART_TIME_EMP subtypes:

database schema The problem I have is one of integrity i.e. I would like to make sure that a full-time employee can only have a corresponding row in the FULL_TIME_EMP table and similarly a part-time employee can only have a correponding row in the PART_TIME_EMP table.

As you will see from the screen captures below, this integrity constraint is not enforced.

Here is the EMP_TYPE table: EMP_TYPE table

And the EMP table: EMP table

The PART_TIME_EMP table: PART_TIME_EMP table

And finally the FULL_TIME_EMP table which presents the integrity violation!! FULL_TIME_EMP table

Is there a way to enforce this integrity constraint by altering my database model design or do I have to resort to triggers?

Regards,


Solution

  • You don't have to resort to triggers, but it might help. Changing the structure will help a lot, but it normally relies on CHECK constraints. In MySQL, if you need a CHECK constraint, you have to write a trigger or add another table that uses a foreign key constraint.

    You have an additional burden, in that a part-time employee could later become full time, and vice versa.

    Here how I'd write it for a more standard SQL platform.

    create table emp (
      emp_id integer primary key,
      emp_type char(1) not null check (emp_type in ('f', 'p')), -- FK in your schema
      emp_name varchar(50) not null,
      start_date date not null default current_date,
      unique (emp_id, emp_type)
    );
    
    create table full_time_emp (
      emp_id integer primary key references emp (emp_id),
      emp_type char(1) not null default 'f' check (emp_type = 'f'),
      foreign key (emp_id, emp_type) references emp (emp_id, emp_type),
      salary decimal(14,2) not null check (salary > 15000)
    );
    
    create table part_time_emp (
      emp_id integer primary key references emp (emp_id),
      emp_type char(1) not null default 'p' check (emp_type = 'p'),
      foreign key (emp_id, emp_type) references emp (emp_id, emp_type),
      rate decimal(14,2) not null check (rate > 0 and rate < 100)
    );
    

    In the table full_time_emp, these two constraints

      check (emp_type = 'f'),
      foreign key (emp_id, emp_type) references emp (emp_id, emp_type),
    

    taken together prevent part-time employees from appearing in the full-time table. If the referenced row in "emp" contains 'p', the foreign key reference will fail; if you try to insert a 'p' into "full_time_emp", the check constraint will fail.

    In this case, you can replace the check constraint with a one-row table and a foreign key reference. The table should contain only 'f'. (Or whatever ID number you use to represent a full-time employee.) Same thing works for "part_time_emp". So instead of the CHECK constraint, you could do this for full_time_emp.

    create table full_time_emp_check (
      full_time_emp_type char(1) primary key
    );
    insert into full_time_emp_check values ('f');
    
    alter table full_time_emp
    add constraint full_time_emp_check
    foreign key (emp_type) references full_time_emp_check (full_time_emp_type);
    

    Changes for part_time_emp are similar. You might want a trigger on both those tables to make sure they never contain more than one row.

    Even the checks on salary and rate could be implemented with additional tables and foreign key references. Be kind of like kicking dead whales down the beach, though. I'd probably implement that kind of range constraint with a trigger in MySQL.

    IMHO, a CHAR(1) code is better than an integer for employee types. A human-readable code doesn't require a join.