CHECK约束指在表的列中增加额外的限制条件。
注: CHECK约束不能在VIEW中定义。CHECK约束只能定义的列必须包含在所指定的表中。CHECK约束不能包含子查询。
创建表时定义CHECK约束
1.1 语法:
1
2
3
4
5
6
7
|
CREATE TABLE table_name
(
column1 datatype null / not null ,
column2 datatype null / not null ,
...
CONSTRAINT constraint_name CHECK (column_name condition) [DISABLE]
);
|
其中,DISABLE关键之是可选项。如果使用了DISABLE关键字,当CHECK约束被创建后,CHECK约束的限制条件不会生效。
1.2 示例1:数值范围验证
1
2
3
4
5
6
7
8
|
create table tb_supplier
(
supplier_id number,
supplier_name varchar2(50),
contact_name varchar2(60),
/*定义 CHECK 约束,该约束在字段supplier_id被插入或者更新时验证,当条件不满足时触发。*/
CONSTRAINT check_tb_supplier_id CHECK (supplier_id BETWEEN 100 and 9999)
);
|
验证:
在表中插入supplier_id满足条件和不满足条件两种情况:
1
2
3
4
5
|
--supplier_id满足check约束条件,此条记录能够成功插入
insert into tb_supplier values (200, 'dlt' , 'stk' );
--supplier_id不满足check约束条件,此条记录能够插入失败,并提示相关错误如下
insert into tb_supplier values (1, 'david louis tian' , 'stk' );
|
不满足条件的错误提示:
1
2
3
4
|
Error report -
SQL Error: ORA-02290: check constraint (502351838.CHECK_TB_SUPPLIER_ID) violated
02290. 00000 - "check constraint (%s.%s) violated"
*Cause: The values being inserted do not satisfy the named check
|
1.3 示例2:强制插入列的字母为大写
1
2
3
4
5
6
7
8
9
|
create table tb_products
(
product_id number not null ,
product_name varchar2(100) not null ,
supplier_id number not null ,
/*定义 CHECK 约束check_tb_products,用途是限制插入的产品名称必须为大写字母*/
CONSTRAINT check_tb_products
CHECK (product_name = UPPER (product_name))
);
|
验证:
在表中插入product_name满足条件和不满足条件两种情况:
1
2
3
4
|
--product_name满足check约束条件,此条记录能够成功插入
insert into tb_products values (2, 'LENOVO' , '2' );
--product_name不满足check约束条件,此条记录能够插入失败,并提示相关错误如下
insert into tb_products values (1, 'iPhone' , '1' );
|
不满足条件的错误提示:
1
2
3
|
SQL Error: ORA-02290: check constraint (502351838.CHECK_TB_PRODUCTS) violated
02290. 00000 - "check constraint (%s.%s) violated"
*Cause: The values being inserted do not satisfy the named check
|
2. ALTER TABLE定义CHECK约束
2.1 语法
1
2
|
ALTER TABLE table_name
ADD CONSTRAINT constraint_name CHECK (column_name condition) [DISABLE];
|
其中,DISABLE关键之是可选项。如果使用了DISABLE关键字,当CHECK约束被创建后,CHECK约束的限制条件不会生效。
2.2 示例准备
1
2
3
4
5
6
7
8
|
drop table tb_supplier;
--创建实例表
create table tb_supplier
(
supplier_id number,
supplier_name varchar2(50),
contact_name varchar2(60)
);
|
2.3 创建CHECK约束
1
2
3
4
|
--创建check约束
alter table tb_supplier
add constraint check_tb_supplier
check (supplier_name IN ( 'IBM' , 'LENOVO' , 'Microsoft' ));
|
2.4 验证
1
2
3
4
5
|
--supplier_name满足check约束条件,此条记录能够成功插入
insert into tb_supplier values (1, 'IBM' , 'US' );
--supplier_name不满足check约束条件,此条记录能够插入失败,并提示相关错误如下
insert into tb_supplier values (1, 'DELL' , 'HO' );
|
不满足条件的错误提示:
1
2
3
|
SQL Error: ORA-02290: check constraint (502351838.CHECK_TB_SUPPLIER) violated
02290. 00000 - "check constraint (%s.%s) violated"
*Cause: The values being inserted do not satisfy the named check
|
3. 启用CHECK约束
3.1 语法
1
2
|
ALTER TABLE table_name
ENABLE CONSTRAINT constraint_name;
|
3.2 示例
1
2
3
4
5
6
7
8
9
10
11
12
13
|
drop table tb_supplier;
--重建表和CHECK约束
create table tb_supplier
(
supplier_id number,
supplier_name varchar2(50),
contact_name varchar2(60),
/*定义 CHECK 约束,该约束尽在启用后生效*/
CONSTRAINT check_tb_supplier_id CHECK (supplier_id BETWEEN 100 and 9999) DISABLE
);
--启用约束
ALTER TABLE tb_supplier ENABLE CONSTRAINT check_tb_supplier_id;
|
3.3使用Check约束提升性能
在SQL Server中,SQL语句的执行是依赖查询优化器生成的执行计划,而执行计划的好坏直接关乎执行性能。
在查询优化器生成执行计划过程中,需要参考元数据来尽可能生成高效的执行计划,因此元数据越多,则执行计划更可能会高效。所谓需要参考的元数据主要包括:索引、表结构、统计信息等,但还有一些不是很被注意的元数据,其中包括本文阐述的Check约束。
图1.简单查询
查询优化器在生成执行计划之前有一个阶段叫做代数树优化,比如说下面这个简单查询:
查询优化器意识到1=2这个条件是永远不相等的,因此不需要返回任何数据,因此也就没有必要扫描表,从图1执行计划可以看出仅仅扫描常量后确定了1=2永远为false后,就可完成查询。
那么Check约束呢?
Check约束可以确保一列或多列的值符合表达式的约束。在某些时候,Check约束也可以为优化器提供信息,从而优化性能,比如看图二的例子。
图2.有Check约束的列提升查询性能
图2是一个简单的例子,有时候在分区视图中应用Check约束也会提升性能,测试代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
|
CREATE TABLE [dbo].[Test2007](
[ProductReviewID] [ int ] IDENTITY(1,1) NOT NULL ,
[ReviewDate] [datetime] NOT NULL
) ON [ PRIMARY ]
GO
ALTER TABLE [dbo].[Test2007] WITH CHECK ADD CONSTRAINT [CK_Test2007] CHECK (([ReviewDate]>= '2007-01-01' AND [ReviewDate] '2007-12-31' ))
GO
ALTER TABLE [dbo].[Test2007] CHECK CONSTRAINT [CK_Test2007]
GO
CREATE TABLE [dbo].[Test2008](
[ProductReviewID] [ int ] IDENTITY(1,1) NOT NULL ,
[ReviewDate] [datetime] NOT NULL
) ON [ PRIMARY ]
GO
ALTER TABLE [dbo].[Test2008] WITH CHECK ADD CONSTRAINT [CK_Test2008] CHECK (([ReviewDate]>= '2008-01-01' AND [ProductReviewID] '2008-12-31' ))
GO
ALTER TABLE [dbo].[Test2008] CHECK CONSTRAINT [CK_Test2008]
GO
INSERT INTO [Test2008] values ( '2008-05-06' )
INSERT INTO [Test2007] VALUES ( '2007-05-06' )
CREATE VIEW testPartitionView
AS
SELECT * FROM Test2007
UNION
SELECT * FROM Test2008
SELECT * FROM testPartitionView
WHERE [ReviewDate]= '2007-01-01'
SELECT * FROM testPartitionView
WHERE [ReviewDate]= '2008-01-01'
SELECT * FROM testPartitionView
WHERE [ReviewDate]= '2010-01-01'
|
我们针对Test2007和Test2008两张表结构一模一样的表做了一个分区视图。并对日期列做了Check约束,限制每张表包含的数据都是特定一年内的数据。当我们对视图进行查询并给定不同的筛选条件时,可以看到结果如图3所示。
图3.不同的条件产生不同的执行计划
由图3可以看出,当筛选条件为2007年时,自动只扫描2007年的表,2008年的表也是同样。而当查询范围超出了2007和2008年的Check约束后,查询优化器自动判定结果为空,因此不做任何IO操作,从而提升了性能。
结论
在Check约束条件为简单的情况下(指的是约束限制在单列且表达式中不包含函数),不仅可以约束数据完整性,在很多时候还能够提供给查询优化器信息从而提升性能。
4. 禁用CHECK约束
4.1 语法
1
2
|
ALTER TABLE table_name
DISABLE CONSTRAINT constraint_name;
|
4.2 示例
1
2
|
--禁用约束
ALTER TABLE tb_supplier DISABLE CONSTRAINT check_tb_supplier_id;
|
5. 约束详细信息查看
语句:
1
2
3
4
5
6
7
8
9
|
--查看约束的详细信息
select
constraint_name, --约束名称
constraint_type, --约束类型
table_name, --约束所在的表
search_condition, --约束表达式
status --是否启用
from user_constraints --[all_constraints|dba_constraints]
where constraint_name= 'CHECK_TB_SUPPLIER_ID' ;
|
6. 删除CHECK约束
6.1 语法
1
2
|
ALTER TABLE table_name
DROP CONSTRAINT constraint_name;
|
6.2 示例
1
2
|
ALTER TABLE tb_supplier
DROP CONSTRAINT check_tb_supplier_id;
|