如何对外键引用字段设置数据库完整性检查

发布于 2024-10-27 00:56:56 字数 1077 浏览 2 评论 0原文

我有四个这样的数据库表:

预订
ID_Book |ID_Company|说明

图书扩展
ID_BookExtension | ID_书| ID_折扣

折扣
ID_折扣|描述 | ID_公司

公司
ID_公司 |说明

任何通过外键的 BookExtension 记录都间接指向两个不同的 ID_Company 字段:

BookExtension.ID_Book 引用包含 Book.ID_Company 的 Book 记录
BookExtension.ID_Discount 引用包含 Discount.ID_Company 的 Discount 记录

是否可以在 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 记录和另一个 Discount 记录不同的公司

I have four Database Tables like these:

Book
ID_Book |ID_Company|Description

BookExtension
ID_BookExtension | ID_Book| ID_Discount

Discount
ID_Discount | 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!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

沐歌 2024-11-03 00:56:56

除非我误解了您的意图,否则您将使用的 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 上创建 UNIQUE 约束
    (ID_图书、ID_公司)。它的一部分看起来像UNIQUE (ID_Book, ID_Company)
  2. 对 Discount (ID_Discount, ID_Company) 创建 UNIQUE 约束。
  3. 添加两列
    图书扩展--Book_ID_Company 和
    折扣_ID_公司。
  4. 填充这些新列。
  5. 更改外键约束
    在 BookExtension 中。你想要
    图书扩展名(ID_Book,
    Book_ID_Company) 参考
    书籍(ID_Book、ID_Company)。外键的类似更改
    参考折扣。

现在您可以添加检查约束来保证 BookExtension.Book_ID_Company 与 BookExtension.Discount_ID_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.

唯憾梦倾城 2024-11-03 00:56:56

我不确定这有多高效,但您也可以使用索引视图来实现这一点。它需要一个包含 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)   

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)   
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文