标准化 Table 数据

Normalizing Table Data

目前,我的 table 和关系有以下设置:

情况:

有员工在上课。他们登录了该课程独有的花名册 sheet。这些课程可以由不同的教师讲授多次。每门课程可能有多个不同学生的花名册(不同课程的不同花名册)。还有其他规格,但在大多数情况下,employees/instructors table 是我关心的。

我的问题是,在某些情况下,讲师也是员工。两个 table 之间的唯一区别是使用 InstructorID 而不是 EmployeeID。这对我的数据造成了一些冗余。我想避免这种情况,并尝试根据最佳实践来构建它。有没有办法合并两个 table 中的数据,以便在任何时候,我都可以在 table 中查询与某些员工一起教授 class 的所有讲师?

我想到了在员工 table 中添加 "isInstructor" 字段和 Yes/No。然后我可以检查一个人是否有。但是,我觉得这是一个糟糕的主意。有人建议在 employeeID 中添加前缀以指定他们是讲师。还有人建议,也许我可以添加一个额外的字段,其中包含同时也是雇员的讲师的标识符。

本质上,我想知道解决这个问题的最佳实践方法是什么。我应该将数据分开并放在两个 table 中,还是应该将它们合并并添加一些东西?对其余结构的任何建议也表示赞赏。这是我在这里的第一个问题,所以如果需要更多详细信息,请告诉我。

我会断开 InstructorsDepartment 的连接(删除 Instructors->DeptID)并将 EmployeeID 作为外键放入 Instructors。所以没有 EmployeeID(实际上有 null ID)的讲师是外部讲师。

这仍会使 Instructors 处于非规范化状态,但应该足以满足您的目的。

导师和员工都是人。 (我故意使用 varchar(5) 所以你不能在生产中使用它,除非至少 出现 考虑名称。)

create table people (
  p_id integer primary key,
  first_name varchar(5) not null,
  last_name varchar(5) not null,
  dept_id integer not null references departments
);

insert into people values
(1, 'Robin', 'Mings', 1),
(2, 'Ora', 'Black', 1),
(3, 'Sheri', 'Johns', 2),
(4, 'Dex', 'Sims', 3);

最佳实践:如果要对 table 名称使用复数形式,请始终使用复数形式。如果您想对 table 名称使用单数形式,请始终使用单数形式。 (我使用复数。)

使用 tables 获取有关某人是否受雇以及是否为教师的详细信息。您至少需要知道他们的身份证号码。 Robin Mings 和 Sheri Johns 是讲师。

create table instructors (
  p_id integer primary key references people
);

insert into instructors values 
(1), (3);

讲师 Sheri Johns 不是雇员。不过其他的都是。

create table employees (
  p_id integer primary key references people
);

insert into employees values
(1), (2), (4);

在table的"employees"和"instructors"中,选择使用"p_id"("people"中的列名)或类似的列名"emp_id" 取决于应用程序。

These courses can be taught several times by different instructors.

不,他们不能。您的设计只允许每门课程有一个日期。将课程与 class 区分开来。 class 是在特定讲师的指导下开设的课程,并在特定日期开始。

create table courses (
  course_id integer primary key,
  course_name varchar(5) not null unique,
  course_desc varchar(5) not null,       -- unique?
  course_objectives varchar(5) not null  -- Think about whether this deserves its own table.
);

insert into courses values
(1, 'AP101', 'Desc', 'Obj'),
(2, 'AR101', 'Desc', 'Obj');

最佳做法:不要在 table 名称中使用 list 之类的词。没有人说,"Are you supposed to be in this class? Let me check my roster list." 此外,语义很重要。如果您使用 "roster_list" 和 "course_list"(在您的情况下实际上不是课程或名册列表),为什么要避免使用 "employee_list"、"department_list" 等? List 在这里只是噪音。选择更好的词。

create table classes (
  course_id integer not null references courses,
  instructor_id integer not null references instructors (p_id),
  start_date date not null,
  -- I have no idea what completion_time means, so I omitted it.
  primary key (course_id, instructor_id, start_date)
);

insert into classes values
(1, 1, '2015-04-01'),
(2, 3, '2015-04-15');

根据我的经验,员工注册 classes,而不是课程。 (您使用了 sessions 这个词,但没有在您的设计中使用它。)根据应用程序,您可能需要比这个更多的 tables。

create table class_rosters (
  course_id integer not null,
  instructor_id integer not null,
  start_date date not null,
  employee_id integer not null references employees (p_id)
    on update restrict on delete cascade,
  primary key (course_id, instructor_id, start_date, employee_id),
  foreign key (course_id, instructor_id, start_date)
    references classes (course_id, instructor_id, start_date) 
    on update cascade on delete cascade
);

insert into class_rosters values 
(1, 1, '2015-04-01', 2),
(1, 1, '2015-04-01', 4),
(2, 3, '2015-04-15', 1), -- An instructor is taking this class.
(2, 3, '2015-04-15', 2),
(2, 3, '2015-04-15', 4);

Is there a way to combine the data in the two tables so that at any point, I could query the tables for all instructors that teach a class with certain employees?

-- Instructors who teach a class that has Ora Black in it.
select distinct cr.instructor_id
from class_rosters cr
inner join people p on p.p_id = cr.employee_id
where p.first_name = 'Ora' and p.last_name = 'Black';

-- Instructors who teach a class that has both Ora Black 
-- and Robin Mings (an instructor) in it.
with students as (
  select p_id 
  from people
  where (first_name = 'Ora' and last_name = 'Black')
     or (first_name = 'Robin' and last_name = 'Mings')
)
select instructor_id
from class_rosters cr
inner join students s on s.p_id = cr.employee_id
group by course_id, instructor_id, start_date
having count(*) = (select count(*) from students);
instructor_id
--
3