标准化 Table 数据
Normalizing Table Data
目前,我的 table 和关系有以下设置:
情况:
有员工在上课。他们登录了该课程独有的花名册 sheet。这些课程可以由不同的教师讲授多次。每门课程可能有多个不同学生的花名册(不同课程的不同花名册)。还有其他规格,但在大多数情况下,employees/instructors table 是我关心的。
我的问题是,在某些情况下,讲师也是员工。两个 table 之间的唯一区别是使用 InstructorID 而不是 EmployeeID。这对我的数据造成了一些冗余。我想避免这种情况,并尝试根据最佳实践来构建它。有没有办法合并两个 table 中的数据,以便在任何时候,我都可以在 table 中查询与某些员工一起教授 class 的所有讲师?
我想到了在员工 table 中添加 "isInstructor" 字段和 Yes/No。然后我可以检查一个人是否有。但是,我觉得这是一个糟糕的主意。有人建议在 employeeID 中添加前缀以指定他们是讲师。还有人建议,也许我可以添加一个额外的字段,其中包含同时也是雇员的讲师的标识符。
本质上,我想知道解决这个问题的最佳实践方法是什么。我应该将数据分开并放在两个 table 中,还是应该将它们合并并添加一些东西?对其余结构的任何建议也表示赞赏。这是我在这里的第一个问题,所以如果需要更多详细信息,请告诉我。
我会断开 Instructors
与 Department
的连接(删除 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
目前,我的 table 和关系有以下设置:
情况:
有员工在上课。他们登录了该课程独有的花名册 sheet。这些课程可以由不同的教师讲授多次。每门课程可能有多个不同学生的花名册(不同课程的不同花名册)。还有其他规格,但在大多数情况下,employees/instructors table 是我关心的。
我的问题是,在某些情况下,讲师也是员工。两个 table 之间的唯一区别是使用 InstructorID 而不是 EmployeeID。这对我的数据造成了一些冗余。我想避免这种情况,并尝试根据最佳实践来构建它。有没有办法合并两个 table 中的数据,以便在任何时候,我都可以在 table 中查询与某些员工一起教授 class 的所有讲师?
我想到了在员工 table 中添加 "isInstructor" 字段和 Yes/No。然后我可以检查一个人是否有。但是,我觉得这是一个糟糕的主意。有人建议在 employeeID 中添加前缀以指定他们是讲师。还有人建议,也许我可以添加一个额外的字段,其中包含同时也是雇员的讲师的标识符。
本质上,我想知道解决这个问题的最佳实践方法是什么。我应该将数据分开并放在两个 table 中,还是应该将它们合并并添加一些东西?对其余结构的任何建议也表示赞赏。这是我在这里的第一个问题,所以如果需要更多详细信息,请告诉我。
我会断开 Instructors
与 Department
的连接(删除 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