强制执行表之间的约束

时间:2010-10-15 01:04:27

标签: sql

如何建立约束,其中一列(不是主键)必须与另一个表的列具有相同的值。我不太清楚怎么说它所以这是一个例子:

例: 我有三个表,EmployeeDirectorDivisionDepartment

表的结构如下:

员工

  • 编号
  • 名称
  • DirectorId(FK)
  • DepartmentID(FK)

  • 编号
  • 名称
  • DepartmentID(FK)

  • 编号
  • 名称
  • DivisionId(FK)

  • 编号
  • 名称

员工和董事都有部门,每个部门都有一个部门,但他们的部门必须相同。有没有办法强制执行此操作? (希望不必使用触发器)

3 个答案:

答案 0 :(得分:3)

创建具有适当GRANTS的存储过程,并且不允许用户直接INSERT到表中。使用存储过程作为数据库的接口,并在插入之前检查其中所需的条件。

答案 1 :(得分:1)

创建外键没有限制 - 没有什么可以阻止您在这些表上定义外键约束:

  • EMPLOYEE

...将它们与DEPARTMENT表相关联。虽然坦率地说,我认为不需要DIRECTOR表 - 它应该是EMPLOYEE表中的布尔指示符,或者可能是EMPLOYEE_TYPE_CODE,它有自己的外键约束来区分员工和董事。

多个外键

外键的存在也不会阻止您在同一列上放置第二个(或第三个等)约束。考虑TABLE_C.column具有TABLE_A.col和TABLE_B.col的外键约束的情况 - 这在数据库中是完全可以接受的,但这意味着只有TABLE_A.col和TABLE_B.col中存在的值才能存在于TABLE_C.column。 IE:

TABLE_A

col
----
a
b
c

表-B

col
----
c

基于此示例数据,TABLE_C.column只允许“c”作为值存在于列中,如果有人向TABLE_C.column添加了两个外键约束,引用TABLE_A.col和TABLE_B.col。

答案 2 :(得分:1)

首先,您的示例表做得太多了。有一个设计原则规定单个表应该模拟实体或实体之间的关系,但不能同时建模。部门,董事和员工之间的关系(我假设董事不是员工;我现在也忽略了部门)。

其次,一个表可以有多个密钥,称为候选密钥。此外,您可以通过向键添加非唯一列来创建UNIQUE约束。例如,员工的姓名不能成为一个好的密钥,因此有一个员工ID的原因(我不认为对于部门来说也是如此,即部门名称本身就是一个足够好的密钥)。如果employee_ID是唯一的,那么(employee_name, employee_ID)也将是唯一的。

第三,任何UNIQUE约束都可以引用一个表,它不必是表的'主'键(这部分解释了为什么'主键'有点无意义)。< / p>

上述优点是可以使用FOREIGN KEY和行级CHECK约束来建模所需的约束。 SQL优化器和程序员更喜欢声明性解决方案到过程代码(触发器,存储过程等)。这个vanilla SQL DDL将移植到大多数SQL产品。

因此,部门名称可以分别与导演密钥和员工密钥结合使用,这些复合密钥可以在简单的两层组织结构图表中引用:因为员工部门及其主管部门都将出现在在同一个表中,可以使用简单的行级CHECK约束来测试它们是否相同,例如

实体表:

CREATE TABLE Departments
(
 department_name VARCHAR(30) NOT NULL UNIQUE
);

CREATE TABLE Employees
(
 employee_ID INTEGER NOT NULL UNIQUE, 
 employee_name VARCHAR(100) NOT NULL
);

CREATE TABLE Directors
(
 director_ID INTEGER NOT NULL UNIQUE, 
 director_name VARCHAR(100) NOT NULL
);

关系表:

CREATE TABLE EmployeeDepartments
(
 employee_ID INTEGER NOT NULL UNIQUE
    REFERENCES Employees (employee_ID), 
 employee_department_name VARCHAR(30) NOT NULL 
    REFERENCES Departments (department_name), 
 UNIQUE (employee_department_name, employee_ID)
);

CREATE TABLE DirectorDepartments
(
 director_ID INTEGER NOT NULL UNIQUE
    REFERENCES Directors (director_ID), 
 director_department_name VARCHAR(30) NOT NULL 
    REFERENCES Departments (department_name), 
 UNIQUE (director_department_name, director_ID)
);

CREATE TABLE OrgChart
(
 employee_ID INTEGER NOT NULL UNIQUE, 
 employee_department_name VARCHAR(30) NOT NULL,  
 FOREIGN KEY (employee_department_name, employee_ID)
    REFERENCES EmployeeDepartments 
       (employee_department_name, employee_ID), 
 director_ID INTEGER NOT NULL, 
 director_department_name VARCHAR(30) NOT NULL,  
 FOREIGN KEY (director_department_name, director_ID)
    REFERENCES DirectorDepartments 
       (director_department_name, director_ID), 
 CHECK (employee_department_name = director_department_name)
);

现在一个稍微有趣的场景是,当一名董事被分配一个部门而不是一个特定的部门时,你必须测试该员工的部门与她的董事在同一部门:

实体表:

CREATE TABLE Divisions
(
 division_name VARCHAR(20) NOT NULL UNIQUE
);

CREATE TABLE Departments
(
 department_name VARCHAR(30) NOT NULL UNIQUE, 
 division_name VARCHAR(20) NOT NULL
    REFERENCES Divisions (division_name), 
 UNIQUE (division_name, department_name)
);

CREATE TABLE Employees
(
 employee_ID INTEGER NOT NULL UNIQUE, 
 employee_name VARCHAR(100) NOT NULL
);

CREATE TABLE Directors
(
 director_ID INTEGER NOT NULL UNIQUE, 
 director_name VARCHAR(100) NOT NULL
);

关系表:

CREATE TABLE EmployeeDepartments
(
 employee_ID INTEGER NOT NULL UNIQUE
    REFERENCES Employees (employee_ID), 
 employee_department_name VARCHAR(30) NOT NULL 
    REFERENCES Departments (department_name), 
 UNIQUE (employee_department_name, employee_ID)
);

CREATE TABLE DirectorDivisions
(
 director_ID INTEGER NOT NULL UNIQUE
    REFERENCES directors (director_ID), 
 director_division_name VARCHAR(20) NOT NULL 
    REFERENCES divisions (division_name), 
 UNIQUE (director_division_name, director_ID)
);

CREATE TABLE OrgChart
(
 employee_ID INTEGER NOT NULL UNIQUE, 
 employee_department_name VARCHAR(30) NOT NULL,  
 FOREIGN KEY (employee_department_name, employee_ID)
    REFERENCES EmployeeDepartments 
       (employee_department_name, employee_ID), 
 employee_division_name VARCHAR(20) NOT NULL 
    REFERENCES divisions (division_name),
 FOREIGN KEY (employee_division_name, employee_department_name)
    REFERENCES Departments (division_name, department_name), 
 director_ID INTEGER NOT NULL, 
 director_division_name VARCHAR(20) NOT NULL,  
 FOREIGN KEY (director_division_name, director_ID)
    REFERENCES DirectorDivisions 
       (director_division_name, director_ID), 
 CHECK (employee_division_name = director_division_name)
);