2009-01-08 35 views
1

假设您的组织中有分支机构的表格。其中一些是“主要”分支机构,另一些则是分支机构分支机构。除了这种仅影响系统中几件事情的区别之外,分支都是同伴并具有相同的属性(地址等)。模型的一种方式是像一个表:如何在SQL表中最好地实施单级递归?

CREATE TABLE Branch (
    branch_id INT NOT NULL PRIMARY KEY IDENTITY(1,1), 
    branch_name VARCHAR(80) NOT NULL, 
    street VARCHAR(80) NULL, 
    city VARCHAR(30) NULL, 
    state CHAR(2) NULL, 
    zip CHAR(5) NULL, 
    is_satellite_office BIT NOT NULL DEFAULT(0), 
    satellite_to_branch_id INT NULL REFERENCES Branch(branch_id) 
) 

其中is_satellite_office = 1当且仅当该记录是一个卫星到另一个分支,satellite_to_branch_id是指你是哪个部门的卫星,如果有的话。

这是很容易把约束放在桌子上,使那些两列在任何给定的记录一致认为:

CONSTRAINT [CK_Branch] CHECK 
    (
    (is_satellite_office = 0 AND satellite_to_branch_id IS NULL) 
    OR (is_satellite_office = 1 AND satellite_to_branch_id IS NOT NULL) 
) 

不过,我真正想要的是一种方法来保证这个递归唯一无二一个级别...也就是说,如果我指向一个分支作为我的父级,它不能有父级本身,并且它的值为is_satellite_office必须为0.换句话说,我并不真的需要一个完全递归树结构,我只是想限制它到一个单一的父母/子女关系。这就是我要写代码的方式,如果有一种方法可以在数据库中强制执行,而不会像完全废话一样执行,我想。

任何想法?我正在开发MSSQL 2005,但一般(非供应商特定的)解决方案是首选。除非确实没有其他方法可以实现,否则不需要触发器。

编辑:要清楚,satellite_to_branch_id是递归指针指向同一分支表中的另一条记录。我知道我可以删除is_satellite_office BIT并依靠IsNull(satellite_to_branch_id)给我提供相同的信息,但我发现它更清晰一点,除此之外,这不是问题的要点。我真的想找一个纯粹的SQL约束的方式来防止大于1

回答

1

可以绑定检查约束的UDF的返回值。创建一个将所涉及的列作为输入参数的UDF,然后使用UDF中的选择来检查所需的状态。

1

递归深度在我看来,就像一个企业的约束,难以在数据定义级别执行。我不相信关系代数有任何支持来确定自引用深度的限制。

+0

+1。那种我害怕的东西,但我希望有人有一个很酷的伎俩来做到这一点。 – 2009-01-08 16:36:21

+0

我会好奇的看看是否有人提出纯粹的数据定义约束,如果解决方案存在水,我会删除我的答案。 – 2009-01-08 16:39:09

0

这个略有不同的结构呢?

CREATE TABLE Branch (
    branch_id INT NOT NULL PRIMARY KEY IDENTITY(1,1), 
    branch_name VARCHAR(80) NOT NULL, 
    street VARCHAR(80) NULL, 
    city VARCHAR(30) NULL, 
    state CHAR(2) NULL, 
    zip CHAR(5) NULL, 
    parent_id int NULL 
) 

PARENT_ID将简单地指向同一个表中另一条记录的BRANCH_ID。如果它是空的,那么你知道它没有父母。

然后,得到递归的一个级别,你可以加入表本身的一次,是这样的:

SELECT 
    PARENT.BRANCH_NAME AS PARENT_BRANCH 
,CHILD.BRANCH_NAME AS CHILD_BRANCH 
FROM 
    BRANCH PARENT 
,BRANCH CHILD 
WHERE CHILD.PARENT_ID PARENT.BRANCH_ID 

如果要强制执行你的树深度的水平,使一个片插入/更新触发器,如果​​此查询返回任何内容,将触发异常。

SELECT * 
FROM 
    BRANCH B1 
,BRANCH B2 
,BRANCH B3 
WHERE B1.PARENT_ID = :NEW.NEW_PARENT_ID 
    AND B2.PARENT_ID = B1.BRANCH_ID 
    AND B2.PARENT_ID = B3.BRANCH_ID; 
+0

对不起,如果我的描述不清楚;这正是它现在正在做的,明智的(我只是把它命名为“parent_id”以外的东西)。问题在于如何在SQL中强制执行单一深度递归,理想情况下没有触发器。但感谢触发解决方案! – 2009-01-08 16:43:23

1

您是否允许在约束中引用存储过程?你可以在PostgreSQL中,所以如果2005年不允许的话,我会感到惊讶。

+0

这应该不会对性能造成太大影响。毕竟,你的SP最多只会在一次迭代后保释,你多久会插入/更新分支? (如果每天超过一次,请告诉我,以便我可以购买股票。) – Kev 2009-01-08 17:05:39