检查可空数据的约束,如何处理?

Jak*_*ake 5 trigger null sql-server check-constraints

这是我数据库中的表之一。为清楚起见,省略了约束。

CREATE TABLE [Person].[Person]
(
    [ID]                     INT           NOT NULL IDENTITY,       
    [Forename]               VARCHAR(16)   NOT NULL,
    [Surname]                VARCHAR(32)   NOT NULL,
    [Gender]                 CHAR(1)       NOT NULL DEFAULT 'U',
    [DateOfBirth]            DATETIME      NULL,
    [HobbiesAndInterests]    VARCHAR(256)  NULL,
    [AdditionalInformation]  VARCHAR(512)  NULL,
    [LocalCentreID]          INT           NOT NULL DEFAULT 0,
    [EmergencyContactID]     INT           NULL, 
)
Run Code Online (Sandbox Code Playgroud)

我的问题涉及出生日期字段。如果提供了值,我需要对日期进行检查。如果未提供值,则无需运行检查。

我基本上需要根据出生日期检查这个人是否年满 18 岁。如果未提供出生日期,则无需对其进行检查。

我想了几种不同的方法来解决这个问题:

第一个显然是在出生日期字段上设置检查约束,但我认为如果日期为空,这将失败。

另一种选择是在检查约束内使用标量 UDF。

我想到的最后一个选项是使用 AFTER INSERT/UPDATE 触发器来检查日期并在日期无效时回滚。

我和一位同事谈过,他说从技术上讲,我正在检查数据库中的业务规则,因此应该将这项检查推送到应用程序。

是否有“正确”的方法来解决这个问题,或者基本上是开发人员认为最好的方法?

ype*_*eᵀᴹ 8

使用CHECK约束。

CHECK当值为 null 时,约束不会失败。如果值为 null,则检查约束的条件通常评估为UNKNOWN接受该行。只有当条件评估为 时FALSE,该行才会被拒绝。

另一种选择是在检查约束内使用标量 UDF。

不不不。用户定义的函数在CHECK约束内是允许的,但有几个问题,通常是因为它们用于实现复杂的行为/约束,比如搜索整个其他表。在您的情况下,UDF 不会执行标准日期函数无法完成的任何操作。所以不要使用UDF。

我想到的最后一个选项是使用AFTER INSERT/UPDATE触发器来检查日期并在日期无效时回滚。

你可以,但CHECK约束更简单。触发器的测试要困难得多。

我和一位同事谈过,他说从技术上讲,我正在检查数据库中的业务规则,因此应该将这项检查推送到应用程序。

关于业务规则应该在数据库中还是在应用程序中,这是一个相当“热门”的争论。我发现“应用程序中的所有业务规则”的方法有点幼稚。例如,主键和外键约束实现业务规则,我们是否也应该从我们的数据库中取消它们?使业务规则更接近数据库的另一个原因是我们可能有多个应用程序使用它。然后,我们发现在数据库中一次又一次地在可能不同的语言/框架中实现相同的规则,而它本来可以完成一次。

尽管特定约束存在一个问题,请参见下文*


您的情况可能如下所示。在reextester.com 上测试:

CREATE TABLE  person
( person_id INT NOT NULL PRIMARY KEY,
  birthdate DATE NULL,
  CONSTRAINT is_adult 
    CHECK (birthdate <= DATEADD(year, -18, GETDATE()))
) ;
Run Code Online (Sandbox Code Playgroud)

插入空和有效日期成功:

INSERT INTO person 
    (person_id, birthdate)
VALUES
    (1, '19500101'),
    (2, NULL) ;

SELECT * FROM person ;

person_id  birthdate
1          01.01.1950
2          NULL
Run Code Online (Sandbox Code Playgroud)

但无效日期失败:

INSERT INTO person 
    (person_id, birthdate)
VALUES
    (3, '20010101') ;
Run Code Online (Sandbox Code Playgroud)

INSERT 语句与 CHECK 约束“is_adult”冲突。冲突发生在数据库“rextester”、表“dbo.person”、“生日”列中。


*:这个特殊情况/约束的一个问题 - 而不是因为该列可以为空:使用的函数之一 - GETDATE()- 不是确定性的。如果它在一天或一年后运行,它不会——显然——产生相同的值。您不太可能遇到问题,因为一旦超过 18 岁,就会一直超过 18 岁。年龄会随着时间的推移而增加。

奇怪的场景:有人更改了运行数据库的系统的日期并将其放入,比如 1986 年。突然之前有效的行变得无效。那会发生什么?(坦率地说,我不知道,必须测试一下!)

考虑上面,它可能只有通过更适合来处理这个约束以程序的方式,无论是在应用程序或通过处理表INSERT,并UPDATE认为强制约束程序。