1

假设您的组织中有一个分支表。其中一些是“主要”分支机构,而另一些则是延伸到主要分支机构的卫星办公室。除了这种只影响系统中一些事情的区别之外,分支都是对等的并且具有相同的属性(地址等)。对此建模的一种方法是在如下表中:

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)
)

Where 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。

4

4 回答 4

1

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

于 2009-01-08T16:35:27.467 回答
1

不允许在约束中引用存储过程吗?你可以在 PostgreSQL 中,所以如果 2005 年不允许这样做,我会感到惊讶。

于 2009-01-08T17:01:26.680 回答
1

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

于 2009-01-08T17:33:01.453 回答
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;
于 2009-01-08T16:37:34.933 回答