## 1.8 数据定义

本小节主要介绍数据表的定义。定义数据表的同时需要定义与该表相关的完整性约束条件，以保证关系的完整性。我们先介绍关系完整性的内容，在介绍数据表的定义、修改与删除。

### 1.8.1 关系完整性

关系模型的完整性是指关系需要遵循某种约束条件，这些约束条件是现实世界的需求。为了保证数据库中数据的正确性，任何关系在任何时刻都必须要满足这些语义约束。

关系完整性约束包括：实体完整性、参照完整性和用户定义完整性。其中实体完整性和参照完整性是关系模型必须保证的约束条件，由关系数据库系统支持。用户定义完整性是具体应用需要遵循的约束条件，由关系数据库提供定义和检验的功能。

实体完整性（entity integrity），也称主码约束，是指关系的主码属性值既不能重复也不能取空值。实体完整性保证的是关系数据库中每个元组是可区分的，是唯一的。在现实世界中，实体对象是可相互区分识别的，即它们具有某种唯一性的标识。例如，每个学生都是独立的实体对象，他们是不一样的。在关系模型中，主码作为每个元组（实体）的唯一性标识，不能取值重复也不能取空值，否则就存在不可区分、不可标识的实体。

参照完整性（Referential integrity），也称外码约束，是指建立实体之间联系的主码和外码的引用约束条件。在关系模型中，实体和实体之间的联系都是用关系来描述的，因此关系之间存在引用和被引用。比如，学生、课程和选课三个关系中，选课关系引用了学生关系的主码（学号）和课程关系的主码（课程号）。选课关系称为参照关系，学生关系和课程关系称为被参照关系。学号和课程号称为选课关系的外码。显然，选课关系中的学号的取值必须是存在学生关系中的学号，同样地，选课关系中的课程号的取值也必须是存在课程关系中的课程号。也就是说，参照完整性约束定义的是参照关系中的外码的取值必须来自于被参照关系中主码的取值。如果参照关系中的外码不是它的主码，那么外码也可以取空值。需要指出的是，参照关系和被参照关系可以是相同的关系也可以是不同的关系，主码和外码可以同名也可以不同名。

> 学生-课程数据库中,选课关系为参照关系，学生和课程关系为被参照关系。
>
> 学生关系：学生（学号，姓名，性别，年龄，系），其中学号是主码 。<br>
> 课程关系：课程（课程号，课程名，学分），其中课程号是主码。<br>
> 选课关系：选课（学号，课程号，成绩），其中学号和课程号是主码，也是外码，学号的取值来自于学生关系，课程号的取值来自于课程关系。

用户定义完整性（user-defined integrity）是指根据应用的需求，对应用的关系模型提出相应的约束条件。比如，某属性的取值可以为空，但必须唯一，即唯一约束；某属性的取值不能取空值，即非空约束；某属性的取值需要大于0等。对于此类约束一般不由应用程序提供，而是由关系模型提供定义和检验机制，并用统一的系统方法进行处理。

通常，关系模型的完整性约束与数据表一同定义。当用户操纵表中数据时，关系数据库系统会自动检查操纵语句是否违背了定义的完整性约束。关系数据库提供了定义完整性约束的关键字，其中：

* "PRIMARY KEY"定义实体完整性；
* "FOREIGN KEY"和"REFERENCES"定义参照完整性；
* "UNIQUE"定义用户的唯一约束，
* "NOT NULL"定义用户的非空约束。


### 1.8.2 表的定义、修改与删除

（1） 定义表 

使用学生-课程数据库之前，首先需要定义数据库中的基本表，SQL语言使用**CREATE TABLE**语句来定义表结构，其基本格式如下：

```SQL
CREATE TABLE <表名> ( <列名><数据类型> [列级完整性约束条件]
					[,<列名><数据类型> [列级完整性约束条件]]
					........
					[,<表级完整性约束条件>] )
```
建表时需要定义表名、所有的属性列名和列的数据类型，同时还可以定义与列或者与表有关的完整性约束条件。如果完整性约束条件涉及多个属性列，则必须定义在表级上，否则既可以定义在列级也可以定义在表级。

SQL支持了多种数据类型，用于定义表中属性的取值域。常用的数据类型包括char(n)，varchar(n)，clob，blob，int/smallint/bigint/，numeric(p,d)，decimal(p,d)，real，double，float(n)，boolean，date，time等。要注意，不同的关系数据库管理系统中支持的数据类型不完全相同。

表的定义会被存入系统的数据字典中。数据字典其实是关系数据库管理系统中的系统表，它记录了数据库中所有的定义信息，包括表结构、完整性约束，索引，视图，用户的操作权限以及统计信息等。当用户操作表中数据时，关系数据库管理系统会根据数据字典中的内容自动检查该操作是否违背了相关定义。

下例中给出了学生表、课程表和学生选课表的定义：

```SQL
[例1.13] 创建学生表Student
CREATE TABLE Student(
Sno CHAR(9) PRIMARY KEY,/*列级完整性约束条件，Sno是主码*/
Sname CHAR(20) NOT NULL, /*列级完整性约束条件，Sname不能取空值*/
Gender CHAR(2),  
Age INT, 
Dept CHAR(10)
);
[例1.14] 创建课程表Course
CREATE TABLE Course(
Cno CHAR(4) PRIMARY KEY,/*列级完整性约束条件，Cno是主码*/
Cname CHAR(40) UNIQUE, /*列级完整性约束条件，Cname取唯一值*/
Credit SMALLINT
);
[例1.15] 创建学生选课表SC
CREATE TABLE SC(
Sno CHAR(9),
Sno CHAR(4),
Grade SMALLINT,
PRIMARY KEY(Sno,Cno), &nbsp; &nbsp;/*表级完整性约束条件，Sno和Cno共同构成主码*/
FOREIGN KEY(Sno) REFERENCES Student(Sno),/*表级完整性约束条件，Sno是外码，被参照表是Student*/
FOREIGN KEY(Cno) REFERENCES Course(Cno) /*表级完整性约束条件，Cno是外码，被参照表是Course*/ 
);
```

学生表和课程表的主码约束只涉及一个属性，所以可以定义在列级上，而学生选课表的主码约束涉及两个属性，所以必须定义在表级上。外码约束只有表级约束，它用于实现表之间的参照完整性。上例中的外码约束表示，学生选课表的学号依赖于学生表的学号，学生选课表的课程号依赖于课程表的课程号，这符合现实意义，即选课的学生一定来自于学生表中的学生，学生选修的课程一定来自于课程表。

（2） 修改表

随着应用环境和应用需求的变化，有时需要修改已建立好的基本表。SQL语言使用**ALTER TABLE**语句修改基本表，其基本格式如下：

```SQL
ALTER TABLE <表名> 
[ ADD [COLUMN] <新列名><数据类型> [完整性约束] ] 
[ ADD <表级完整性约束> ] 
[ DROP [COLUMN] <列名> [CASCADE | RESTRICT] ] 
[ DROP CONSTRAINT <完整性约束名> [CASCADE | RESTRICT] ] 
[ ALTER COLUMN <列名><数据类型> ];
```
修改基本表时可以增加新列、新的列级完整性约束条件，新增表级完整性约束条件，删除表中的列，删除完整性约束条件和修改原有列的定义。删除列和完整性约束条件时可以添加关键字CASCADE和RESTRICT。CASCADE表示级联删除，即删除列和约束条件的同时删除引用该列和该约束条件的其他对象；RESTRICT则指如果该列或该约束条件被其他对象引用，则不能删除。

虽然关系数据库管理系统提供了修改基本表的功能，但是在应用程序开发中并不提倡使用这些操作。因为对于关系数据库而言，一旦允许程序访问数据库中的数据，进行交互时，它的假设前提就是数据的组织形式或者表的模式已经定义好并且已具有完整的约束条件。

下面给出了修改表的一些例子：
```SQL
[例1.16] 给学生表新增“入学时间”列，数据类型为日期型，列约束条件为非空。
ALTER TABLE Student ADD Entrance DATE NOT NULL; 
[例1.17] 将学生表中性别的数据类型由字符串改为整数。
ALTER TABLE Student ALTER COLUMN Gender INT;
```

（3） 删除表

当不再需要某个基本表时，SQL语言使用**DROP TABLE**语句删除基本表，其基本格式如下：

```SQL
DROP TABLE <表名> [CASCADE | RESTRICT];
```

其中，CASCADE关键字指级联删除，即删除基本表的同时删除基本表的相关依赖对象，如索引、触发器，有的关系数据库管理系统还会同时删除视图。读者可以查阅使用产品的用户手册，了解具体的删除策略；RESTRICT表示如果预删除的基本表被其他表的约束所引用（如，FOREIGN KEY）或者基本表有视图、触发器、存储过程和函数时，则基本表不能被删除。默认情况下设置为RESTRICT。

下例给出了删除学生表的SQL语句：
```SQL
[例1.18] 删除学生表
DROP TABLE Student CASCADE;
```

基本表被删除时，表的定义以及表中的数据都将一起被删除。由于学生选课表SC通过外码Sno引用了学生表Student，因此Student表被删除的同时SC也将被级联删除。

### 练习题

**1**. 外键约束起哪些作用？（1）告诉数据库系统数据中的引用关系；（2）明确外键所在元组存在依赖于它指向的主键的存在；（3）告诉数据库系统外键指向的主键不能被随意删除。

 <ol type="A">
 <li>（1）（2）（3）</li>
 <li>（1）（2）</li>
 <li>（1）（3）</li>
 <li>（2）（3）</li>
 </ol>

**2**. 对于一张学生表Student (s_no, s_name, birthday, gender)，我们可以将主键定义在单个属性s_no上，也可以将定义在复合属性 (s_no,s_name) 上。请问那种方式更优？

 <ol type="A">
 <li>s_no作为主键更优</li>
 <li>(s_no,s_name)作为主键更优</li>
 <li>两者各有优势</li>
 <li>以上说法均不对</li>
 </ol>

**3**. 你觉得在什么样的属性上适合使用Not Null这样的约束？

 <ol type="A">
 <li>一个人的住址</li>
 <li>学生选课表中的成绩</li>
 <li>一本出版物的出版社</li>
 <li>一门课的教材</li>
 </ol>

[**上一页<<**](chapter1.7-R.md) | [**>>下一页**](chapter1.9-R.md)























