关系表设计:防止循环引用

时间:2014-02-06 08:48:42

标签: mysql sql database-design relational-database

我有一个问题,我不知道如何正确解决。我目前的设计包括两个表格。员工和员工管理。我的RDBMS是MySQL。

雇员:

CREATE TABLE `employee` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci

employee_management:

CREATE TABLE `employee_management` (
  `manager_id` int(11) NOT NULL,
  `employee_id` int(11) NOT NULL,
   UNIQUE INDEX `association` (`manager_id`,`employee_id`),
   FOREIGN KEY (`manager_id`)
   REFERENCES employee(id)
   ON UPDATE CASCADE ON DELETE RESTRICT,
   FOREIGN KEY  (`employee_id`)
   REFERENCES employee(id)
   ON UPDATE CASCADE ON DELETE RESTRICT
) 

测试数据:

INSERT INTO employee(name) VALUES(
  'Bob Smith'
)
INSERT INTO employee(name) VALUES(
  'Bill Smith'
)

INSERT INTO employee_management(manager_id, employee_id) VALUES(
  1,2
)
INSERT INTO employee_management(manager_id, employee_id) VALUES(
  2,1
)

从employee_management中选择行显示:

+------------+-------------+
| manager_id | employee_id |
+------------+-------------+
|          2 |           1 |
|          1 |           2 |
+------------+-------------+

返回的行表明Bill Smith负责管理Bob Smith,Bob Smith负责管理Bill Smith,我认为这是一个循环引用。两个人互相管理是没有意义的。我认为UNIQUE INDEX会阻止插入具有任何现有值组合的行,但这不起作用。我知道我可以防止这种情况发生在应用程序级别,但我不确定这是否适合做,但是这是必须在应用程序级别强制实施的,或者我可以做些什么来做阻止循环引用?经理不应该也有经理。

2 个答案:

答案 0 :(得分:1)

这种复杂性的主要原因是对声明这些约束的支持不足 DBMS。

您可以装饰性地说明两种类型的表约束:

  • 唯一标识属性(键)
  • 子集要求引用回同一个表,在这种情况下 子集需求是表约束(相同的外键) 表)。

实现所有其他类型的表约束需要您开发过程 数据完整性代码。实际上,这意味着您通常不得不在应用程序的业务层中采用触发的程序策略或实现。

thisthis post之后,您可以使用触发器来检查下面的查询是否超过0,然后回滚更改

SELECT count(*) FROM employee_management e1
            WHERE EXISTS (SELECT * FROM employee_management  e2
            WHERE
             e1.manager_id = e2.employee_id
             AND
            e1.employee_id = e2.manager_id )

作为脚注,您可能会有一个从员工到员工的外键,显示员工的经理

答案 1 :(得分:0)

我需要一个触发器才能在SQL Server中完成此操作 - 如果它可以帮助某人解决同样的问题,那么这就是触发器定义。

CREATE TRIGGER iu_erp_user
ON [dbo].[erp_user]
FOR INSERT, UPDATE
AS
BEGIN
SET NOCOUNT ON

declare @erp_user_id int
declare @supervisor_supervisor_id int

select @erp_user_id = i.erp_user_id, @supervisor_supervisor_id = eu.supervisor_id
from inserted i
left join erp_user eu on i.supervisor_id = eu.erp_user_id

if @erp_user_id = @supervisor_supervisor_id
begin
    RAISERROR('Circular reference created.', 16, 1)
    ROLLBACK TRAN
end

END
GO