Designing relationships around an inheritance structure

坚强是说给别人听的谎言 提交于 2020-01-21 05:45:08

问题


I have a conceptual question regarding how best to organise my database.

Currently I have four core tables users, teachers, students and notifications. However both the teachers and students tables inherit from the users table so contain the foreign key user_id.

The notifications table as you might have guessed refers to notifications. These need to appear for all users that belong to an employee group i.e. under the employment of another.

Both students and teachers can employ other users.

So the crux is I need an eloquent way of modelling this. The basic workflow of the code would be the below:

getCurrentUser->getAllEmployer(s)->getNotifications

This is the Laravel Eloquent I'm used to $user->employers()->notifications;

Unfortunately it's not as simple as that as in this case an employer can refer to two tables.

So my choices are as follows.

  1. Create an Eloquent Relationship for both the student and teacher relationship as employers. The shortfall being I need to write if tests to check if the current user belongs to either and this code would be repeated frequently.
  2. Add a teacher_id and student_id to the users table. However one would obviously be redundant in each record. The chance of needing to add other columns is very likely as well due to the emergence of new employer entities.
  3. Create an employer_employee table that contains two columns both referencing a user_id. A SQL query would LEFT JOIN both student and teacher tables with the employer_employee table and then a JOIN with notifications would return all those relevant. However would so many joins reduce the speed of the query when compared with the other options.
  4. Something I haven't considered.

I'm really looking for the most efficient, scalable solution.

Any help is appreciated. If you could clarify why your answer is the most efficient scalable solution as well that would be superb.


回答1:


There is a similar question here using a Media supertype and adding subtypes of CD, VCR, DVD, etc.

This is scalable in that in creating, say, a BluRay subtype, you create the table to contain the BluRay-specific data and add an entry to the MediaTypes table. No changes needed for existing data or code -- except, of course, to add the code that will work with BluRay data.

In your case, Users would be the supertype table with Teachers and Students the subtype tables.

create table Users(
    ID      int not null auto_generating,
    Type    char( 1 ) check( Type in( 'T', 'S' )),
    -- other data common to all users,
    constraint PK_Users primary key( ID ),
    constraint UQ_UserType unique( ID, Type ),
    constraint FK_UserTypes foreign key( Type )
        references UserTypes( ID )
);
create table Teachers(
    TeacherID int not null,
    TeacherType char( 1 ) check( TeacherType = 'T' )),
    -- other data common to all teachers...,
    constraint PK_Teachers primary key( TeacherID ),
    constraint FK_TeacherUser foreign key( TeacherID, TeacherType )
        references Users( ID, Types )
);

The makeup of the Students table would be similar to the Teachers table.

Since both teachers and students may employ other teachers and students, the table that contains this relationship would refer to the Users table.

create table Employment(
    EmployerID    int not null,
    EmployeeID    int not null,
    -- other data concerning the employment...,
    constraint CK_EmploymentDupes check( EmployerID <> EmployeeID ),
    constraint PK_Employment primary key( EmployerID, EmployeeID ),
    constraint FK_EmploymentEmployer foreign key( EmployerID )
        references Users( ID ),
    constraint FK_EmploymentEmployee foreign key( EmployeeID )
        references Users( ID )
);

As I understand it, Notifications are grouped by employer:

create table Notifications(
    EmployerID    int not null
    NotificationDate date,
    NotificationData varchar( 500 ),
    -- other notification data...,
    constraint FK_NotificationsEmployer foreign key( EmployerID )
        references Users( ID )
);

The queries should be simple enough. For example, if a user wanted to see all the notifications from his employer(s):

select  e.EmployerID, n.NotificationDate, n.NotificationData
from    Employment  e
join    Notifications n
    on  n.EmployerID = e.EmployerID
where   e.EmployeeID = :UserID;

This is an initial sketch, of course. Refinements are possible. But to your numbered points:

  1. The Employment table relates employers to employees. The only check if to make user employers cannot employee themselves, but otherwise any user can be both an employee and employer.
  2. The Users table forces each user to be either a teacher ('T') or student ('S'). Only users defined as 'T' can be placed in the Teachers table and only users defined as 'S' can be placed in the Students table.
  3. The Employment table joins only to the Users table, not to both the Teachers and Students tables. But this is because both teachers and students can be both employers and employees, not for any performance reason. In general, don't worry about performance during the initial design. Your primary concern at this point is data integrity. Relational databases are very good with joins. If a performance issue should crop up, then fix it. Don't restructure your data to solve problems that do not yet exist and may never exist.
  4. Well, give this a try and see how it works.


来源:https://stackoverflow.com/questions/32574929/designing-relationships-around-an-inheritance-structure

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!