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 条件非空约束的主要内容,如果未能解决你的问题,请参考以下文章