2011-03-26 62 views
0

我有四個數據庫表像這樣:如何設置外鍵數據庫完整性檢查引用的字段


ID_Book | ID_Company |說明

BookExtension
ID_BookExtension | ID_Book | ID_Discount

折扣
ID_Discount |說明| ID_公司

公司
ID_Company |說明

通過外鍵的任何BookExtension記錄指向間接兩種不同的ID_Company領域:

BookExtension.ID_Book引用包含Book.ID_Company
BookExtension.ID_Discount一書記錄引用一個折扣記錄中包含Discount.ID_公司

是否有可能在Sql Server中執行任何新記錄BookExtension必須有Book.ID_Company = Discount.ID_Company

簡而言之,我希望以下查詢必須返回0記錄!

SELECT count(*) from BookExtension 
INNER JOIN Book ON BookExstension.ID_Book = Book.ID_Book 
INNER JOIN Discount ON BookExstension.ID_Discount = Discount.ID_Discount 
WHERE Book.ID_Company <> Discount.ID_Company 

,或者用簡單的英語:
我不希望這樣一個BookExtension記錄引用一個CompanyBook記錄和另一個不同Company一個Discount紀錄!

+0

你說「任何新的記錄」的獨特索引 - 這是否限制也適用於已經存在的記錄? – 2011-03-26 21:23:30

+0

@Martin現有記錄很好,因爲一旦將新的ID_Discount字段添加到BookExtension中,所有記錄和相應的ID_Discount值將按照嚴格的規則進行批量更新。該表現在由Web應用程序提供,雖然我可以在插入新記錄之前從應用程序中進行雙重檢查,但我希望使用數據庫規則強制實施此限制。 – systempuntoout 2011-03-26 21:30:33

回答

2

除非我誤解你的意圖,你會使用SQL語句的一般形式是

ALTER TABLE FooExtension 
ADD CONSTRAINT your-constraint-name 
CHECK (ID_Foo = ID_Bar); 

,它假定現有數據已符合新的約束。如果現有數據不符合,則可以修復數據(假定需要修復),也可以通過檢查ID_FooExtension的值來限制新約束的範圍(可能)。 (假設您可以通過ID_FooExtension的值識別「新」行)。

後來。 。 。

謝謝,我確實誤解了你的情況。

據我所知,你不能在SQL Server中以你想要的方式實施這個約束,因爲它不允許CHECK約束中的SELECT查詢。 (我在SQL Server 2008中可能是錯的。)常見的解決方法是將SELECT查詢包裝在一個函數中,然後調用該函數,但根據我所瞭解的情況,這不是可靠的。

可以這樣做,但。

  1. 在Book (ID_Book,ID_Company)上創建一個UNIQUE約束。其中一部分看起來像UNIQUE (ID_Book, ID_Company)
  2. 在折扣(ID_Discount,ID_Company)上創建唯一約束。
  3. 將兩列添加到 BookExtension - Book_ID_Company和 Discount_ID_Company。
  4. 填充這些新列。
  5. 更改BookExtension中的外鍵約束 。您想要 BookExtension(ID_Book, Book_ID_Company)引用 Book(ID_Book,ID_Company)。引用折扣的外鍵
    的類似更改。

現在你可以添加一個檢查約束,以保證BookExtension.Book_ID_Company相同BookExtension.Discount_ID_Company。

+0

請參閱我的編輯。 – systempuntoout 2011-03-26 17:07:17

+0

您是否感覺將字符串「Foo」更改爲「Book」,將字符串「Bar」更改爲「Discount」會產生完全不同的問題?它沒有。 – 2011-03-26 18:32:29

+0

@Cat是'ID_Foo = ID_Bar'關於如何創建一個約束的一個通用的例子(foo,bar)還是它特定於我的問題?我改變了表名來理解它。 – systempuntoout 2011-03-26 20:02:06

2

我不知道這將是多高效,但你也可以使用索引視圖來實現這一點。它需要一個具有2行的幫助表作爲CTE,並且索引視圖中不允許使用UNION

CREATE TABLE dbo.TwoNums 
(
Num int primary key 
) 

INSERT INTO TwoNums SELECT 1 UNION ALL SELECT 2 

然後視圖定義

CREATE VIEW dbo.ConstraintView 
WITH SCHEMABINDING 
AS 
    SELECT 1 AS Col FROM dbo.BookExtension 
    INNER JOIN dbo.Book ON dbo.BookExtension.ID_Book = Book.ID_Book 
    INNER JOIN dbo.Discount ON dbo.BookExtension.ID_Discount = Discount.ID_Discount 
    INNER JOIN dbo.TwoNums ON Num = Num 
    WHERE dbo.Book.ID_Company <> dbo.Discount.ID_Company 

並且在這個視圖

CREATE UNIQUE CLUSTERED INDEX [uix] ON [dbo].[ConstraintView]([Col] ASC) 
+0

有趣的技術 – systempuntoout 2011-03-28 21:37:39

+1

'INNER JOIN dbo.TwoNums ON Num = Num' is effective'CROSS JOIN dbo.TwoNums'。交叉連接在索引視圖中是否被禁止(語法上)? – 2011-03-29 06:27:58

+0

@Andriy M - 我當時懶得檢查!沒有'OUTER JOIN'和自連接只是'CROSS JOIN' * *被允許。 – 2011-03-29 11:38:07

相關問題