我可以看到您需要两个表的几个原因:
- 真实员工必须有姓名、部门等,而预测员工可能只有这些属性
- 会有只有真正的员工才能承担的职责,因此您希望能够单独引用它们
但同时您要确保两个表之间的 ID 没有冲突,因为(希望)预测员工将成为实际员工。
这样做的方法是实现一个超类型/子类型结构。因此,您有一个表 EMPLOYEES 保证单个主键,以及两个用于实际和预测员工的依赖表。类型列的使用至关重要,因为它确保给定的员工只出现在一个子表中。
create table employees
( emp_id number not null
, emp_type varchar2(8) not null
, constraint emp_pk primary key (emp_id)
, constraint emp_uk unique (emp_id, emp_type)
, constraint emp_type_ck check (emp_type in ('FORECAST', 'ACTUAL'));
create table actual_employees
( emp_id number not null
, emp_type varchar2(8) not null
, name varchar2(30) not null
, deptno number(2,0) not null
, sal number(7,2) not null
, hiredate date not null
, constraint actemp_pk primary key (emp_id)
, constraint actemp_type_ck check (emp_type = 'ACTUAL')
, constraint actemp_emp_fk foreign key (emp_id, emp_type)
references emp (emp_id, emp_type)
deferrable initially deferred ;
create table forecast_employees
( emp_id number not null
, emp_type varchar2(8) not null
, name varchar2(30)
, deptno number(2,0)
, sal number(7,2)
, predicted_joining_date date
, constraint foremp_pk primary key (emp_id)
, constraint foremp_type_ck check (emp_type = 'FORECAST')
, constraint foremp_emp_fk foreign key (emp_id, emp_type)
references emp (emp_id, emp_type)
deferrable initially deferred ;
所以键可能看起来有点奇怪。父表同时具有主键和复合唯一键。主键保证 EMP_ID 的单个实例。唯一键允许我们在引用 EMP_ID 和 EMP_TYPE 的子表上构建外键。结合对子 t 的检查约束这是因为它们引用父表上的唯一键而不是其主键。这种安排能够确保员工可以在 FORECAST_EMPLOYEES 或 ACTUAL_EMPLOYEES 中,但不能同时在两者中。
外键是可延迟的,以允许将预测员工转换为实际员工。这需要三个活动:
- 从 FORECAST_EMPLOYEES 中删除记录
- 将记录插入 ACTUAL_EMPLOYEES
- 更改 EMPLOYEES 中的 EMP_TYPE(但不是EMP_ID)。
使用延迟约束同步操作 2 和 3 更容易。
另外,请注意引用 EMPLOYEES 的其他外键约束应该使用主键而不是唯一键。如果关系关心员工的类型,那么它可能应该链接到子表。
“有点头疼”
欢迎来到数据建模的世界。这是一个很大的头痛。因为试图将混乱的现实融入一个干净的数据模型是很困难的:你需要明确的要求才能让它正确,并且了解最重要的事情,这样你才能做出明智的妥协。
我根据您的另一个问题提出了一种超类型/子类型方法,因为这似乎是处理两组数据的最佳方法:真实员工和名义员工。我认为这两个群体需要区别对待。例如,我会坚持认为经理是真正的雇员。这很容易通过针对 ACTUAL_EMPLOYEES 的完整性约束来实现,而使用包含两种类型员工的单个表则更难实现。
肯定有两个表意味着在同步它们的结构方面可能会产生更多的工作。所以呢?这在很大程度上是微不足道的,因为编写两个 ALTER TABLE 语句的工作量几乎不比一个多。此外,新列很可能仅适用于实际员工,对预测员工没有意义(例如 EARNED_COMMISSION、LAST_REVIEW_RATING)。鉴于此,拥有单独的表格会使数据模型更加准确。
正如 Ollie 所指出的,关于必须复制依赖表,这是一种误解。适用于所有员工的表,无论其实际情况如何,都应该引用 EMPLOYEES 表而不是其子表。
最后我不明白为什么用两张表比一张表更难维护历史数据。大多数日志代码应该完全从数据字典中生成。
“如果我有 Employee 表和 Employee_forecast 表……”
共有三个表:
- EMPLOYEES - 保证唯一 EMP_ID 的主表
- ACTUAL_EMPLOYEES - 为贵公司工作的人员的子表
- FORECAST_EMPLOYEES - 您希望招募到贵公司的人员的子表
“......他们的产品或活动都将存储在一个产品/活动表中?”
请记住,我是根据您提供的少量细节对您的业务逻辑做出假设。
现在在我看来,尚未为贵公司工作的人不应该有任何相关活动。在这种情况下,您将拥有一个表 EMPLOYEE_ACTIVITIES,它是 ACTUAL_EMPLOYEES 的子表。
但也许你确实为不存在的人提供活动。所以这里有一个选择:一张还是两张?单表设计将 EMPLOYEE_TASKS 作为主 EMPLOYEES 表的子表。这两个表设计分别将 ACTUAL_EMPLOYEE_TASKS 和 FORECAST_EMPLOYEE_TASKS 作为 ACTUAL_EMPLOYEES 和 FORECAST_EMPLOYEES 表的子表。
哪种设计是正确的取决于您是否需要强制执行有关任务分配的规则。例如,您的公司可能有一条规则,规定只有真人才能雇用新员工。因此,拥有一个只允许将招聘任务分配给 ACTUAL_EMPLOYEES 的模型会很有用。
“这个设计没有考虑月度预测”
好的,我已将日期列添加到两个表中。这将允许您运行所需的报告。