如何跨多个表强制执行 CHECK 约束

How to enforce a CHECK constraint across multiple tables

我在 Microsoft SQL Server 2012 Express 中有一个记录奶牛繁殖信息的数据库。很明显,一头牛要到出生后才能配种,而且一生中可能配种多次;我需要在我的数据库中强制执行这些约束。我目前按照下图整理了一个schema:

DataID 是所有动物的主键。我试图实现 Table-Per-Type 继承,因此 [Animals].[Master][Animals].[Females] 之间存在一对一的关系。由于每个雌性可能会被多次繁殖,所以我在[Animals].[Females][Breedings].[Breedings]

之间建立了一对多的关系

我的问题是:我如何才能对所有 BirthDate < Breedings.Date 的女性执行规则?

我基本上需要类似下面的伪代码(我实际上已经将其放入 CHECK 约束的 "expression" 框中并收到验证错误):

[Animals].[Master].[BirthDate] < [Breedings].[Breedings].[Date]
INNER JOIN [Animals].[Master] ON
[Breedings].[Breedings].[DataID] = [Animals].[Master].[DataID]

我也尝试过使用正确的连接创建视图,但发现不能在视图中使用 CHECK 约束。

那么,有谁知道我可以如何执行这些约束?

EDIT - 我尝试了使用触发器的建议,但无法正确制定触发器语法。这是我的代码:

USE [CowInventory];
GO
CREATE TRIGGER [Breedings].[iCheckBreedingDateAfterBirthDate]
ON [Breedings].[Breedings]
FOR INSERT
AS
BEGIN
    DECLARE @CowID UniqueIdentifier
    SELECT @CowID = DataID FROM inserted;

    DECLARE @CowBirthDate Date
    SELECT @CowBirthDate = BirthDate FROM [Animals].[Master] WHERE [Master].[DataID] = @CowID

    DECLARE @BreedingDate Date
    SELECT @BreedingDate = Date FROM inserted;

    IF(@CowBirthDate > @BreedingDate)
        BEGIN
            THROW;
        END
END

根据我的一本书(SQL Server 2012 Step by Step),此语法应该可以完美运行。但是相反,SQL 服务器在 THROW 和最后一个 END 下给我粉红色的线条,说明 Incorrect syntax near 'THROW'. Expecting CONVERSATION, DIALOG, DISTRIBUTED, or TRANSACTION.Incorrect syntax near 'END'. Expecting CONVERSATION. 我已经插入了这些关键字,但它们没有任何改变.

您可以在 Breedings table 上创建触发器来检查此规则。触发器是一种特殊的存储过程,它在某些 table 上的 INSERT\UPDATE\DELETE 上自动执行。因此,您可以编写一个触发器来检查 Breedings 中插入的所有新行,如果有一行 Date 小于适当的 BirthDate,则抛出错误。与 UPDATE 相同,如果更改了 Date 列,请检查适当动物的 BirthDate 并相应地抛出错误。 DELETE 在这件事上是安全的。

CHECK 不适用于涉及其他 table 的规则。一般建议仅将它们用于一个 table 内的基本检查。

稍后编辑

试试这个触发体

...
BEGIN
 if exists
 (
  SELECT 1
  FROM inserted i
    join Animals.Females f
      on i.DataID = f.DataID
    join Animals.Master m
      on f.DataID = m.DataID
  WHERE
    m.BirthDate > i.Date

 )
 RAISERROR("Trigger iCheckBreedingDateAfterBirthDate - Breedings.Date is wrong", 18, 0)
END
GO

我不会使用触发器。使用检查约束。干净多了。请记住检查约束可以调用函数。编写一个函数来检查另一个 table。然后从检查约束调用函数。这是实现多table检查约束的最佳方式。