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

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。

有帮助吗?

解决方案

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

其他提示

对我而言似乎是业务约束,难以在数据定义级别强制执行。我不相信关系代数有任何支持来确定自引用深度的限制。

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

这个稍微不同的结构怎么样?

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;
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top