开发者

How to set a database integrity check on foreign keys referenced fields

I have four Database Tables like these:

Book

ID_Book |ID_Company|Description

BookExtension

ID_BookExtension | ID_Book| ID_Discount

Discount

ID_Discount开发者_JAVA百科 | Description | ID_Company

Company

ID_Company | Description

Any BookExtension record via foreign keys points indirectly to two different ID_Company fields:

BookExtension.ID_Book references a Book record that contains a Book.ID_Company

BookExtension.ID_Discount references a Discount record that contains a Discount.ID_Company

Is it possible to enforce in Sql Server that any new record in BookExtension must have Book.ID_Company = Discount.ID_Company ?

In a nutshell I want that the following Query must return 0 record!

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

or, in plain English:

I don't want that a BookExtension record references a Book record of a Company and a Discount record of another different Company!


Unless I've misunderstood your intent, the general form of the SQL statement you'd use is

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

That assumes existing data already conforms to the new constraint. If existing data doesn't conform, you can either fix the data (assuming it needs fixing), or you can limit the scope (probably) of the new constraint by also checking the value of ID_FooExtension. (Assuming you can identify "new" rows by the value of ID_FooExtension.)

Later . . .

Thanks, I did indeed misunderstand your situation.

As far as I know, you can't enforce that constraint the way you want to in SQL Server, because it doesn't allow SELECT queries within a CHECK constraint. (I might be wrong about that in SQL Server 2008.) A common workaround is to wrap a SELECT query in a function, and call the function, but that's not reliable according to what I've learned.

You can do this, though.

  1. Create a UNIQUE constraint on Book (ID_Book, ID_Company). Part of it will look like UNIQUE (ID_Book, ID_Company).
  2. Create a UNIQUE constraint on Discount (ID_Discount, ID_Company).
  3. Add two columns to BookExtension--Book_ID_Company and Discount_ID_Company.
  4. Populate those new columns.
  5. Change the foreign key constraints in BookExtension. You want BookExtension (ID_Book, Book_ID_Company) to reference Book (ID_Book, ID_Company). Similar change for the foreign key
    referencing Discount.

Now you can add a check constraint to guarantee that BookExtension.Book_ID_Company is the same as BookExtension.Discount_ID_Company.


I'm not sure how [in]efficient this would be but you could also use an indexed view to achieve this. It needs a helper table with 2 rows as CTEs and UNION are not allowed in indexed views.

CREATE TABLE dbo.TwoNums
 (
 Num int primary key
 )

 INSERT INTO TwoNums SELECT 1 UNION ALL SELECT 2

Then the view definition

 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

And a unique index on the View

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

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜