2009-01-08 23 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