Sql 条件非空约束

Posted

技术标签:

【中文标题】Sql 条件非空约束【英文标题】:Sql Conditional Not Null Constraint 【发布时间】:2012-05-03 15:55:33 【问题描述】:

我很想知道是否可以在 sql 中创建条件非空约束?换句话说,是否可以创建一个约束,使得 B 列可以为空,只要 A 列包含让我们说“新”,但如果 A 列的内容更改为其他内容,那么 B 列不再允许为空? 为了扩展这一点,只要 A 列显示“新”,就有可能使 B 列必须为空或为空? 谢谢大家:D

【问题讨论】:

FWIW,我认为大多数数据库设计人员不会将此称为条件约束。 我会 可能只是将其称为约束,但如果我必须对其进行更多限定,我可能会将其称为多列约束或多列 CHECK() 约束。这是标准 SQL。 @Catcall: Chris Date 引用:“这样的约束有时被非正式地称为元组约束或 SQL 中的行约束——尽管后一个术语也用于 SQL 中,更具体地说,不能被表述为列约束的行约束......然而,所有这样的用法都被弃用了,因为约束约束更新并且......在关系世界中没有元组或行级别更新这样的东西。“ @onedaywhen:我没有当前版本。在第 7 版中,我在这里看到的(除了 NULL)看起来像他所说的 relvar 约束(p253)。它可以容纳多个列,他说它可以任意复杂(我想这意味着它可以引用其他关系、视图、聚合和月相)。 @Catcall: SQL and Relational Theory: How to Write Accurate SQL Code 在那段中,他还说,“特别注意,可以通过单独检查该元组来检查给定的供应商元组——无需查看relvar 或数据库中的任何其他 relvar。” (听起来是件好事。)我认为“所有此类用法”是指“不能被表述为列约束的行约束”,而不是引用其他列的列约束.但我可能是错的。 【参考方案1】:

这对于 CONSTRAINT CHECK 来说非常合适。只需这样做:

要求:

是否可以创建一个约束,使得 B 列可以为空 只要 A 列包含让我们说“新”,但如果列的内容 A 更改为其他内容,然后不再允许列 B 空?

注意这句话:B列可以为空

解决方案:

create table tbl
(
    A varchar(10) not null,
    B varchar(10),

    constraint uk_tbl check
    (
      A = 'NEW' -- B can be null or not null: no need to add AND here
      OR (A <> 'NEW' AND B IS NOT NULL)
    )
);

你可以进一步简化:

create table tbl
(
    A varchar(10) not null,
    B varchar(10),

    constraint uk_tbl check
    (
      A = 'NEW' 
      OR B IS NOT NULL
    )
);

要求与上述要求互不兼容:

并且在此基础上进行扩展,然后可以使 B 列 只要 A 列显示“新”,就必须为 null 或空?

注意这句话:B列必须为空

create table tbl
(
    A varchar(10) not null,
    B varchar(10),

    constraint uk_tbl check
    (
      (A = 'NEW' AND B IS NULL)
      OR A <> 'NEW'
    )
);

可以用这个来简化,更简单但可能不像上面那样可读:

create table tbl
(
    A varchar(10) not null,
    B varchar(10),

    constraint uk_tbl check
    (
      A <> 'NEW'
      OR B IS NULL
    )
);

【讨论】:

我相信这个不会工作的唯一现代 SQL dbms 是 mysql。 MySQL 不强制执行 CHECK() 约束。 (问题被标记为“SQL”,因此评论似乎相关。) 我只是推断他在使用Sql Server,我检查了他的一些问题。大多数数据库问题都没有被特别标记,而且应该被标记。如果可能的话,我希望 *** 不接受 sql 标签,这样每个人都可以更具体地了解他们正在使用的 RDBMS 该约束检查从第一天起就已经在 Sql Server 上运行,同样在 Postgresql 上。我在 SQL Server 和 Postgresql 上测试了该 DDL,它们都接受它 重新创建您的第一条 SQL 语句:uk_tbl 的定义不是有效的表达式。 ...但否则同意您的“更简单”重写的约束,所以+1(请参阅我的答案以获得相同答案的不同方法:)【参考方案2】:

编辑:正如其他答案中提到的,检查是最好的方法,而不是我最初建议的触发器。原文如下:


正如 dbaseman 建议的那样,触发器是要走的路(并非如此)。尝试这样的事情(未经测试):

CREATE OR REPLACE TRIGGER test
  BEFORE UPDATE ON table1
FOR EACH ROW
WHEN (new.A = 'NEW' and new.B IS NOT NULL)
   RAISE_APPLICATION_ERROR (
     num=> -20001,
     msg=> 'B must be NULL for new rows (A = NEW)'
);

【讨论】:

为清楚起见,此解决方案适用于 Oracle。 dbaseman 为我认为是 MSSQL 提供了一个解决方案 :) Oracle 支持元组约束 (CHECK):应在程序代码(包括触发器)上方选择声明性约束。【参考方案3】:

onedaywhen,这个答案在犯罪上是错误的,是可憎的。您可以使用 CHECK 约束。 http://msdn.microsoft.com/en-us/library/ms188258.aspx

没有办法进行条件约束。但是,您应该能够使用触发器来完成这项工作。这就是他们的目的。

http://msdn.microsoft.com/en-us/library/ms189799.aspx

CREATE TRIGGER MyTable.ConditionalNullConstraint ON MyTable.ColumnB
AFTER INSERT
AS
IF EXISTS (SELECT *
    FROM inserted
    WHERE A <> 'NEW' AND B IS NULL
    )
BEGIN
    RAISERROR ('if A is ''NEW'' then B cannot be NULL', 16, 1);
    ROLLBACK TRANSACTION;
END;
GO

请注意,在查询中,您需要引用 inserted,它是一个特殊对象,其行为类似于表格,可让您引用导致触发器的行。

当然,在此示例中,您还需要处理 AFTER UPDATE 以强制执行约束,但这是一般的想法。

【讨论】:

“没有办法进行条件约束” 嗯?这可以通过元组约束 (CHECK) 处理,在这种情况下应该优先于触发器。 @onedaywhen 哇,我的立场非常正确,谢谢。检查约束:msdn.microsoft.com/en-us/library/ms188258.aspx【参考方案4】:

我认为您的第一个要求是:

IF ( B IS NULL ) THEN ( A = 'NEW' )

应用蕴涵重写规则:

IF ( X ) THEN ( Y )   <=>   ( NOT ( X ) OR ( Y ) )

在你的情况下;

( NOT ( B IS NULL ) OR ( A = 'NEW' ) )

小幅重写以利用 SQL 语法:

( B IS NOT NULL OR A = 'NEW' )

您的第二个陈述(“扩展”)要求:

IF ( A = 'NEW' ) THEN ( B IS NULL )

应用重写规则:

( NOT ( A = 'NEW' ) OR ( B IS NULL ) )

轻微改写:

( A <> 'NEW' OR B IS NULL )

【讨论】:

以上是关于Sql 条件非空约束的主要内容,如果未能解决你的问题,请参考以下文章

SQL 怎么添加非空约束?

怎样用SQL语句对指定字段建立非空约束?

非空约束对数据插入的影响

非空约束对数据更新的影响

SQL - 创建一个学生表,要求有主键约束和非空约束

Sql约束(MySQL)