如何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 个职位(这允许最多两个):
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 一样使用(尽管不能插入其中)。
我是 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 个职位(这允许最多两个):
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 一样使用(尽管不能插入其中)。