SQLServer - 约束
时间:2022-03-14 09:58
目标
SQL
限制Month列为合适的数字
BETWEEN 1 AND 12
正确的SSN格式
LIKE‘[0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9][0-9][0-9]‘
限制为一个快递公司的特定列表
IN(‘UPS‘,‘Fed Ex‘,EMS‘)
价格必须为正数
UnitPrice >= 0
引用同一行中的另外一列
ShipDate >= OrderDate
上面给出的列表只是一小部分,而条件实际上市无限多的。几乎所有可以放到where子句的条件都可以放到该约束中。而且和其他选择(规则和触发器)相比,CHECK约束执行速度更快。
在已存在的表中添加一个CHECK约束:
alter table Account add constraint CN_AccountAge check (Account_Age > 18); -- 插入年龄必须大于18
如果此时视图添加一条不满足的记录,将报如下错误:
insert into Account values (22,‘洪‘,17)
消息 547,级别 16,状态 0,第 1 行 INSERT 语句与 CHECK 约束"CN_AccountAge"冲突。该冲突发生于数据库"Nx",表"dbo.Account", column ‘Account_Age‘。 语句已终止。
5、DEFAULT约束
和所有约束一样,DEFAULT约束也是表定义的一个组成部分,它定义了当插入的新行对于定义了默认约束的列未提供相应数据时该怎么办。可以定义它为一个字面值(例如,设置默认薪水为0,或者设置字符串列为"UNKNOWN"),或者某个系统值(getdate())。
对于DEFAULT约束,要了解以下几个特性:
1、默认值只在insert语句中使用-在update语句和delete语句中被忽略。
2、如果在insert语句中提供了任意值,那就不使用默认值。
3、如果没有提供值,那么总是使用默认值。
值得注意的是,update命令的规则由一个例外,如果显示说明使用默认值就是例外。可以通过使用关键字DEFAULT表示更新的值设置为默认值。
5.1在创建表时定义DEFAULT约束:
create table person ( person_id int identity not null primary key, person_name nvarchar(30) not null default ‘无名氏‘, person_age int not null )
在执行语句后:
insert into person (person_age) values(24)
表中被插入一条记录如下:
5.2在已存在的表上添加DEFAULT约束:
alter table person add constraint CN_DefaultName default ‘无名氏‘ for person_name
6、禁用约束
有时我们想暂时或永久地消除约束。但是SQL Server并没有提供删除约束的方法。SQL Server只允许禁用外键约束或CHECK约束,而同时保持约束的完整性。
禁用一个数据完整性规则通常是因为已经有无效数据了。这样的数据通常分为以下两类:
1、在创建约束时已经在数据库中的数据
2、在约束创建以后希望添加的数据
SQL Server允许禁用完整性检查一段时间来对例外的无效数据作处理,然后再重新启用完整性(不是物理删除数据完整性约束)。
注意:不能禁用主键约束或者唯一约束
6.1、在创建约束时,忽略检查之前的不满足数据
要添加一个约束,但是有不应用到已存在的数据中,可以再执行Alter Table语句添加约束时使用WITH NOCHECK选项。
按照上面创建Check约束的方法,已经Alter Table时,表中本身已经存在不符合的数据,那么Alter Table操作将被SQL Server拒绝执行。除非已经存在的所有数据都满足CHECK约束的条件,否则SQL Server不会执行创建约束的命令。要解决这个问题,我们可以添加WITH NOCHECK。
我们先新建一个表只有3个字段的表,Id、姓名、年龄,并在里面插入一条不满足要求的数据:
insert into Account values (23,‘洪‘,17)
然后执行添加约束命令:
alter table Account add constraint CN_AccountAge18 check (Account_Age > 18); -- 插入年龄必须大于18
SQL Server报一下错误:
消息 547,级别 16,状态 0,第 1 行 ALTER TABLE 语句与 CHECK 约束"CN_AccountAge18"冲突。该冲突发生于数据库"Nx",表"dbo.Account", column ‘Account_Age‘。
这时候我们换一种方式去执行:
alter table Account WITH NOCHECK add constraint CN_AccountAge18 check (Account_Age > 18); -- 插入年龄必须大于18
以上代码就能够成功执行,并且只有以后添加的数据具备约束,之前添加的不符合条件的数据记录依然存在。
6.2临时禁用已存在的约束
当我们需要从另一数据库中导入数据到表中,而表中已建立了约束的时候,可能会存在一些数据和规则不匹配。当然有一个解决方式是先删除约束,添加需要的数据,然后WITH NOCHECK在添加回去。但是这样做太麻烦了。我们不需要这么做。我们可以采用名为NOCHECK的选项来运行ALTER语句,这样就能够取消需要的约束。
先来看看上节中创建的这个约束:
alter table Account add constraint CN_AccountAge18 check (Account_Age > 18); -- 插入年龄必须大于18
要取消以上约束可以这样来:
Alter Table Account NOCHECK constraint CN_AccountAge18
执行命令:
insert into Account values (25,‘取消了约束‘,17)
执行成功,成功添加了一行数据。
留意到又能够向表中插入格式不匹配的数据了。
这里要说明下,如下知道一个约束是否是启用还是禁用呢?sp_helpconstraint命令,当我们执行sp_helpconstraint的时候,会有一列status_enabled显示该约束的启用状态:
sp_helpconstraint Account
留意到status_enabled列为Disabled说明是禁用的意思。
当要启用约束时,只需要用将语句中的NO CHECK替换为CHECK就可以了:
Alter Table Account CHECK constraint CN_AccountAge18
执行之后,约束又启用了:再来sp_helpconstraint看下:
留意到status_enabled列变成了Enabled。
status_enabled的两种状态如下:
Enabled:启用;
Disabled:禁用;
7、规则和默认值
规则和默认值的应用要早于CHECK和DEFAULT约束。他们是较老的SQL Server备用约束的一部分,当然也不是没有优点。自7.0版本之后,MicroSoft列出规则和默认值只是为了向后兼容,而不准备在以后继续支持这个特性。因此对于生成新代码时,应该使用约束。
规则、默认值与约束的本质区别是:约束是一个表的特征,本身没有存在形式,而规则和默认值是表和自身的实际对象,本身存在。约束是在表定义中定义的,而规则和默认值是单独定义,然后"绑定到"表上。
规则和默认值的独立对象特性使得它们可以在重用时不用重新定义。实际上,规则和默认值不限于被绑定到表上,它们也可以绑定到数据类型上。
7.1规则
规则和CHECK约束非常相似。它们之间的唯一区别是规则每次只能作用于一个列。可以将同一规则分别绑定到一个表中的多个列,但是规则分别作用于每个列,根本不会意识到其他列的存在。像QtyShipped
<= QtyOrdered这样的约束不适用于规则(它引用多个列),而LIKE([0-9][0-9][0-9])这样的定义适用于规则。
定义规则:
下面定义一个规则,这样就可以首先看到区别所在:
CREATE RULE Age18Rule AS @Age > 18
这里比较的是一个变量,不管被检查的列是什么值,这个值将用于替换@Age。因此在这个示例中,规则所绑定的任何列的值都必须大于18。
到目前为止,只是创建了一个规则,但这个规则还没对任何表的任何列起作用,要激活这个规则需要使用一个存储过程:sp_bindrule。
将规则Age18绑定到表person的person_age列:
EXEC sp_bindrule ‘Age18Rule‘,‘person.person_age‘;
此时,如果我们执行不满足规则的插入操作:
insert into person values (‘绑定规则‘,17)
将返回如下报错信息:
消息 513,级别 16,状态 0,第 1 行 列的插入或更新与先前的 CREATE RULE 语句所指定的规则发生冲突。该语句已终止。冲突发生于数据库 ‘Nx‘,表 ‘dbo.person‘,列 ‘person_age‘。 语句已终止。
很明显,规则已经生效。
要特别注意的是,在绑定之前,规则与任何表,任何列都没有关系,因此在绑定的时候,第二个参数要加.指定表名与列名(tablename.column)。
解除绑定规则:
当我们需要在一个列上解除绑定规则的时候,只要执行sp_unbindrule
删除刚才绑定的规则:
EXEC sp_unbindrule ‘person.person_age‘;
这时候,执行刚才的插入操作,就不会报错了。
删除规则:
如果希望将规则从数据库中彻底删除,那么可以在表中使用非常熟悉的DROP语法。
DROP RULE <rule name>
如删除刚才创建的那条规则:
DROP RULE Age18Rule
7.2默认值
默认值类似于DEFAULT。实际上默认值-DEFAULT约束的关系与规则-CHECK约束的关系差不多。区别在于它们被追加到表中的方式和对用户自定义数据类型的默认值(是对象,而不是约束)支持。
定义默认值的语法和定义规则类似:
CREATE DEFAULT <default_name> AS <default value>
创建默认值:
因此,假设要为Age定义一个值为0的默认值:
CREATE DEFAULT AgeDefault AS 0
绑定默认值:
同样,如果不绑定到一个对象上,则默认值是不起作用的。要绑定的话,使用存储过程sp_bindefault。
EXEC sp_bindefault ‘AgeDefault‘,‘person.person_age‘;
要从表中解决默认值的绑定,使用sp_unbindefault:
sp_unbindefault ‘person.person_age‘;
删除默认值:
如果要从数据库中彻底删除一个默认值,则可以使用DROP语法,与删除规则相同:
DROP DEFAULT AgeDefault
7.3确定哪个表和数据类型使用给定的规则或默认值
如果希望删除或者修改规则或默认值。那么您可以先看看哪些表和数据类型在使用它们。SQL Server还是采用系统存储过程解决这个问题。这个存储过程是sp_depends。其语法如下所示:
EXEC sp_depends <object name>
sp_depends提供了依赖于你所查询对象的所有对象列表。
8、触发器
触发器也能够用于实现数据完整性,这个内容比较多,新建一篇文章叙述。
9、如何选择
经过以上的学习,对于数据完整性,你会发现有很多种可以选择,那么如何挑选合适的约束呢?
限制 | 优点 | 缺点 |
约束 |
快速 可以引用其他列 在命令执行前发生 遵循ANSI标准 |
必须对每个表重新定义 不能引用其他表 不能绑定到数据类型 |
规则 |
独立的对象 可重用 可以绑定到数据类型 命令执行前发生 |
稍慢 不能跨列使用 不能引用其他表 实际上只用于向后兼容 |
默认值 |
非常灵活 可以引用其他列或其他表 可以通过.NET引用SQL Server之外的其他信息 |
在命令执行之后发生 系统开销很大 |
如果要实现更健壮的逻辑模型以及广泛使用用户自定义数据类型,则一般使用规则和默认值。在这种情况下规则和默认值可以提供很多功能,容易管理,而不用太多的编程开销。
只有在不能选择约束时使用触发器。和约束一样,他们被附加到表中,而且必须对创建的每个表重新定义。好的方面是触发器几乎可以做数据完整性方面的任何操作。实际上再没有出现外键时,他们常被用作外键的替代品。
而在其他情况下,应将约束作为数据完整性解决方案的选择。它们执行速度快,而且不难创建。他们的缺点是功能有限(除了外键约束,都不能引用其他表),而且对于通用约束逻辑来说,需要一次次地重新定义。