我不清楚为什么需要在 TASKS 表上识别 FRUIT_TYPE。从表面上看,这只是一个糟糕的(去规范化的)数据模型。
根据我的经验,对此类数据建模的最佳方法是使用通用事物的超类型(在您的示例中为 FRUIT)和特定事物的子类型(APPLE、GRAPE、BANANA)。这使我们可以将通用属性存储在一个地方,同时记录每个实例的特定属性。
这是超类型表:
create table fruits
(fruit_id number not null
, fruit_type varchar2(10) not null
, constraint fruit_pk primary key (fruit_id)
, constraint fruit_uk unique (fruit_id, fruit_type)
, constraint fruit_ck check (fruit_type in ('GRAPE', 'APPLE', 'BANANA'))
)
/
FRUITS 有一个主键和一个复合唯一键。我们需要在外键约束中使用主键,因为复合键很麻烦。除非它们不是,这是这些子类型表的情况。这里我们使用唯一键作为参考,因为通过在子类型中约束 FRUIT_TYPE 的值,我们可以保证 GRAPES 表中的记录映射到 'GRAPE' 类型的 FRUITS 记录等。
create table grapes
(fruit_id number not null
, fruit_type varchar2(10) not null default 'GRAPE'
, seedless_yn not null char(1) default 'Y'
, colour varchar2(5) not null
, constraint grape_pk primary key (fruit_id)
, constraint grape_ck check (fruit_type = 'GRAPE')
, constraint grape_fruit_fk foreign key (fruit_id, fruit_type)
references fruit (fruit_id, fruit_type)
, constraint grape_flg_ck check (seedless_yn in ('Y', 'N'))
)
/
create table apples
(fruit_id number not null
, fruit_type varchar2(10) not null
, apple_type varchar2(10) not null default 'APPLE'
, constraint apple_pk primary key (fruit_id)
, constraint apple_ck check (fruit_type = 'APPLE')
, constraint apple_fruit_fk foreign key (fruit_id, fruit_type)
references fruit (fruit_id, fruit_type)
, constraint apple_type_ck check (apple_type in ('EATING', 'COOKING', 'CIDER'))
)
/
create table bananas
(fruit_id number not null
, fruit_type varchar2(10) not null default 'BANANA'
, constraint banana_pk primary key (fruit_id)
, constraint banana_ck check (fruit_type = 'BANANA')
, constraint banana_fruit_fk foreign key (fruit_id, fruit_type)
references fruit (fruit_id, fruit_type)
)
/
在 11g 中,我们可以使 FRUIT_TYPE 成为子类型的虚拟列,并取消检查约束。
所以,现在我们需要一个任务类型表('Peel'、'Refrigerate'、'Eat'等)。
create table task_types
(task_code varchar2(4) not null
, task_descr varchar2(40) not null
, constraint task_type_pk primary key (task_code)
)
/
实际的 TASKS 表是 FRUITS 和 TASK_TYPES 之间的简单交集。
create table tasks
(task_code varchar2(4) not null
, fruit_id number not null
, constraint task_pk primary key (task_code, fruit_id)
, constraint task_task_fk ask foreign key (task_code)
references task_types (task_code)
, constraint task_fruit_fk foreign key (fruit_id)
references fruit (fruit_id)
/
如果这不能满足您的需求,请编辑您的问题以包含更多信息。
“……如果你想为不同的水果做不同的任务……”
是的,我想知道这是否是 OP 发布设计的动机。但通常工作流程比这要困难得多:有些任务适用于所有水果,有些只适用于(比如说)成束的水果,有些只适用于香蕉。
“在我们的实际逻辑中,‘水果’是完全不同的表格,几乎没有共同点。想想客户、员工、会议、房间、建筑物、资产标签等。步骤列表应该是自由格式的,允许用户指定对任何这些事情的行动。”
所以你有一堆现有的表。您希望能够以随心所欲的方式将这些表中的记录分配给任务,同时又能够保证识别拥有该任务的特定记录。
我认为您仍然需要一个通用表来保存任务中参与者的 ID,但您需要以某种方式将其链接到其他表。以下是我可能会采用的方法:
Soem 示例现有表:
create table customers
(cust_id number not null
, cname varchar2(100) not null
, constraint cust_pk primary key (fruit_id)
)
/
create table employees
(emp_no number not null
, ename varchar2(30) not null
, constraint emp_pk primary key (fruit_id)
)
/
保存演员的通用表:
create table actors
(actor_id number not null
, constraint actor_pk primary key (actor_id)
)
/
现在,您需要交集表将现有表与新表相关联:
create table cust_actors
(cust_id number not null
, actor_id number not null
, constraint cust_actor_pk primary key (cust_id, actor_id)
, constraint cust_actor_cust_fk foreign key (cust_id)
references customers (cust_id)
, constraint cust_actor_actor_fk foreign key (actor_id)
references actors (actor_id)
)
/
create table emp_actors
(emp_no number not null
, actor_id number not null
, constraint emp_actor_pk primary key (emp_no, actor_id)
, constraint emp_actor_emp_fk foreign key (emp_no)
references eployees (emp_no)
, constraint cust_actor_actor_fk foreign key (actor_id)
references actors (actor_id)
)
/
鉴于之前的情况,TASKS 表并不令人惊讶:
create table tasks
(task_code varchar2(4) not null
, actor_id number not null
, constraint task_pk primary key (task_code, actor_id)
, constraint task_task_fk ask foreign key (task_code)
references task_types (task_code)
, constraint task_actor_fk foreign key (actor_id)
references actors (actor_id)
/
我同意所有这些交集表看起来像很多开销,但没有任何其他方法可以强制执行外键约束。每次在 CUSTOMERS 中创建记录时,额外的障碍是创建 ACTORS 和 CUSTOMER_ACTORS 记录。同上删除。唯一的好消息是您可以生成所需的所有代码。
这个解决方案是否比具有一百个可选外键的表更好?也许不是:这是一个品味问题。但我更喜欢它,而不是完全没有外键。如果在数据库实践中存在普遍的真理,那就是:依赖应用程序代码来强制执行关系完整性的数据库是充满了引用错误父级或根本没有引用父级的子级的数据库。