2011-03-26 89 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