如何link三个表?

How to link three tables?

我是 SQL 的新手,请求您的帮助。
有 3 个 table,它们是 "Employees""Positions""EmployeesPositions"。 例如,2 positions 可以附加到一个 employee。 如何 link table 不重复?我阅读了有关外键和 JOIN 的内容,但我还没有弄清楚如何正确执行此操作。

Table结构: Employees (id, Name); Positions (id, Post, Rate); EmployeesPositions - 我不知道如何正确。

我需要的是:将员工添加到 "Employees" table 时,将条目与 "Positions" table 中的帖子相关联,但正如我上面所写的,一名员工可以与 2 个职位相关联(但并非总是如此)。如何正确实现第三个table(EmployeesPositions),因为在Positions中只存储职位和费率,而在EmployeesPositions中应该有记录,例如Name1 => Post1和Post2,仅 Name2 Post 1?

如果我想错了,请告诉我如何最好地实现它。

有多种方法可以解决您的问题,每种方法各有利弊。

首先,如果我们将您的问题简化为 "an employee has zero or more positions",那么您可以使用以下 table 将员工与职位相关联:

create table employeespositions (
    employee_id integer not null,
    position_id integer not null,
    constraint pk_employeespositions 
        primary key (employee_id, position_id),
    constraint fk_employeespositions_employee 
        foreign key (employee_id) references employees (id),
    constraint fk_employeespositions_position 
        foreign key (position_id) references positions (id)
)

外键强制员工和职位的存在,而主键确保员工和职位的组合只存在一次。

这个解决方案有两个缺点:

  1. 它不强制员工至少有一个职位
  2. 它允许一个员工拥有两个以上的职位

第二个问题很容易通过添加一个触发器来解决,该触发器在尝试插入时检查员工是否最多有 1 个职位(这允许最多两个):

create exception tooManyPositions 'Too many positions for employee';

set term #;
recreate trigger employeespositions_bi 
    active before insert on employeespositions
as
    declare position_count integer;
begin
    select count(*) 
        from employeespositions 
        where employee_id = new.employee_id 
        into position_count;
    if (position_count > 1) then
        exception tooManyPositions;
end#
set term ;#

但是,此解决方案并不强制要求一名员工至少拥有一个职位。您可以添加一个 before delete 触发器来确保不能删除最后一个职位,但不能确保新创建的员工至少有一个职位。如果你想强制执行,你可能需要考虑使用存储过程来插入和更新员工及其职位,并让这些存储过程的代码强制执行(例如,通过在创建员工时要求职位)。

或者,您也可以考虑对您的设计进行非规范化,并将职位作为 employees 记录的一部分,其中员工有一个 'primary' 和(可选)一个 'secondary' 职位.

create table employees (
    -- using Firebird 3 identity column, change if necessary
    id integer generated by default as identity primary key,
    name varchar(100),
    primary_position_id integer not null,
    secondary_position_id integer,
    constraint fk_employees_primary_position 
        foreign key (primary_position_id) references positions (id),
    constraint fk_employees_secondary_position 
        foreign key (secondary_position_id) references positions (id),
    constraint chk_no_duplicate_position 
        check (secondary_position_id <> primary_position_id)
)

primary_position_id 上的 not null 约束强制存在该位置,而检查约束防止将相同位置分配给两列。您可以选择添加一个 before insert or update 触发器,当 primary_position_id 设置为 null 时,会将其设置为 secondary_position_id 的值并将 secondary_position_id 设置为 null.

此解决方案的优点是允许强制执行主要职位的存在,但在查询职位时可能会导致额外的复杂性。这个缺点可以通过创建视图来克服:

create view employeespositions
as
    select id as employee_id, primary_position_id as position_id
    from employees
    union all
    select id as employee_id, secondary_position_id as position_id
    from employees
    where secondary_position_id is not null;

此视图可以像 table 一样使用(尽管不能插入其中)。