【问题标题】:Can a foreign key of a table be a part of the composite primary key of the same table?表的外键可以是同一张表的复合主键的一部分吗?
【发布时间】:2015-08-25 14:18:18
【问题描述】:

假设我有两个表:A(列:a、b、c、d)和 B(列:x、y、z)。现在,(a,b) 一起成为表 A 的主键,x 是表 B 的主键。是否可以将 b 设置为表 A 的外键,从表 B 中引用 x

请尽快回复!

提前致谢! :-)

【问题讨论】:

  • 是的,当然。您为什么不自己尝试一下。写两个CREATE TABLE 语句的时间与写那个问题的时间一样长。请不要使用“尽快”之类的内容 - 这不是付费支持平台。

标签: database database-design data-modeling


【解决方案1】:

是的,这没有问题。一个典型的例子(使用 MySQL 进行演示)是一个数据库表,其中包含许多公司和另一个包含可以为这些公司中的任何一个工作的员工:

create table companies (
  id            int primary key,
  name          varchar(20));

create table employees (
  id            int,
  c_id          varchar(20) references companies(id),
  name          varchar(20),
  primary key   (id, c_id));

insert into companies (id, name) values (1, 'ABC');
insert into companies (id, name) values (2, 'DEF');
insert into companies (id, name) values (3, 'HIJ');

insert into employees (id, c_id, name) values (101, 1, "Allan");
insert into employees (id, c_id, name) values (102, 1, "Bobby");
insert into employees (id, c_id, name) values (101, 2, "Carol");
insert into employees (id, c_id, name) values (101, 3, "David");

请注意,employees 的主键是由员工 ID 和公司 ID 组成的复合键。另请注意,公司 ID 是对 companies 主键的外键约束,即您询问的确切情况(功能上)。

显示谁为哪家公司工作的查询显示了这一点:

select   c.id, c.name, e.id, e.name
from     companies c, employees e
where    c.id = e.c_id
order by c.id, e.id

c.id   c.name   e.id   e.name
----   ------   ----   ------        
   1   ABC       101   Allan
   1   ABC       102   Bobby
   2   DEF       101   Carol
   3   HIJ       101   David

【讨论】:

    【解决方案2】:

    复合主键中的列也可以是引用另一个表的主键的外键吗?当然可以。重要的问题是,什么时候这是个好主意?

    最常见的场景可能是交叉口或交汇点表。客户可以有多个地址(运输、账单等),并且地址可以有多个客户使用它们。因此,表 CUSTOMER_ADDRESSES 有一个主键,它同时引用了 CUSTOMER 和 ADDRESS 主键(对于奖励点,ADDRESS_TYPE 也引用数据表)。

    我的示例使用 Oracle 12c 语法:

    create table customer_address 
        ( customer_id number(38,0) not null
          , address_id number(38,0) not null
          , address_type_code varchar2(3) not null
          , constraint customer_address_pk primary key 
                (customer_id, address_id, address_type_code)
          , constraint customer_address_customer_fk foreign key 
                (customer_id) references customer(customer_id)
          , constraint customer_address_address_fk foreign key 
                (address_id) references address(address_id)
          , constraint customer_address_type_fk foreign key 
                (address_type_code) references address_type(address_type_code)
         );
    

    当子表的主键由父键和仅在父键中唯一的标识符(通常是数字)组成时,会发生第二种情况。例如,一个订单有一个订单标题和一些订单行。订单由订单标题 ID 标识,其行由单调递增的数字标识。 ORDER_LINE 表可能如下所示:

    create table order_line 
        ( order_header_id number(38,0) not null
          , order_line_no number(38,0) not null
          , product_id number(38,0) not null
          , qty number(38,0) not null
          , constraint order_line_pk primary key 
                (order_header_id, order_line_no)
          , constraint order_line_header_fk foreign key 
                (order_header_id) references order_header(order_header_id)
          , constraint order_line_product_fk foreign key 
                (product_id) references product(product_id)
         );  
    

    请注意,我们可以将 ORDER_LINE 建模为另一个交集表,主键为(order_header_id, product_id),并将order_line_no 降级为普通属性状态:这取决于我们必须表示的业务规则。

    第二种情况比您想象的要少:复合主键在现实生活中非常少见。例如,我认为that other answer 中提出的模型很弱。我们可能需要将 Employee 用作许多关系(例如 Manager、Assignment、Sales)的外键。为外键使用复合键很笨拙(更多输入!)。此外,当我们深入研究这些模型时,我们经常发现其中一个键列是自然键而不是主键,因此可能会发生变化。对复合外键中的自然键列的级联更改是 PITN。

    因此,通常的做法是使用代理(或合成)主键,例如使用序列或标识列,并使用唯一约束强制自然键。后一步经常被遗忘,但它对于保持参照完整性至关重要。假设我们需要存储来自多个公司的员工的详细信息,包括公司的员工标识符,我们可能会有一个像这样的 EMPLOYEE 表:

    create table employee 
        ( employee_id number(38,0) generated always as number
          , company_id number(38,0) not null
          , company_employee_id varchar2(128) not null
          , name varchar2(128) not null
          , constraint employee_pk primary key 
                (employee_id)
          , constraint employee_uk unique 
                (company_id, company_employee_id)
          , constraint employee_company_fk foreign key 
                (company_id) references company(company_id)
         );  
    

    在数据仓库和其他 VLDB 中发现级联到依赖表的复合主键很常见的一种情况。在这里,复合键列构成了非规范化策略的一部分,以支持分区方案和/或有效的访问路径。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-09-11
      • 1970-01-01
      • 1970-01-01
      • 2023-03-25
      • 1970-01-01
      • 2012-09-22
      • 1970-01-01
      • 2018-11-20
      相关资源
      最近更新 更多