# 创建数据库

# 连接数据库

在开始实践任务前,你需要正确安装 MySQL 数据库管理系统,确保 MySQL 服务正常启动,平台已为你准备好 MySQL 8。在开始操作前,需要连接到数据库,连接的方法跟你在本机上的操作没有区别,请在命令行输入:

mysql -h host -u user -ppassword dbname

该命令适用于 Windows, Linux 和 MacOs。
上述命令各部分含义如下:

  • mysql 是 MySQL 的缺省管理工具,你在本地操作时,可以使用第 3 方管理工具如 Navicat Premium 等,具体连接参数配置和操作指南请参阅相关手册;本平台只提供 MySQL 的官方管理工具。

  • -h 参数用于指定服务器主机名或 ip 地址,同学们在自己的电脑上操作,一般不需要指定 - h 参数,但在本实验中,需要指定 127.0.0.1 作为服务器的 ip 地址;

  • -u 参数用于指定连接服务器的用户名 (账户名),MySQL 在安装时自动创建的管理员帐户名为 root,并指定其密码,管理员有权创建其它帐户,并分配账户权限,如何创建用户、分配权限等将在数据库安全部分再介绍,在此之前,你可以一直使用 root 用户;

  • -p 参数用于指定用户的密码;通常不建议在命令行直接给出密码,因为这样可能泄密,带来安全隐患。在本实验中,用户 root 的密码为 123123,你可以直接在命令行给出;

  • dbname 为连接数据库服务器后将要访问的数据库名称(一个服务器上可能有多个数据库),此名称不是必须的,你也可以在连接成功后,再用 use 语句指定数据库名;

  • -h,-u 参数后,可以有空格,也可以没有空格,但用 - p 参数直接给出密码时,-p 后不能有空格,因为空格后的标识符会被解释成将要访问的数据库名,例如:

mysql -h127.0.0.1 -uroot -p 123123 中的 123123 将被理解为数据库名 (它实际上不符合数据库命名规则);

mysql -h127.0.0.1 -uroot -p123123

带参数 - p,但不直接给出密码,则程序运行时,将首先要求输入密码:

shell> mysql -h127.0.0.1 -uroot -p
Enter password: ********
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 25338 to server version: 8.0.22-standard
Type 'help;' or '\h' for help. Type '\c' to clear the buffer.
mysql>

其中 shell> 是操作系统提示符, mysql> 是 mysql 成功连接上数据库服务器器后的操作指示符。

键入 quit 命令,可从 MySQL 管理工具退回到 Linux 操作系统。

# 创建数据库

创建数据库的语句为:

mysql> CREATE DATABASE dbname;
  • 如前所述, mysql> 为 mysql 的提示符,它表示已准备好接受你输入的命令或 SQL 语句。

  • CREATE DATABASE 为 MySQL 的保留字,保留字不区分大小写,因此,写成 Create Database 或 create database 都可以;这两个保留字表示创建数据库;

  • dbname 为要创建的数据库名;切记:在 linux 系统里,数据库对象 (数据库、表、列、索引等) 的名字是严格区分大小写的。为保证程序的兼容性和可移植性,应该坚持区分大小写(不管使用 Windows 系统还是 Linux 系统);

  • 分号 (;) 是 SQL 语句的结束符,必须是英文的分号(不能是全角状态下的中文分号),初学者易犯的错误是输入了中文分号。如果输完一条语句,MySQL 并不执行,而是继续显示提示符 “>”,很可能是你忘了输入语句结束符 “;”.MySQL 的语句可以分多行书写,在下一行接着写未完成的部分(哪怕仅缺一个分号)就可以了。

下面创建一个名为 TestDb 的数据库:

1666682769433

创建完数据库之后我们可以通过 show databases; 命令查看 MySQL 中已存在的数据库:

1666682807137

重复创建同一名称的数据库时会报错。可以使用

CREATE DATABASE if not exists  dbname;

# 查看当前使用的数据库

# select database()

1666683980143

# status;

1666684040827

# 创建表及表的主码约束

# 指定当前工作数据库

把数据库理解成容器,数据库里可以容纳多张表、视图、索引、过程和函数等数据对象。一个数据库服务器可以存储多个数据库,用户或客户程序登录到数据库服务器后,需要指明要访问的数据库,方法是使用命令:

use dbname;

其中,dbname 为数据库名。在下面的例子里,首先创建数据库 MyDB,接着指定这个数据库为当前工作数据库:

1666683645685

use 语句是个例外,语句结束符分号 (;) 可以省略。

如果已经处在一个数据库中,可以使用上述命令切换操作的数据库么?

# 在当前工作数据库里创建表

建表语句的语法如下:

CREATE TABLE [IF NOT EXISTS] tbl_name
(列定义|表约束,...)

在语法描述中,方括号里的内容为可选项,可以有,也可以没有;用 “ | ” 隔开的内容为多选一,“ a|b ” 表示要么 a,要么 b。

  • CREATE TABLE 为保留字,其语义为创建表对象;

  • IF NOT EXISTS 为可选短语,其语义为仅当该表不存在时才创建表;如果不带该短语,创建表时,如果同名表已存在,则输出报错信息;

  • tbl_name 为表的名字;

  • (列定义|表约束,...) 表示表的其它定义都写在一对括号里,括号里为一个或多个 “列定义” 或 “表约束”,如果有多个列的定义或表约束,则它们之间用逗号隔开。

列定义的语法如下:

列定义 ::=  列名  数据类型
      [NOT NULL | NULL] 
      [DEFAULT {常量 | (表达式)} ]
      [AUTO_INCREMENT] [UNIQUE [KEY]] [PRIMARY KEY]
      [COMMENT '列备注']
      [REFERENCES tbl_name (col_name)
           [ON DELETE RESTRICT|CASCADE|SET NULL|NO ACTION|SET DEFAULT]
           [ON UPDATE RESTRICT|CASCADE|SET NULL| NO ACTION|SET DEFAULT]]
      [[CONSTRAINT [约束名]] CHECK (表达式)]

列的定义至少包括列的名字及其数据类型,然后还可以有其它一些选项(这些选项大多用来定义对该列的约束,或者给该列附加备注):

  • [NOT NULL |NULL] 表示空或非空约束,缺省为 NULL,即该列的内容允许为空值,NOT NULL 则约束该列的内容必须为非空;

  • DEFAULT 关键字为列指定缺省值,可以是常量,也可以是表达式;

  • AUTO_INCREMENT 指定该列为自增列 (如 1,2,3,...),一般用于自动编号,显然只有数字类型的列才可以定义这一特性;

  • [UNIQUE] 指定该列值具有唯一性(但可以有空值 - 甚至多个空值的存在,如果该列没有定义 NOT NULL 约束);

  • PRIMARY KEY 指定该列为主码,相当于定义表的实体完整性约束;只有当主码由单属性组成时,才可以这样定义主码(主码由多属性组成时,应当用表约束来定义);

  • COMMENT 用来给列附加一条注释;

  • “REFERENCES” 短语为该列定义参照完整性约束,指出该列引用哪个表的哪一列的值,以及违背参照完整性后的具体处理规则(多个规则中选一),具体内容将在随后的练习里再讲解;

  • CHECK (表达式) 为列指定 “自定义约束”,只有使(表达式)的值为 true 的数据才允许写入数据库;关键词 CONSTRAINT 用来为约束命名。

表约束的语法规则如下:

表约束 ::= [CONSTRAINT [约束名]]
       | PRIMARY KEY (key_part,...)
       | UNIQUE (key_part,...)
       | FOREIGN KEY (col_name,...) 
        REFERENCES tbl_name (col_name,...)
           [ON DELETE RESTRICT|CASCADE|SET NULL|NO ACTION|SET DEFAULT]
           [ON UPDATE RESTRICT|CASCADE|SET NULL| NO ACTION|SET DEFAULT]
       | CHECK (表达式)

表约束以关键词 CONSTRAINT 打头,后跟约束名,约束名可以省略,甚至连同关键词 CONSTRAINT 一同省略。这时,系统将自动为约束命名,DBMS 取的名字一般可读性不强,不好记,会给将来可能的修改约束、删除约束等操作带来麻烦,总是给约束取一个有意义的名字是个好习惯。
表约束可以是主码约束 (PRMARY KEY)、唯一性约束 (UNIQUE)、外码约束 (FOREIGN KEY)、CHECK 约束等中的一种。
建议同学们遵守约定俗成的约束命名规则:

  • 主码约束以 “PK_” 打头,后跟表名,一个表只会有一个主码约束;
  • 外码约束以 “FK_” 打头,后跟表名及列名;
  • CHECK 约束以 “CK_” 打头,后跟表名及列名。

主码约束及唯一性约束中 “key_part” 的语法规则如下:

主码约束 (索引) 和唯一性约束 (索引) 均可由一至多个列(或含列的表达式)组成,每个列(或含列的表达式)后用关键词 ASC 或 DESC 指示排序规则 (升序或降序),ASC (升序) 为缺省值,可以省略。系统会为主码约束和唯一性约束自动建索引。

外码约束可以由 1 个或多个列组成,后跟被引用表的名字和被引用的列,引用列和被引用列要一一对应。随后还可以定义违背参照完整性时的处理策略。

# 如何定义主码

由前面介绍的建表语句语法规则知,定义主码有两种方法:

  • 单属性主码,既可在列定义里用 PRIMARY KEY 约束指定主码,也可以作为表约束单独定义;

  • 组合属性作主码时,该主码只能定义为表约束。

在 MySQL 中,主码作列的约束时,不能自主取名,作表约束时,才可以自主命名。不过,MySQL 对主码命名短语的支持只停留在语法上,实际并没有实现(给主码约束所起的名字会被直接忽略)。

# 示例

建一个表 t_user,定义表的各列,为表指定主码。表 t_user 的结构如下:

1666685297007

完成上述任务的语句如下:

CREATE TABLE t_user
(
    id INT PRIMARY KEY,
    username VARCHAR(32),
    password VARCHAR(32),
    phone VARCHAR(11)
);

当然,你也可以把主码约束单独定义成一个表约束,为了与其它 DBMS 兼容,你甚至还可以给这个约束命名(如果你愿意的话):

CREATE TABLE t_user
(
id INT,
username VARCHAR(32),
password VARCHAR(32),
phone VARCHAR(11),
CONSTRAINT PK_t_emp PRIMARY KEY (id)
);

1666685387508

注意,在输入建表语句时,为了方便阅读和检查,每列定义在单独一行上,定义完一列后即回车,接着输入下一列的定义。但这种方法也有缺点:当你输入语句结束符(逗号),回车运行后,系统可能会给出错误提示(如果输入的语句中存在错误)。这时,想接着在之前录入的语句上修改是件比较麻烦的事 (你需要借助鼠标、按键,进行复制、粘贴)。
所以,为了修改方例,你也可以选择整条语句写在一行上,如下图所示

1666685438615

这种方法输入时,如果遇到错误,只需按键盘上的 “↑” 键即可呼出之前输入的内容,再移动光标即可在原来的基础上修改,然后再回车执行。

表创建好之后可以使用如下语句列出所有的表: show tables;

还可以使用如下语句查看表的结构,用来检查所建的表是否正确体现了原意: DESC 表名;

1666685503937

# 创建外码约束 (foreign key)

# 什么是外码

外码是表中的一个或一组字段(属性),它可以不是本表的主码,但它与某个主码 (同一表或其它表的主码) 具有对应关系(语义完全相同)。外码可以是一列或多列,一个表可以有一个或多个外码。当我们谈论外码时,一定有个主码与它对应,外码不可能单独存在。主码所在的表为主表,又称父表,外码所在的表为从表,又称子表。比如在以下两表:“学生表” 和 “院系表” 中,“学生表” 中的字段 “所在院系” 与 “院系表” 中的 “院系代码” 具有对应关系,其中 “院系代码” 是 “院系表” 的主码,“学生表” 中的 “所在院系” 为外码。

1666685900607

1666685909755

# 什么是外码约束(参照完整性约束)

外码用来在数据之间(即外码与其对应的主码间)建立关联。参照完整性约束用于约束外码列的取值范围:外码列的取值要么为空,要么等于其对应的主码列的某个取值。在语义允许,又不违反其它约束规则的情形下,外码列的取值才可以为空。在上面的例子中,学生所在的院系,要么取空值 (可以表示:已录取,尚未确定院系),要么为院系表中的某个院系(总之,绝对不允许出现院系表中不存在的院系)。

# 如何定义外码约束

可在定义表的同时定义各种完整性约束规则 (当然包括外码约束,亦即参照完整性约束)。在建表时,外码约束既可以定义为列约束,亦可定义为表约束:

# 列级外码约束

列级外码约束的语法格式如下:

列级外码约束 ::=  列名  数据类型
      [REFERENCES tbl_name (col_name)
      [ON DELETE RESTRICT|CASCADE|SET NULL|NO ACTION|SET DEFAULT]
      [ON UPDATE RESTRICT|CASCADE|SET NULL| NO ACTION|SET DEFAULT]]

即在列的名称和数据类型后,直接用关键词 references 定义外码约束,指明该外码对应的主码(表名和列名),同时还可以定义(可选)一旦违反参照完整性时,应采取何种应对策略。

# 表级外码约束

表约外码约束的语法格式如下:

表级外码约束 ::= [CONSTRAINT [约束名]]
        FOREIGN KEY (col_name,...) 
        REFERENCES tbl_name (col_name,...)
           [ON DELETE RESTRICT|CASCADE|SET NULL|NO ACTION|SET DEFAULT]
           [ON UPDATE RESTRICT|CASCADE|SET NULL| NO ACTION|SET DEFAULT]

MySQL 表级外码约束的好处是可以给约束命名,且支持多属性组合外码(即外码由多个列组成)。事实上,外码约束定义在表一级,是不二的选择,因为 MySQL 对列级外码约束的支持仅停留在语法检查阶段,实际并没有实现 (至少 8.0.22 还没有实现)。外码约束的名称一般以 “FK_” 为前缀,这是约定俗成的规则。

在前例中,被引用的表 “院系表” 应先于引用表 “学生表” 定义:

CREATE TABLE 院系表
(
    院系代码 char(3) PRIMARY KEY,
    名称 VARCHAR(22)
);
CREATE TABLE 学生表
(
    学号 char(9) PRIMARY KEY,
    姓名 VARCHAR(22),
    所在院系 char(3),
    CONSTRAINT FK_student_dept FOREIGN KEY(所在院系) REFERENCES 院系表(院系代码)
);

在上述实现中,两个表的主码约束都定义成列约束,且省略了命名,外码约束显然是作为表约束定义的。由于是单属性作外码,这个外码约束也可以定义为列级约束。

# CHECK 约束

# 用户定义的完整性约束

关系数据库的完整性约束共有三类:实体完整性约束,参照完整性约束以及用户定义的完整性约束。实体完整性约束和参照完整性约束分别用 PRIMARY KEY 和 FOREIGN KEY 来实现;CHECK 约束是最主要的一类用户定义的完整性约束,用于定义用户对表中的某列的数据约束,或表中一行中几列之间应该满足的完整性约束。MySQL 从 8.0.16 开始支持 CHECK 约束。

# CHECK 约束的定义方法

CHECK 约束的定义方法:

  • 如果约束仅涉及单个列,则该约束既可以定义为列约束,也可以定义为表约束,例如:“性别” 列的取值仅限从(“男”,“女”) 中取值;

  • 如果约束涉及表的多个列,则该约束只能定义为表约束,例如:如果职称为 “教授”,则它的薪资应当不低于 6000 元。这个约束涉及到 “职称” 和 “薪资” 两个列的内容,故只能用表约束来实现。

CHECK 约束的语法:

CHECK约束 ::= [CONSTRAINT [约束名]] CHECK (条件表达式)]

可以给约束显示命名(可选),CHECK 约束由关键字 CHECK 引出,后跟一对括弧,括弧里为条件表达式,只有当条件表达式的值为 true 时,数据(插入的新数据,或修改后的数据)才会被接受,否则将被拒绝。

CHECK 约束作列约束时,紧跟在列定义之后定义(即跟在列名,列数据类型之后申明);CHECK 约束作为表约束时,单独申明。

作为列约束的示例如下:

create table student(
  sid char(12) primary key,
  name varchar(32),
  sex char(1) constraint CK_student_sex CHECK(sex in ('M','F')),
  dob date
)

作为表约束的示例如下:

create table student(
  sid char(12) primary key,
  name varchar(32),
  sex char(1),
  dob date,
  constraint CK_student_sex CHECK(sex in ('M','F'))
)

# DEFAULT 约束

# 相关知识

默认值约束 (Default 约束) 用于给表中的字段指定默认值,即往表里插入一条新记录时,如果没有给这个字段赋值,那么 DBMS 就会自动赋于这个字段默认值。

# DEFAULT 约束的语法

Default 约束只能定义为列一级约束,即在需要指定默认值的列之后用关键字 DEFAULT 申明默认值,其语法为:

col_name data_type [DEFAULT {literal | (expr)} ]

即在列名与列的数据类型之后申明 Default 约束。当然 Default 约束只是众多列约束中的一种,该列可能还有 NOT NULL, UNIQUE, AUTO_INCREMENT, CHECK,FOREIGN KEY 等其它约束。

DEFAULT 关键字引出的默认值可以是常量,也可以是一个表达式。

# DEFAULT 约束举例

假设表 student 的结构如下:

1666688416154

则可以用以下语句创建该表,及性别列的默认值约束(姓名列还有一个 NOT NULL 约束):

create table student(
  sno char(10) primary key,
  name varchar(32) NOT NULL,
  sex char(2) default '男',
  DOB date
);

又比如,在一个订单系统中,订单表 order 的结构如下:

1666688564912

订单通常需记录购买者 (customerNo) 和达成该订单的雇员 (employeeNo),以便计算佣金。在一个实际的订单系统中,这两列都是外码 (订单系统中通常还会有 customer 和 employee 等其它表)。这里暂且把这两列当成普通的列对待。

可以用以下代码创建 order 表,并创建 orderNo 的自动编号约束,以及 orderDate 的默认值约束:

create table `order`(
   orderNo int auto_increment primary key, 
   orderDate date default (curdate()), 
   customerNo char(10), 
   employeeNo char(10)
);

注意:

  • AUTO_INCREMENT 约束仅用于整数列;

  • DEFAULT 约束指定默认值为表达式时,表达式要写在一对括弧里;

  • 这里,curdate () 是 MySQL 的系统函数,其功能是取当前日期;

  • 语句中,表名称 order 前后的 " "号是必须的,因为order是MySQL的关键字,当表名或列名与关键字冲突时,名称前后必须加" " 号。

# UNIQUE 约束

# 相关知识

唯一性约束 (Unique 约束) 用于保证表中字段取值的唯一性。Unique 约束既可以约束表中的单列,也可以约束表中的组合列(多列)。

# UNIQUE 约束

跟主码 (Primary Key) 约束一样,Unique 约束既可以是对单属性的约束,也可以是对属性组约束,具有 Unique 约束的属性或属性组的取值必须是唯一的,否则就违反了 Unique 约束。不过,跟主码不同的是,Unique 约束并不要求字段必须非空 (Not Null),所以,实际上,它只能约束非空的属性 (组)取值是唯一的。同时具有 Not Null 约束的 Unique 属性 (组) 相当于候选码。一个表只能定义一个主码约束,但可以定义多个 Unique 约束。

# UNIQUE 约束的语法

跟主码约束一样,单字段的 Unique 约束既可定义为列约束,亦可定义为表约束,组合字段的 Unique 约束只能定义为表约束。

Unique 列约束的语法为:

col_name data_type UNIQUE

即在列名与列的数据类型之后用关键字 UNIQUE 申明 Unique 约束。当然 Unique 约束只是众多列约束中的一种,该列可能还有其它约束。

Unique 表约束的语法为:

[CONSTRAINT [约束名]] UNIQUE(列1, 列2, ...)

Constraint 短语可以省略。既使写上关键词 constraint,也可以省略约束名。约束未命名时,MySQL 将按一定规则自动予以命名。

# Unique 约束举例

假设表 department 的结构如下:

1666689294167

则可以用以下语句创建该表:

create table department(
  dno char(10) primary key,
  dname varchar(32) NOT NULL UNIQUE
);

本例中,院系名称 (dname) 同时具有 NOT NULL 约束和 UNIQUE 约束。注意,NOT NULL 只能作列约束,且不用命名。本例中的 UNIQUE 约束还可以用表约束来实现。 UNIQUE 约束作列约束时不能自主命名,作表约束时可以自主命名。