假设您的组织中有分支表。其中一些是“主要”分支机构,另一些是附属于主要分支机构的卫星办公室。除了这种仅影响系统中的一些事物的区别之外,分支都是对等的并且具有相同的属性(地址等)。建模的一种方法是在表格中:
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。
答案 0 :(得分:1)
对我而言似乎是业务约束,难以在数据定义级别强制执行。我不相信关系代数有任何支持来确定自引用深度的限制。
答案 1 :(得分:1)
您是否可以在约束中引用存储过程?你可以在PostgreSQL中,所以如果2005年不允许的话,我会感到惊讶。
答案 2 :(得分:1)
您可以将检查约束绑定到UDF的返回值。创建一个UDF,将所涉及的列作为输入参数,然后使用UDF中的select检查所需的状态。
答案 3 :(得分: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。如果它为null,那么你知道它没有父母。
然后,要获得一个级别的递归,您可以将表连接到自己一次,如下所示:
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;