阅读 772

MySQL入门系列:数据库和表的基本操作

标签: MySQL入门


上集回顾

MySQL使用表来存放数据,表的每一列都需要存放特定格式的数据,所以MySQL为不同的格式的数据定义了不同的类型,我们介绍了各种数值、字符串、时间和二进制类型的含义以及要求的存储空间。本集来看一下在MySQL中关于数据库和表的各种操作。

命令使用注意事项

我们知道MySQL的基本运行过程就是:通过客户端程序发送命令给服务器程序,服务器程序按照接收的命令去操作实际的数据。在我们使用黑框框启动了MySQL客户端程序之后,界面上会一直显示一行mysql>的提示符,你可以在它后边输入我们的命令然后按一下回车键,在书写命令的时候需要注意下边这几点:

  1. 命令结束符号。

    在书写完一个命令之后需要以下边这几个符号之一结尾:

    • ;
    • \g
    • \G

    比如说我们执行一个简单的查询当前时间的命令:

    mysql> SELECT NOW();
    +---------------------+
    | NOW()               |
    +---------------------+
    | 2018-02-06 17:50:55 |
    +---------------------+
    1 row in set (0.00 sec)
    
    mysql>
    复制代码

    其中的SELECT意味着这是一个查询命令,NOW()MySQL内置的函数,用于返回当前时间。不过我们现在并不是深究具体的某个命令是什么意思,只是想介绍一下书写命令时需要注意的一些事情。结果中1 row in set (0.00 sec)的意思是结果只有1行数据,用时0.00秒。使用\g可以起到一样的效果:

    mysql> SELECT NOW()\g
    +---------------------+
    | NOW()               |
    +---------------------+
    | 2018-02-06 17:50:55 |
    +---------------------+
    1 row in set (0.00 sec)
    
    mysql>
    复制代码

    \G有一点特殊,它并不以表格的形式返回查询数据,而是以垂直的形式展现查询数据:

    mysql> SELECT NOW()\G
    *************************** 1. row ***************************
    NOW(): 2018-02-06 17:51:51
    1 row in set (0.00 sec)
    
    mysql>
    复制代码

    如果查询结果非常长的话,使用\G可以让我们看清结果。如果显式格式没啥问题,那我们平时都使用分号;作为命令结束符了~

  2. 命令可以随意换行。

    并不是按了回车键就提交命令了,只要按回车键的时候输入的语句里没有;\g或者\G就算是语句没结束。比如上边查询当前时间的命令还可以这么写:

    mysql> SELECT
        -> NOW()
        -> ;
    +---------------------+
    | NOW()               |
    +---------------------+
    | 2018-02-06 17:57:15 |
    +---------------------+
    1 row in set (0.00 sec)
    
    mysql>
    复制代码
  3. 可以一次提交多个命令

    我们可以在一条语句里写多个命令(命令之间用上面说的结束符分隔),比如这样:

    mysql> SELECT NOW(); SELECT NOW(); SELECT NOW();
    +---------------------+
    | NOW()               |
    +---------------------+
    | 2018-02-06 18:00:05 |
    +---------------------+
    1 row in set (0.00 sec)
    
    +---------------------+
    | NOW()               |
    +---------------------+
    | 2018-02-06 18:00:05 |
    +---------------------+
    1 row in set (0.00 sec)
    
    +---------------------+
    | NOW()               |
    +---------------------+
    | 2018-02-06 18:00:05 |
    +---------------------+
    1 row in set (0.00 sec)
    
    mysql>
    复制代码

    连着输入了3个查询当前时间的命令,只要没按回车键,就不会提交命令。

    小贴士: 后边我们还会介绍把命令都写在文件里,然后再批量执行文件中的命令,那个感觉更爽!

  4. 使用\c清除本次操作。

    如果你想放弃本次编写的命令,可以使用\c来清除,比如这样:

    mysql> SELECT NOW()\c
    mysql>
    复制代码

    如果不使用\c,那客户端会以为这是一个多行命令,还在一直傻傻的等你输入命令~

  5. 大小写问题。

    MySQL默认对命令的大小写并没有限制,也就是说我们这样查询当前时间也是可以的:

    mysql> select now();
    +---------------------+
    | now()               |
    +---------------------+
    | 2018-02-06 18:23:01 |
    +---------------------+
    1 row in set (0.00 sec)
    
    mysql>
    复制代码

    不过按照习俗,这些命令、函数什么的都是要大写的,而一些名称类的东西,比如数据库名,表名、列名啥的都是要小写的,更多具体的书写规范等我们遇着再详细介绍。

  6. 字符串的表示。

    在命令里有时会使用到字符串,我们可以使用单引号''或者双引号""把字符串内容引起来,比如这样:

    mysql> SELECT 'aaa';
    +-----+
    | aaa |
    +-----+
    | aaa |
    +-----+
    1 row in set (0.00 sec)
    
    mysql>
    复制代码

    这个语句只是简单的把字符串'aaa'又输出来了而已。但是一定要在字符串内容上加上引号,不然的话MySQL服务器会把它当作列名,比如这样就会返回一个错误:

    mysql> SELECT aaa;
    ERROR 1054 (42S22): Unknown column 'aaa' in 'field list'
    mysql>
    复制代码

    但是MySQL中有一种叫ANSI_QUOTES的模式,如果开启了这种模式,双引号就有其他特殊的用途了,可能你并不能看懂我在说什么,但是这都不重要,重要的是建议你最好使用单引号来表示字符串~

当一条命令发送给了MySQL服务器之后,服务器处理完后就会给你发送回来响应的结果,然后显示到界面上。然后你就可以接着输入下一条命令了。

数据库相关操作

MySQL中把某种类型的表的集合称为一个数据库MySQL服务器管理着若干个数据库,每个数据库下都可以有若干个表,画个图就是这样:

image_1c4jjlck0101k1lbq100u1ggd4ml46.png-75.7kB

展示数据库

在我们刚刚安装好MySQL的时候,它已经内建了许多数据库和表了,我们可以使用下边这个命令来看一下都有哪些数据库:

SHOW DATABASES;
复制代码

我自己的电脑上安装的MySQL版本是5.7.21,看一下在这个版本里内建了哪些数据库(启动客户端软件,用你的用户名和密码登录后输入命令):

mysql> SHOW DATABASES;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| sys                |
+--------------------+
4 rows in set (0.01 sec)

mysql>
复制代码

可以看到,这一版本的MySQL已经为我们内建了4个数据库,这些数据库都是给MySQL自己使用的,如果我们想使用MySQL存放自己的数据的话,首先需要创建一个属于自己的数据库。

创建数据库

创建数据库的语法贼简单:

CREATE DATABASE 数据库名;
复制代码

来实际操作一下:

mysql> CREATE DATABASE xiaohaizi;
Query OK, 1 row affected (0.00 sec)

mysql>
复制代码

我把我的名字xiaohaizi作为了数据库名称,敲完命令回车之后提示了一个Query OK, 1 row affected (0.00 sec)说明数据库创建成功了。然后我们再用SHOW DATABASES的命令查看一下现在有哪些数据库:

mysql> SHOW DATABASES;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| sys                |
| xiaohaizi          |
+--------------------+
5 rows in set (0.00 sec)

mysql>
复制代码

看到我们自己创建的数据库xiaohaizi就已经在列表里了。

IF NOT EXISTS

我们在一个数据库已经存在的情况下再使用CREATE DATABASE去创建这个数据库会产生错误:

mysql> CREATE DATABASE xiaohaizi;
ERROR 1007 (HY000): Can't create database 'xiaohaizi'; database exists
mysql>
复制代码

执行结果提示了一个ERROR,意思是数据库xiaohaizi已经存在!所以如果我们并不清楚数据库是否存在,可以使用下边的语句来创建数据库:

CREATE DATABASE IF NOT EXISTS 数据库名;
复制代码

这个命令的意思是如果指定的数据库不存在的话就创建它,否则什么都不做。我们试一试:

mysql> CREATE DATABASE IF NOT EXISTS xiaohaizi;
Query OK, 1 row affected, 1 warning (0.00 sec)

mysql>
复制代码

可以看到语句执行成功了,报的ERROR错误也没有了,只是结果中有1个warning而已。这个warning只是MySQL善意的提醒我们数据库xiaohaizi不存在而已,并不会影响语句的执行。

小贴士: 前边说过MySQL的命令可以多条一起执行,可以在黑框框中一次输入多个命令,也可以把好多命令放到一个文件中执行。如果某一条命令的执行结果是产生了一个`ERROR`,MySQL会停止执行该命令之后的命令,但是如果仅仅是在执行结果中有`warning`的话,是不会中断执行的。

切换当前数据库

对于每一个连接到MySQL服务器的客户端,都有一个当前数据库的概念,我们创建的表默认都会被放到当前数据库中,切换当前数据库的命令也贼简单:

USE 数据库名称;
复制代码

所以在介绍创建表之前,我们应该把当前数据库切换到刚刚创建的数据库xiaohaizi上:

mysql> USE xiaohaizi;
Database changed
mysql>
复制代码

看到显示了Database changed说明当前数据库已经切换成功了。需要注意的是,在退出当前客户端之后,也就是你输入了exit或者quit命令之后或者直接把当前的黑框框页面关掉,当你再次调用mysql -h 主机名 -u 用户名 -p 密码的时候,相当于重新开启了一个客户端,需要重新调用USE 数据库名称的命令来选择一下当前数据库。

删除数据库

如果你创建的数据库没用了,我们还可以把它删掉,语法如下:

DROP DATABASE 数据库名;
复制代码

在真实的工作环境里,在删除数据库之前你需要先拿体温计量量是不是发高烧了,然后再找至少两个人核实一下自己是不是发烧了,然后你才敢执行删除数据库的命令。删除数据库意味着里边的表就都被删除了,也就意味着你的数据都没了,所以是个极其危险的操作,使用时需要极其谨慎。不过我们这是学习环境,而且刚刚创建了xiaohaizi数据库,什么表都没往里头放,删了就删了吧:

mysql> DROP DATABASE xiaohaizi;
Query OK, 0 rows affected (0.01 sec)

mysql>
复制代码

然后看一下现在还有哪些数据库:

mysql> SHOW DATABASES;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| sys                |
+--------------------+
4 rows in set (0.01 sec)

mysql>
复制代码

可以看到我们前边创建的xiaohaizi数据库就没有啦。

IF EXISTS

如果某个数据库并不存在,我们仍旧调用DROP TABLE语句去删除它,会报错的:

mysql> DROP DATABASE xiaohaizi;
ERROR 1008 (HY000): Can't drop database 'xiaohaizi'; database doesn't exist
mysql>
复制代码

如果想避免这种报错,可以使用这种形式的语句来删除数据库:

DROP DATABASE IF EXISTS 表名;
复制代码

再次删除一下xiaohaizi

mysql> DROP DATABASE IF EXISTS xiaohaizi;
Query OK, 0 rows affected, 1 warning (0.00 sec)

mysql>
复制代码

这回就不会报错啦!演示完删除数据库的流程之后还是把xiaohaizi数据库创建出来并且切换到当前数据库吧,毕竟我们之后还要在这个数据库里创建各种表呢~

表的操作

数据库建好之后,我们就可以接着创建真正存储数据的表了。创建表的时候首先需要描述清楚这个表长什么样,它有哪些列,这些列都是用来存什么类型的数据等等,这个对表的描述称为表的模式(scheme)。有了表的模式之后,我们就可以着手把数据塞到这个表里了。表中的每一行也叫做一条记录,每一列也叫做一个字段

创建表

基本语法

创建一个表需要至少要完成下列事情:

  1. 给表起个名。
  2. 给表定义一些列,并且给这些列都起个名。
  3. 每一个列都需要定义一种数据类型,来规范它能存储的数据。
  4. 如果有需要的话,可以给这些列定义一些列的属性,比如不许为空,设置默认值等等,这些属性也叫做列的约束性条件,具体有哪些列的属性我们稍后会详细唠叨。

MySQL中创建表的基本语法就是这样的:

CREATE TABLE 表名 (
    列名1    列的类型    [列的属性],
    列名2    列的类型    [列的属性],
    ...
    列名n    列的类型    [列的属性]
);
复制代码

也就是说,我们在CREATE TABLE后写清楚我们定义的表的表名,然后在小括号()中定义上这个表的各个列的信息,包括列的列名、类型,如果有需要的话也可以定义这个列的属性,其中列的属性用中括号[]引起来的意思是可选的,也就是说可以有也可以没有,列名、列的类型、列的属性之间用空白字符分开就好,然后各个列的信息之间用逗号,分隔开。

小贴士: 我们也可以把这个创建表的语句都放在一行里(把换行删掉),分成多行并且加上缩进仅仅是为了美观而已~

废话不多说,赶紧创建一个超级简单的表瞅瞅:

CREATE TABLE first_table (
    first_column INT,
    second_column VARCHAR(100)
);
复制代码

我们新创建的这个表的名字叫做first_table,它有两个列,第一个列的列名是first_column,列的类型是INT,意味着只能存放整数型数据,第二个列的列名是second_column,列的类型是VARCHAR(100),意味着这个列可以存放长度不超过100个字符的字符串。我们在客户端执行一下这个语句(当前数据库是xiaohaizi):

mysql> CREATE TABLE first_table (
    ->     first_column INT,
    ->     second_column VARCHAR(100)
    -> );
Query OK, 0 rows affected (0.02 sec)

mysql>
复制代码

输出Query OK, 0 rows affected (0.02 sec)意味着创建成功了,并且耗时0.02秒。

有了创建first_table的经验,我们就可以着手用MySQL把之前提到的学生基本信息表和成绩表给创建出来了,先把学生信息表搬下来看看:

**学生基本信息表**
学号 姓名 性别 身份证号 学院 专业 入学时间
20180101 杜子腾 158177199901044792 计算机学院 计算机科学与工程 2018/9/1
20180102 杜琦燕 151008199801178529 计算机学院 计算机科学与工程 2018/9/1
20180103 范统 17156319980116959X 计算机学院 软件工程 2018/9/1
20180104 史珍香 141992199701078600 计算机学院 软件工程 2018/9/1

很显然,这个表有学号姓名性别身份证号学院专业入学时间这几个列,其中的学号是整数类型的,入学时间是日期类型的,由于身份证号是固定的18位,我们可以把身份证号这一列定义成固定长度的字符串类型,性别一列只能填,所以我们这里把它定义为ENUM类型的,其余各个字段都是可变的字符串类型的。看一下创建学生基本信息表的语句:

CREATE TABLE student_info (
    number INT,
    name VARCHAR(5),
    sex ENUM('男', '女'),
    id_number CHAR(18),
    department VARCHAR(30),
    major VARCHAR(30),
    enrollment_time DATE
);
复制代码

然后再看一下学生成绩表:

学生成绩表
学号 科目 成绩
20180101 母猪的产后护理 78
20180101 论萨达姆的战争准备 88
20180102 母猪的产后护理 100
20180102 论萨达姆的战争准备 98
20180103 母猪的产后护理 59
20180103 论萨达姆的战争准备 61
20180104 母猪的产后护理 55
20180104 论萨达姆的战争准备 46

这个表有学号科目成绩这几个列,学号成绩是整数类型的,科目是字符串类型的,所以我们创建一下这个表:

CREATE TABLE student_score (
    number INT,
    subject VARCHAR(30),
    score TINYINT
);
复制代码

赶紧到你的客户端里填写创建这两个表的命令吧~

展示当前数据库中的表

我们刚才在xiaohaizi数据库里创建了几个表,那我们怎么查看xiaohaizi数据库下都有哪些表呢?MySQL提供了这样的一个命令:

SHOW TABLES;
复制代码

该命令会展示出当前数据库中都有哪些表,我们执行一下:

mysql> show tables;
+---------------------+
| Tables_in_xiaohaizi |
+---------------------+
| first_table         |
| student_info        |
| student_score       |
+---------------------+
3 rows in set (0.01 sec)

mysql>
复制代码

我们刚才创建的表就都被展示出来了。

IF NOT EXISTS

和重复创建数据库一样,如果创建一个已经存在的表的话是会报错的,我们来试试重复创建一下first_table表:

mysql> CREATE TABLE first_table (
    ->     first_column INT,
    ->     second_column VARCHAR(100)
    -> );
ERROR 1050 (42S01): Table 'first_table' already exists
mysql>
复制代码

执行结果提示了一个ERROR,意思是first_table已经存在!所以如果想要避免这种尴尬,我们可以在创建表的时候使用这种形式:

CREATE TABLE IF NOT EXISTS 表名(
    各个列信息的定义 ...
);
复制代码

加入了IF NOT EXISTS的语句表示如果指定的表名不存在则创建这个表,如果不存在那就什么都不做。我们使用这种IF NOT EXISTS的语法再来创建一遍first_table表:

mysql> CREATE TABLE IF NOT EXISTS first_table (
    ->     first_column INT,
    ->     second_column VARCHAR(100)
    -> );
Query OK, 0 rows affected, 1 warning (0.00 sec)

mysql>
复制代码

可以看到语句执行成功了,报的ERROR错误也没有了,只是结果中有1个warning而已。

简单的表操作语句

在创建完表之后,我们只是创建了一个壳子,里边什么数据都没有。使用表的目的当然是存储数据啦,下边我们来看几个简单的查询与插入语句,更多关于表中数据的操作语句我们之后会详细唠叨的。

简单的查询语句

如果我们想查看某个表里已经存储了哪些数据,可以用下边这个语句:

SELECT * FROM 表名;
复制代码

比如我们想看看前边创建的first_table表中有哪些数据,可以这么写:

mysql> SELECT * FROM first_table;
Empty set (0.01 sec)

mysql>
复制代码

很遗憾,我们从来没有向表中插入过数据,所以查询结果显示的是Empty set,表示什么都没查出来~

简单插入语句

MySQL插入数据的时候是以行为单位的,语法格式如下:

INSERT INTO 表名(列1, 列2, ...) VALUES(列1的值,列2的值, ...);
复制代码

也就是说我们可以在表名后边的括号中指定要插入数据的列,然后在VALUES后边的括号中按指定的列顺序填入对应的值,我们来为first_table表插入第一行数据:

mysql> INSERT INTO first_table(first_column, second_column) VALUES(1, 'aaa');
Query OK, 1 row affected (0.00 sec)

mysql>
复制代码

这个语句的意思就是我们要向first_table表中插入一行数据,first_column列的值是1second_column列的值是'aaa'。看一下现在表中的数据:

mysql> SELECT * FROM first_table;
+--------------+---------------+
| first_column | second_column |
+--------------+---------------+
|            1 | aaa           |
+--------------+---------------+
1 row in set (0.00 sec)

mysql>
复制代码

第一行数据就插入成功了!

我们也可以只指定部分的列,没有显式指定的列的值将被设置为NULL,意思是还没有指定值,比如这样写:

mysql> INSERT INTO first_table(first_column) VALUES(2);
Query OK, 1 row affected (0.00 sec)

mysql> INSERT INTO first_table(second_column) VALUES('ccc');
Query OK, 1 row affected (0.00 sec)

mysql>
复制代码

第一条插入语句我们只指定了first_column列的值是2,而没有指定second_column的值,所以second_column的值就是NULL;第二条插入语句我们只指定了second_column的值是'ccc',而没有指定first_column的值,所以first_column的值就是NULL,也表示没有数据~ 看一下现在表中的数据:

mysql> SELECT * FROM first_table;
+--------------+---------------+
| first_column | second_column |
+--------------+---------------+
|            1 | aaa           |
|            2 | NULL          |
|         NULL | ccc           |
+--------------+---------------+
3 rows in set (0.00 sec)

mysql>
复制代码
批量插入

每插入一行数据写一条语句也不是不行,但是对人来说太烦了,而且每插入一行数据提交一个请求给服务器远没有一次把所有插入的数据提交给服务器效率高,所以MySQL为我们提供了批量插入的语句:

INSERT INTO 表名(列1,列2, ...) VAULES(列1的值,列2的值, ...), (列1的值,列2的值, ...), (列1的值,列2的值, ...), ...;
复制代码

也就是在原来的单条插入语句后边多写几项插入行的内容,用逗号分隔开就好了,举个例子:

mysql> INSERT INTO first_table(first_column, second_column) VALUES(4, 'ddd'), (5, 'eee'), (6, 'fff');
Query OK, 3 rows affected (0.00 sec)
Records: 3  Duplicates: 0  Warnings: 0

mysql> SELECT * FROM first_table;
+--------------+---------------+
| first_column | second_column |
+--------------+---------------+
|            1 | aaa           |
|            2 | NULL          |
|         NULL | ccc           |
|            4 | ddd           |
|            5 | eee           |
|            6 | fff           |
+--------------+---------------+
6 rows in set (0.01 sec)

mysql>
复制代码

可以看到3行记录就插入成功了!

删除表

如果一个表不用了,就可以删掉了,在真实环境中删除表一定要慎重谨慎,失去了的就再也回不来了~ 看一下删除的语法:

DROP TABLE 表1, 表2, ..., 表n;
复制代码

也就是说我们可以同时删除多个表。我们现在把first_table表给删掉看看:

mysql> DROP TABLE first_table;
Query OK, 0 rows affected (0.01 sec)

mysql> SHOW TABLES;
+---------------------+
| Tables_in_xiaohaizi |
+---------------------+
| student_info        |
| student_score       |
+---------------------+
2 rows in set (0.00 sec)

mysql>
复制代码

可以看到现在数据库xiaohaizi中没有了first_table表,说明删除成功了!

IF EXISTS

如果我们尝试删除某个不存在的表的话会报错:

mysql> DROP TABLE first_table;
ERROR 1051 (42S02): Unknown table 'xiaohaizi.first_table'
mysql>
复制代码

提示我们要删除的表并不存在,如果想避免报错,可以使用这种删除语法:

DROP TABLE IF EXISTS 表名;
复制代码

然后再删除一下不存在的first_table表:

mysql> DROP TABLE IF EXISTS first_table;
Query OK, 0 rows affected, 1 warning (0.00 sec)

mysql>
复制代码

这样就不报错了~

约束性条件(列的属性)

对于某些列来说,可能有一些特殊含义或者用法,我们把这些特殊的含义或用法称为列的属性,也可以称为列的约束性条件,在创建表的时候可以显式的定义出来。下边我们看都有哪些约束性条件以及怎么在创建表的时候把它们定义出来。

默认值

前边介绍INSERT语句的时候说过,如果在指定的插入列中省略了某些列,那这些列的值将被设置为NULL,也就是列的默认值为NULL,表示没有设置值。我们在创建表的时候也可以指定一些有意义的默认值,指定方式如下:

列名 列的类型 DEFAULT 默认值
复制代码

比如我们把first_tablesecond_column列的默认值指定为'abc',创建一下这个表:

mysql> CREATE TABLE first_table (
    ->     first_column INT,
    ->     second_column VARCHAR(100) DEFAULT 'abc'
    -> );
Query OK, 0 rows affected (0.02 sec)

mysql>
复制代码

然后插入一条数据后看看默认值是不是起了作用:

mysql> INSERT INTO first_table(first_column) VALUES(1);
Query OK, 1 row affected (0.00 sec)

mysql> SELECT * FROM first_table;
+--------------+---------------+
| first_column | second_column |
+--------------+---------------+
|            1 | abc           |
+--------------+---------------+
1 row in set (0.00 sec)

mysql>
复制代码

我们的插入语句并没有指定second_column的值,但是可以看到插入结果是按照我们规定的默认值'abc'来设置的。

如果我们不设置默认值,其实就相当于指定的默认值为NULL,比如first_table表并没有设置first_column列的默认值,那它的默认值就是NULL,也就是说上边的表定义语句和下边这个是等价的:

CREATE TABLE first_table (
    first_column INT DEFAULT NULL,
    second_column VARCHAR(100) DEFAULT 'abc'
);
复制代码

非空约束

对于某些列,我们要求它们是必填的,也就是不允许存放NULL值,我们用这样的语法来定义这个列:

列名 列的类型 NOT NULL
复制代码

比如我们把first_tablefirst_column列定义为NOT NULL。当然,我们在重新定义表之前需要把原来的表删掉:

mysql> DROP TABLE first_table;
Query OK, 0 rows affected (0.00 sec)

mysql> CREATE TABLE first_table (
    ->     first_column INT NOT NULL,
    ->     second_column VARCHAR(100) DEFAULT 'abc'
    -> );
Query OK, 0 rows affected (0.02 sec)

mysql>
复制代码

这样的话,我们就不能再往这个字段里插入NULL值了,比如这样:

mysql> INSERT INTO first_table(first_column, second_column) VALUES(NULL, 'aaa');
ERROR 1048 (23000): Column 'first_column' cannot be null
mysql>
复制代码

可以看到,弹出了错误提示。

一旦对某个列定义了NOT NULL属性,那这个列的默认值就不为NULL了。上边first_column并没有指定默认值,意味着我们在使用INSERT插入行时必须显式的指定这个列的值,而不能省略它,比如这样就会报错的:

mysql> INSERT INTO first_table(second_column) VALUES('aaa');
ERROR 1364 (HY000): Field 'first_column' doesn't have a default value
mysql>
复制代码

可以看到执行结果提示我们first_column并没有设置默认值,所以在插入数据的时候不能省略掉这个列的值。

主键

有时候在我们的表里可以通过某个列或者某些列确定唯一的一条记录,我们就可以把这个列或者这些列称为候选键。比如在学生信息表student_info中,只要我们知道某个学生的学号,就可以确定一个唯一的学生信息,也就是一条记录。当然,我们也可以通过身份证号来确定唯一的一条学生信息记录,所以学号身份证号都可以作为学生信息表的候选键。在学生成绩表student_score中,我们可以通过学号科目这两个列的组合来确定唯一的一条成绩记录,所以学号、科目这两个列的组合可以作为学生成绩表的候选键

一个表可能有多个候选键,我们可以选择一个候选键作为表的主键。从定义中就可以看出,一个表最多只能有一个主键,主键的值不能重复,通过主键可以找到唯一的一条记录。如果我们的表中有定义主键的需求可以选用下边这两种方式之一来指定主键:

  1. 如果主键只是单个列的话,可以直接在该列后声明PRIMARY KEY,比如我们把学生信息表student_info学号列声明为主键可以这么写:

    CREATE TABLE student_info (
        number INT PRIMARY KEY,
        name VARCHAR(5),
        sex ENUM('男', '女'),
        id_number CHAR(18),
        department VARCHAR(30),
        major VARCHAR(30),
        enrollment_time DATE
    );
    复制代码
  2. 我们也可以把主键的声明单独提取出来,用这样的形式声明:

    PRIMARY KEY (列名1, 列名2, ...)
    复制代码

    然后把这个主键声明放到列定义的后边就好了。比如student_info学号列声明为主键也可以这么写:

    CREATE TABLE student_info (
        number INT,
        name VARCHAR(5),
        sex ENUM('男', '女'),
        id_number CHAR(18),
        department VARCHAR(30),
        major VARCHAR(30),
        enrollment_time DATE,
        PRIMARY KEY (number)
    );
    复制代码

    值得注意的是,对于多个列的组合作为主键的情况,必须使用这种单独声明的形式,比如student_score表里的学号,科目的列组合作为主键,可以这么声明:

    CREATE TABLE student_score (
        number INT,
        subject VARCHAR(30),
        score TINYINT,
        PRIMARY KEY (number, subject)
    );
    复制代码

在我们创建表的时候就声明了主键的话,MySQL会对我们插入的记录做校验,如果两条记录里有相同的主键值的话就会报错

另外,主键列默认是NOT NULL的,也就是必填的,如果填入NULL值会报错(先删除原来的student_info表,使用上边的两种方式之一重新创建表):

mysql> INSERT INTO student_info(number) VALUES(NULL);
ERROR 1048 (23000): Column 'number' cannot be null
mysql>
复制代码

所以大家在插入数据的时候至少别忘了给主键列赋值哈~

唯一性约束

对于不是主键的其他候选键,如果我们也想让MySQL替我们校验数据的唯一性,那我们可以把这个列或列组合声明为UNIQUE的,表明该列或者列组合的值是不允许重复的,这种列的属性叫做唯一性约束。同主键的定义一样,唯一性约束的定义也有两种方式:

  1. 如果具有唯一性约束的列是单个列的话,可以直接在该列后声明UNIQUE或者UNIQUE KEY,比如在学生信息表student_info中,我们不允许两条学生记录中的身份证号是一样的,那我们让id_number这一列具有唯一性约束。

    CREATE TABLE student_info (
        number INT PRIMARY KEY,
        name VARCHAR(5),
        sex ENUM('男', '女'),
        id_number CHAR(18) UNIQUE,
        department VARCHAR(30),
        major VARCHAR(30),
        enrollment_time DATE
    );
    复制代码
  2. 我们也可以把唯一性约束的声明单独提取出来,用这样的形式声明:

    UNIQUE [约束名称] (列名1, 列名2, ...)
    复制代码

    或者:

    UNIQUE KEY [约束名称] (列名1, 列名2, ...)
    复制代码

    其中的约束名称是可选的,其实就是我们为这个唯一性约束起的一个名字而已,如果不起名字的话该名称默认和列名相同,这个不重要哈~ 然后把这个唯一性约束声明放到列定义的后边就好了。比如student_info身份证号列声明唯一性约束的属性也可以这么写:

    CREATE TABLE student_info (
        number INT PRIMARY KEY,
        name VARCHAR(5),
        sex ENUM('男', '女'),
        id_number CHAR(18),
        department VARCHAR(30),
        major VARCHAR(30),
        enrollment_time DATE,
        UNIQUE KEY (id_number)
    );
    复制代码

    值得注意的是,对于多个列的组合具有唯一性约束的情况,必须使用这种单独声明的形式。

如果表中定义了唯一性约束的话,MySQL会对我们插入的记录做校验,如果插入的值违反了唯一性约束的话就会报错

主键和唯一性约束的区别

主键和唯一性约束都能保证某个列或者列组合的唯一性,但是:

  1. 一张表中只能定义一个主键,却可以定义多个唯一性约束
  2. 主键列不允许存放NULL值,而普通的唯一性约束列可以存放NULL值

小贴士: 你可能会问为啥主键列不允许存放NULL值,而普通的唯一性约束列却可以呢?哈哈,这涉及到底层存储的事情,现在你只需要记住这个规定就好了,如果你想知道更多的事情,那就继续往后看呗~

外键

插入到学生成绩表student_score中的学号(number)列中的值必须能在学生基本信息表student_info中的学号列中找到,否则如果一个学号只在成绩表里出现,而在信息表里找不到相应的记录的话,就相当于插入了一个不知道是哪个学生的成绩,这显然是荒谬的。为了防止这样荒谬的情况出现,MySQL给我们提供了外键约束机制。定义外键的语法是这样的:

CONSTRAINT [外键名称] FOREIGN KEY(列1, 列2, ...) REFERENCES 父表名(父列1, 父列2, ...);
复制代码

其中的外键名称也是可选的,一个名字而已有没有都行,不是很重要~ 如果A表中的某个列或者某些列依赖与B表中的某个列或者某些列,那么就称A表为子表,B表为父表。子表和父表可以使用外键来关联起来,上边例子中student_info就是一个父表,student_score就是子表,我们可以这样来定义student_score列,来使用外键关联起父表和子表:

CREATE TABLE student_score (
    number INT,
    subject VARCHAR(30),
    score TINYINT,
    PRIMARY KEY (number, subject),
    CONSTRAINT FOREIGN KEY(number) REFERENCES student_info(number)
);
复制代码

这样,在对student_score表插入数据的时候,MySQL都会为我们检查一下插入的学号是否能在student_info中找到,如果找不到则会报错。

小贴士: 父表中作为外键的列或者列组合必须建立索引,主键和具有唯一性约束的列默认的都建立了索引,置于什么是索引,我们之后会详细唠叨的。

自增

如果一个表中的某个列是数值类型的,包括整数类型和浮点数类型,那么这个列可以设置自增属性。所谓自增,意思是如果我们在插入数据的时候不指定该列的值,那么该列的值就是上一列的值加1后的值,定义语法就是这样:

列名 列的类型 AUTO_INCREMENT
复制代码

比如我们想在first_table里设置一个自增列id,并把这个列设置为主键,来唯一标记一行记录,我们可以这么写:

mysql> DROP TABLE first_table;
Query OK, 0 rows affected (0.00 sec)

mysql> CREATE TABLE first_table (
    ->     id int UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    ->     first_column INT,
    ->     second_column VARCHAR(100) DEFAULT 'abc'
    -> );
Query OK, 0 rows affected (0.01 sec)

mysql>
复制代码

先把原来的表删掉,然后在新表中增加了一个非负int类型的id列,并把它设置为主键而且具有递增属性,那我们插入数据的时候就可以不用管这个列,但是它的值将会递增,看:

mysql> INSERT INTO first_table(first_column, second_column) VALUES(1, 'aaa'), (2, 'bbb'), (3, 'ccc');
Query OK, 3 rows affected (0.00 sec)
Records: 3  Duplicates: 0  Warnings: 0

mysql> SELECT * FROM first_table;
+----+--------------+---------------+
| id | first_column | second_column |
+----+--------------+---------------+
|  1 |            1 | aaa           |
|  2 |            2 | bbb           |
|  3 |            3 | ccc           |
+----+--------------+---------------+
3 rows in set (0.00 sec)

mysql>
复制代码

可以看到,列id是从1开始递增的。在使用递增属性的时候需要注意这几点:

  1. 一个表中最多有一个递增列
  2. 一般只为整数类型的列定义递增属性,浮点数类型基本不用递增属性
  3. 具有AUTO_INCREMENT属性的列必须建立索引。主键和具有唯一性约束的列会自动建立索引,至于什么是索引,我们后边会详细唠叨。
  4. 一般递增列都是作为主键的属性,来自动生成唯一标识一个记录的主键值
  5. 因为具有AUTO_INCREMENT属性的列是从1开始递增的,所以最好用UNSIGNED来修饰这个列,可以提升正数的表示范围

约束性条件的组合

每个列可以有多个约束性条件,声明的顺序无所谓,各个约束性条件之间用空白隔开就好了~

小贴士: 注意,有的约束性条件是冲突的,一个列不能具有两个冲突的约束性条件,比如一个列不能既声明为`PRIMARY KEY,又声明为UNIQUE KEY`,不能既声明为`DEFAULT NULL`,又声明为`NOT NULL`。大家在使用过程中需要注意这一点。

添加注释

我们可以对每个列信息以及表作注释,具体语法如下:

CREATE TABLE 表名 (
    列1 列的类型 [列的属性] COMMENT '列1的注释信息',
    列2 列的类型 [列的属性] COMMENT '列2的注释信息',
    列3 列的类型 [列的属性] COMMENT '列3的注释信息',
) COMMENT '表的注释信息';
复制代码

比如我们可以这样创建我们的first_table表:

CREATE TABLE first_table (
    id int UNSIGNED AUTO_INCREMENT PRIMARY KEY COMMENT '自增主键',
    first_column INT COMMENT '第一列',
    second_column VARCHAR(100) DEFAULT 'abc' COMMENT '第二列'
) COMMENT '第一个表';
复制代码

注释没必要太长,言简意赅即可,毕竟是给人看的,让人看明白是个啥意思就OK了。为了我们自己的方便,也为了阅读你创建的人的方便,请遵守一下职业道德,写个注释吧~ 求求你了~ 求求你了~ 求求你了~

标识符的命名

像数据库名、表名、列名、约束名称或者我们之后会遇到的别的名称,这些我们起的名字统统被称为标识符。虽然MySQL中对标识符的命名没多少限制,但是却不欢迎下边的这几种命名:

  1. 名称中全都是数字。

    因为在一些MySQL命令中也会使用到数字,如果你起的名称中全部都是数字,会让MySQL服务器分别不清哪个是名称,哪个是数字了。比如名称1234567就是非法的。

  2. 名称中有空白字符

    MySQL命令是靠空白字符来分隔各个单词的,比如下边这两行命令是等价的:

    CREATE DATABASE xiaohaizi;
    CREATE   DATABASE   xiaohaizi;
    复制代码

    但是如果你定义的名称中有空白字符,这样会被当作两个词去处理,就会造成歧义。比如名称word1 word2 word3就是非法的。

  3. 名称使用了MySQL中的保留字

    MySQL中有很多保留的词是会被当作命令处理的,比如CREATEDATABASEDROPTABLE等等等等,我们稍后还会介绍大量的命令,这些命令用到的单词都是作为MySQL的保留字,如果你的名称用到了这些词儿也会导致歧义。比如名称create就是非法的。

虽然某些名称可能会导致歧义,但是如果你坚持要使用的话,也不是不行,你可以使用反引号``来将你定义的名称扩起来,这样MySQL的服务器就能检测到你提供的是一个名称而不是别的什么东西,比如说把上边几个非法的名称加上反引号``就变成合法的名称了:

`1234567`
`word1 word2    word3`
`create`
复制代码

我们上边对表first_table的定义可以把里边的标识符全都使用反引号``引起来,这样语义更清晰一点:

CREATE TABLE `first_table` (
    `id` int UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    `first_column` INT,
    `second_column` VARCHAR(100) DEFAULT 'abc'
);
复制代码

虽然反引号比较强大,但是我们还是建议大家不要起各种非主流的名称,也不要使用全数字、带有空白字符或者MySQL保留字的名称。由于MySQL是C语言实现的,所以在名称定义上还是尽量遵从C语言的规范吧,就是用小写字母、数字、下划线、美元符号等作为名称,如果有多个单词的话,各个单词之间用下划线连接起来,比如studentstudent_info啥的~

查看表结构

有时候我们可能忘记了自己定义的表的结构,可以使用下边这些语句来查看,它们起到的效果都是一样的:

DESCRIBE 表名;
DESC 表名;
EXPLAIN 表名;
SHOW COLUMNS FROM 表名;
SHOW FIELDS FROM 表名;
复制代码

比如我们看一下student_info这个表的结构:

mysql> DESC student_info;
+-----------------+-------------------+------+-----+---------+-------+
| Field           | Type              | Null | Key | Default | Extra |
+-----------------+-------------------+------+-----+---------+-------+
| number          | int(11)           | NO   | PRI | NULL    |       |
| name            | varchar(5)        | YES  |     | NULL    |       |
| sex             | enum('男','女')   | YES  |     | NULL    |       |
| id_number       | char(18)          | YES  | UNI | NULL    |       |
| department      | varchar(30)       | YES  |     | NULL    |       |
| major           | varchar(30)       | YES  |     | NULL    |       |
| enrollment_time | date              | YES  |     | NULL    |       |
+-----------------+-------------------+------+-----+---------+-------+
7 rows in set (0.00 sec)

mysql>
复制代码

可以看到,这个student_info表的各个列的类型和属性就都展示出来了,其中PRIPRIMARY KEY的缩写,UNIUNIQUE KEY的缩写。

小贴士: 请注意`number`列的类型是`int(11)`,这个小括号里的`11`是什么意思?这个`11`其实是所谓的显示宽度,我们稍后马上唠叨。

如果你看不惯这种详细展示各个字段的的信息,我们还可以使用下边这个语句来查看表结构:

SHOW CREATE TABLE 表名;
复制代码

比如:

mysql> SHOW CREATE TABLE student_info;
+--------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table        | Create Table                                                                                                                                                                                                                                                                                                                                                                                        |
+--------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| student_info | CREATE TABLE `student_info` (
  `number` int(11) NOT NULL,
  `name` varchar(5) DEFAULT NULL,
  `sex` enum('男','女') DEFAULT NULL,
  `id_number` char(18) DEFAULT NULL,
  `department` varchar(30) DEFAULT NULL,
  `major` varchar(30) DEFAULT NULL,
  `enrollment_time` date DEFAULT NULL,
  PRIMARY KEY (`number`),
  UNIQUE KEY `id_number` (`id_number`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8   |
+--------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

mysql>
复制代码

由于这行数据太长了,所以输出效果并不是很好,所以把原来用于标记语句结束的分号;改为\G,这样的效果可能好点:

mysql> SHOW CREATE TABLE student_info\G
*************************** 1. row ***************************
       Table: student_info
Create Table: CREATE TABLE `student_info` (
  `number` int(11) NOT NULL,
  `name` varchar(5) DEFAULT NULL,
  `sex` enum('男','女') DEFAULT NULL,
  `id_number` char(18) DEFAULT NULL,
  `department` varchar(30) DEFAULT NULL,
  `major` varchar(30) DEFAULT NULL,
  `enrollment_time` date DEFAULT NULL,
  PRIMARY KEY (`number`),
  UNIQUE KEY `id_number` (`id_number`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
1 row in set (0.00 sec)

mysql>
复制代码

可以看到,使用SHOW CREATE TABLE这种语句展示出来的表结构就是我们平时创建表的语句。而且MySQL默认为主键字段加了NOT NULL属性,为其他字段加了DEFAULT NULL属性,而且还给身份证号的唯一性约束起了和列名一样的名称。

小贴士: 你可能疑惑的是在表定义末尾的`ENGINE=InnoDB DEFAULT CHARSET=utf8`是什么意思,这个是指定表的存储引擎和默认字符集,这些内容后边会有专门的专题来详细唠叨的,你现在不用关心~

ZEROFILL与显示宽度

对于无符号整数类型的列,我们可以在查询数据的时候让数字左边补0,如果想实现这个效果需要给该列加一个ZEROFILL属性,就像这样:

mysql> CREATE TABLE zero_table (
    ->     i1 INT UNSIGNED ZEROFILL,
    ->     i2 INT UNSIGNED
    -> );
Query OK, 0 rows affected (0.03 sec)

mysql>
复制代码

我们在zero_table表中创建了两个整数列,不同的是i1列具有ZEROFILL属性,下边我们为这个表插入一条记录:

mysql> INSERT INTO zero_table(i1, i2) VALUES(1, 1);
Query OK, 1 row affected (0.00 sec)

mysql>
复制代码

然后我们使用查询语句来显示一下刚插入的数据:

mysql> SELECT * FROM zero_table;
+------------+------+
| i1         | i2   |
+------------+------+
| 0000000001 |    1 |
+------------+------+
1 row in set (0.00 sec)

mysql>
复制代码

对于具有ZEROFILL属性的i1列,在显示的时候补了一堆0,仔细数数发现是9个0,而没有ZEROFILL属性的i2列,在显示的时候并没有补0。为什么i1列会补9个0呢?我们查看一下zero_table的表结构:

mysql> DESC zero_table;
+-------+---------------------------+------+-----+---------+-------+
| Field | Type                      | Null | Key | Default | Extra |
+-------+---------------------------+------+-----+---------+-------+
| i1    | int(10) unsigned zerofill | YES  |     | NULL    |       |
| i2    | int(10) unsigned          | YES  |     | NULL    |       |
+-------+---------------------------+------+-----+---------+-------+
2 rows in set (0.01 sec)

mysql>
复制代码

可以看到,其实i1i2列的类型INT后边都加了一个(10),这个10就是所谓的显示宽度显示宽度是在查询语句显示的结果中,如果声明了 ZEROFILL 属性的整数列的实际值的位数小于显示宽度时,会在实际值的左侧补0,使补0的位数和实际值的位数相加正好等于显示宽度。我们也可以自己指定显示宽度,比方说这样:

mysql> DROP TABLE zero_table;
Query OK, 0 rows affected (0.00 sec)

mysql> CREATE TABLE zero_table (
    ->     i1 INT(5) UNSIGNED ZEROFILL,
    ->     i2 INT UNSIGNED
    -> );
Query OK, 0 rows affected (0.02 sec)

mysql> INSERT INTO zero_table(i1, i2) VALUES(1, 1);
Query OK, 1 row affected (0.01 sec)

mysql> SELECT * FROM zero_table;
+-------+------+
| i1    | i2   |
+-------+------+
| 00001 |    1 |
+-------+------+
1 row in set (0.00 sec)

mysql>
复制代码

新创建的表中,i1字段的显示宽度是5,所以最后的显示结果中补了4个0。

注意事项

  1. 在查询时,数据自动补0的条件有这几个

    • 该列必须是整数类型的
    • 该列必须有UNSIGNED ZEROFILL的属性
    • 该列的实际值的位数必须小于显示宽度
  2. 在创建表的时候,如果声明了ZEROFILL属性的列没有声明UNSIGNED属性,那MySQL会为该列自动生成UNSIGNED属性

    也就是说如果我们创建表语句是这样的:

    CREATE TABLE zero_table (
        i1 INT ZEROFILL,
        i2 INT UNSIGNED
    );
    复制代码

    MySQL会自动帮我们为i1列加上UNSIGNED属性,也就是这样:

    CREATE TABLE zero_table (
        i1 INT UNSIGNED ZEROFILL,
        i2 INT UNSIGNED
    );
    复制代码
  3. 每个整数类型都会有默认的显示宽度

    比如TINYINT的默认显示宽度是4INT的默认显示宽度是(11)... 如果加了UNSIGNED属性,则该类型的显示宽度减1,比如TINYINT UNSIGNED的显示宽度是3INT UNSIGNED的显示宽度是11...

  4. 显示宽度并不会影响实际类型的实际存储空间

    也就是说INT(1)INT(10)其实并没有什么区别,比方说zero_table表中i1列的显示宽度是5,而数字12345678的位数是8,它照样可以被填入i1列中:

    mysql> INSERT INTO zero_table(i1, i2) VALUES(12345678, 12345678);
    Query OK, 1 row affected (0.01 sec)
    复制代码
  5. 对于没有声明ZEROFILL属性的列,显示宽度没有一毛钱卵用

    记住,只有在查询声明了ZEROFILL属性的列时,显示宽度才会起作用,否则忽略显示宽度这个东西的存在。

  6. 只有列的实际值的位数小于显示宽度时才会补0,实际值的位数大于显示宽度时照原样输出

    比方说我们刚刚把12345678存到了i1列里,在展示这个值时,并不会截短显示的数据,而是照原样输出:

    mysql> SELECT * FROM zero_table;
    +----------+----------+
    | i1       | i2       |
    +----------+----------+
    |    00001 |        1 |
    | 12345678 | 12345678 |
    +----------+----------+
    2 rows in set (0.00 sec)
    
    mysql>
    复制代码

没有选择默认数据库时对表的操作

有时候我们并没有使用USE语句来选择当前的数据库,或者在一条语句中遇到的表分散在不同的数据库中,我们就必须显式的指定这些表所属的数据库了。比如不管当前数据库是不是xiaohaizi,我们都可以调用这个语句来展示数据库xiaohaizi里边的表:

mysql> SHOW TABLES FROM xiaohaizi;
+---------------------+
| Tables_in_xiaohaizi |
+---------------------+
| first_table         |
| student_info        |
| student_score       |
+---------------------+
3 rows in set (0.00 sec)

mysql>
复制代码

其他地方如果使用到表名的话,需要显式指定这个表所属的数据库,指明方式是这样的:

数据库名.表名
复制代码

比如我们需要查询first_table表中的数据,可以这么写:

SELECT * FROM xiaohaizi.first_table;
复制代码

查看表结构:

SHOW CREATE TABLE xiaohaizi.first_table\G
复制代码

在其他不确定当前数据库的情况下,使用到表的地方也都需要加上所属的数据库名,就不一一列举了。

修改表

在表创建好之后如果对表的结构不满意,比如想增加或者删除一列,想修改某一列的数据类型或者约束性条件,想对表名或者列名进行重命名,这些操作统统都算是修改表结构。MySQL给我们提供了一系列修改表结构的语句。

修改表名

我们可以通过下边这两种方式来修改表的名称:

  1. 方式一:

    ALTER TABLE 旧表名 RENAME TO 新表名;
    复制代码

    我们把first_table表的名称修改为first_table1

    mysql> ALTER TABLE first_table RENAME TO first_table1;
    Query OK, 0 rows affected (0.01 sec)
    
    mysql> SHOW TABLES;
    +---------------------+
    | Tables_in_xiaohaizi |
    +---------------------+
    | first_table1        |
    | student_info        |
    | student_score       |
    +---------------------+
    3 rows in set (0.00 sec)
    
    mysql>
    复制代码

    通过SHOW TABLES命令可以看到已经改名成功了。

  2. 方式二:

    RENAME TABLE 旧表名1 TO 新表名1, 旧表名2 TO 新表名2, ... 旧表名n TO 新表名n;
    复制代码

    这种改名方式的牛逼之处就是它可以在一条语句中修改多个表的名称。这里就不举例了,自己测试一下吧。

如果在修改表名的时候指定了数据库名,还可以转移表所属的数据库,我们先再创建一个数据库dahaizi

mysql> CREATE DATABASE dahaizi;
Query OK, 1 row affected (0.00 sec)

mysql>
复制代码

然后把first_table1表转移到这个数据库下:

mysql> ALTER TABLE xiaohaizi.first_table1 RENAME TO dahaizi.first_table1;
Query OK, 0 rows affected (0.01 sec)

mysql> SHOW TABLES FROM dahaizi;
+-------------------+
| Tables_in_dahaizi |
+-------------------+
| first_table1      |
+-------------------+
1 row in set (0.00 sec)

mysql> SHOW TABLES FROM xiaohaizi;
+---------------------+
| Tables_in_xiaohaizi |
+---------------------+
| student_info        |
| student_score       |
+---------------------+
2 rows in set (0.00 sec)

mysql>
复制代码

可以看到first_table1就从数据库xiaohaizi转移到dahaizi里边了。我们再换一种方式把它转回来,并且更名为first_table

mysql> RENAME TABLE dahaizi.first_table1 TO xiaohaizi.first_table;
Query OK, 0 rows affected (0.00 sec)

mysql>
复制代码

增加列

我们可以使用下边的语句来增加表中的列:

ALTER TABLE 表名 ADD COLUMN 列名 列的类型 [列的属性];
复制代码

比如我们向first_table里添加一个名叫third_column的列就可以这么写:

mysql> ALTER TABLE first_table ADD COLUMN third_column CHAR(4) DEFAULT '1234' COMMENT '第三列';
Query OK, 0 rows affected (0.02 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> DESC first_table;
+---------------+------------------+------+-----+---------+----------------+
| Field         | Type             | Null | Key | Default | Extra          |
+---------------+------------------+------+-----+---------+----------------+
| id            | int(10) unsigned | NO   | PRI | NULL    | auto_increment |
| first_column  | int(11)          | YES  |     | NULL    |                |
| second_column | varchar(100)     | YES  |     | abc     |                |
| third_column  | char(4)          | YES  |     | 1234    |                |
+---------------+------------------+------+-----+---------+----------------+
4 rows in set (0.00 sec)

mysql>
复制代码

通过查看表的结构可以看到该列已经添加成功了。

增加列到特定位置

默认的情况下列都是加到现有列的最后一列,我们也可以在添加列的时候指定它的位置,具体有两种方式:

  1. 添加到第一列:

    ALTER TABLE 表名 ADD COLUMN 列名 列的类型 [列的属性] FIRST;
    复制代码

    让我们把fourth_column插入到第一列:

    mysql> ALTER TABLE first_table ADD COLUMN fourth_column CHAR(4) DEFAULT '1234' COMMENT '第四列' FIRST;
    Query OK, 0 rows affected (0.05 sec)
    Records: 0  Duplicates: 0  Warnings: 0
    
    mysql> DESC first_table;
    +---------------+------------------+------+-----+---------+----------------+
    | Field         | Type             | Null | Key | Default | Extra          |
    +---------------+------------------+------+-----+---------+----------------+
    | fourth_column | char(4)          | YES  |     | 1234    |                |
    | id            | int(10) unsigned | NO   | PRI | NULL    | auto_increment |
    | first_column  | int(11)          | YES  |     | NULL    |                |
    | second_column | varchar(100)     | YES  |     | abc     |                |
    | third_column  | char(4)          | YES  |     | 1234    |                |
    +---------------+------------------+------+-----+---------+----------------+
    5 rows in set (0.00 sec)
    
    mysql>
    复制代码

    看到插入成功了。

  2. 添加到指定列的后边:

    ALTER TABLE 表名 ADD COLUMN 列名 列的类型 [列的属性] AFTER 指定列名;
    复制代码

    再插入一个fifth_columnfirst_column后边瞅瞅:

    mysql> ALTER TABLE first_table ADD COLUMN fifth_column CHAR(4) DEFAULT '1234' COMMENT '第五列' AFTER first_column;
    Query OK, 0 rows affected (0.05 sec)
    Records: 0  Duplicates: 0  Warnings: 0
    
    mysql> DESC first_table;
    +---------------+------------------+------+-----+---------+----------------+
    复制代码

| Field | Type | Null | Key | Default | Extra | +---------------+------------------+------+-----+---------+----------------+ | fourth_column | char(4) | YES | | 1234 | | | id | int(10) unsigned | NO | PRI | NULL | auto_increment | | first_column | int(11) | YES | | NULL | | | fifth_column | char(4) | YES | | 1234 | | | second_column | varchar(100) | YES | | abc | | | third_column | char(4) | YES | | 1234 | | +---------------+------------------+------+-----+---------+----------------+ 6 rows in set (0.00 sec)

mysql>
```
`fifth_column`列就被插到`first_column`列后边了。
复制代码

删除列

我们可以使用下边的语句来删除表中的列:

ALTER TABLE DROP COLUMN 列名;
复制代码

我们把刚才向first_table里添加几个列都删掉试试:

mysql> ALTER TABLE first_table DROP COLUMN third_column;
Query OK, 0 rows affected (0.05 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> ALTER TABLE first_table DROP COLUMN fourth_column;
Query OK, 0 rows affected (0.05 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> ALTER TABLE first_table DROP COLUMN fifth_column;
Query OK, 0 rows affected (0.04 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> DESC first_table;
+---------------+------------------+------+-----+---------+----------------+
| Field         | Type             | Null | Key | Default | Extra          |
+---------------+------------------+------+-----+---------+----------------+
| id            | int(10) unsigned | NO   | PRI | NULL    | auto_increment |
| first_column  | int(11)          | YES  |     | NULL    |                |
| second_column | varchar(100)     | YES  |     | abc     |                |
+---------------+------------------+------+-----+---------+----------------+
3 rows in set (0.01 sec)

mysql>
复制代码

可以看到删除成功了。

修改列信息

修改列的信息有下边这两种方式:

  1. 方式一:

    ALTER TABLE 表名 MODIFY 列名 新数据类型 [新属性];
    复制代码

    我们来修改一下first_table表的second_column列,把它修改为VARCHAR(2)以及具有NOT NULL约束条件:

    mysql> ALTER TABLE first_table MODIFY second_column VARCHAR(2) NOT NULL COMMENT '第二列';
    ERROR 1406 (22001): Data too long for column 'second_column' at row 1
    mysql>
    复制代码

    咦,看到报了个错,意思是second_column列里存放的数据太大了,我们看看都存了哪些数据:

    mysql> SELECT * FROM first_table;
    +----+--------------+---------------+
    | id | first_column | second_column |
    +----+--------------+---------------+
    |  1 |            1 | aaa           |
    |  2 |            2 | bbb           |
    |  3 |            3 | ccc           |
    +----+--------------+---------------+
    3 rows in set (0.00 sec)
    
    mysql>
    复制代码

    原来存放的数据是'aaa''bbb''ccc',它们都是包含3个字符的字符串。现在我们要把second_column列的数据类型改为VARCHAR(2),而VARCHAR(2)最多只能存放两个字符,所以才会报错了!知道了错误原因后,我们把类型改为VARCHAR(3)试试:

    mysql> ALTER TABLE first_table MODIFY second_column VARCHAR(3) NOT NULL COMMENT '第二列';
    Query OK, 3 rows affected (0.03 sec)
    Records: 3  Duplicates: 0  Warnings: 0
    
    mysql> DESC first_table;
    +---------------+------------------+------+-----+---------+----------------+
    复制代码

| Field | Type | Null | Key | Default | Extra | +---------------+------------------+------+-----+---------+----------------+ | id | int(10) unsigned | NO | PRI | NULL | auto_increment | | first_column | int(11) | YES | | NULL | | | second_column | varchar(3) | NO | | NULL | | +---------------+------------------+------+-----+---------+----------------+ 3 rows in set (0.00 sec)

mysql>
```
好了,这回就对了。这个过程也提醒我们:<span style="color:red">不能随便修改列信息,修改后的数据类型和属性一定要兼容表中现有的数据!</span>。
复制代码
  1. 方式二:

    ALTER TABLE 表名 CHANGE 旧列名 新列名 新数据类型 [新属性];
    复制代码

    可以看到这种修改方式需要我们填两个列名,也就是说在修改数据类型和属性的同时也可以修改列名!比如我们修改second_column的列名为second_column1

    mysql> ALTER TABLE first_table CHANGE second_column second_column1 VARCHAR(3) NOT NULL COMMENT '第二列';
    Query OK, 0 rows affected (0.01 sec)
    Records: 0  Duplicates: 0  Warnings: 0
    
    mysql> DESC first_table;
    +----------------+------------------+------+-----+---------+----------------+
    复制代码

| Field | Type | Null | Key | Default | Extra | +----------------+------------------+------+-----+---------+----------------+ | id | int(10) unsigned | NO | PRI | NULL | auto_increment | | first_column | int(11) | YES | | NULL | | | second_column1 | varchar(3) | NO | | NULL | | +----------------+------------------+------+-----+---------+----------------+ 3 rows in set (0.00 sec)

mysql>
```
我们只是把`second_column`的列名修改了一下而已,并没有改动它的数据类型和属性,所以直接把旧的数据类型和属性抄过来就好了,可以看到结果名称已经被修改了。
复制代码
修改列排列位置

如果我们觉得当前列的顺序有问题的话,可以使用下边这几条语句进行修改:

  1. 将列设为表的第一列:

    ALTER TABLE 表名 MODIFY 列名 列的类型 列的属性 FIRST;
    复制代码

    先看一下现在表first_table的各个列的排列顺序:

    mysql> DESC first_table;
    +----------------+------------------+------+-----+---------+----------------+
    复制代码

| Field | Type | Null | Key | Default | Extra | +----------------+------------------+------+-----+---------+----------------+ | id | int(10) unsigned | NO | PRI | NULL | auto_increment | | first_column | int(11) | YES | | NULL | | | second_column1 | varchar(3) | NO | | NULL | | +----------------+------------------+------+-----+---------+----------------+ 3 rows in set (0.01 sec)

mysql>
```
可以看到,列的顺序依次是: `id`、`first_column`、`second_column1`。现在我们想把`first_column`放在第一列可以这么写:
```
mysql>  ALTER TABLE first_table MODIFY first_column int(11)  COMMENT '第一列' FIRST;
Query OK, 0 rows affected (0.04 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> DESC first_table;
+----------------+------------------+------+-----+---------+----------------+
复制代码

| Field | Type | Null | Key | Default | Extra | +----------------+------------------+------+-----+---------+----------------+ | first_column | int(11) | YES | | NULL | | | id | int(10) unsigned | NO | PRI | NULL | auto_increment | | second_column1 | varchar(3) | NO | | NULL | | +----------------+------------------+------+-----+---------+----------------+ 3 rows in set (0.00 sec)

mysql>
```
看到`first_column`已经成为第一列了!
复制代码
  1. 将列放到指定列的后边:

    ALTER TABLE 表名 MODIFY 列名 列的类型 列的属性 after 指定列名;
    复制代码

    比方说我们想把first_column放到second_column1后边可以这么写:

    mysql> ALTER TABLE first_table MODIFY first_column int(11)  COMMENT '第一列' AFTER second_column1;
    Query OK, 0 rows affected (0.04 sec)
    Records: 0  Duplicates: 0  Warnings: 0
    
    mysql> DESC first_table;
    +----------------+------------------+------+-----+---------+----------------+
    复制代码

| Field | Type | Null | Key | Default | Extra | +----------------+------------------+------+-----+---------+----------------+ | id | int(10) unsigned | NO | PRI | NULL | auto_increment | | second_column1 | varchar(3) | NO | | NULL | | | first_column | int(11) | YES | | NULL | | +----------------+------------------+------+-----+---------+----------------+ 3 rows in set (0.00 sec)

mysql>
```
复制代码

一条语句中包含多个修改操作

如果对同一个表有多个修改操作的话,我们可以把它们放到一条语句中执行,就像这样:

ALTER TABLE 表名 操作1, 操作2, ..., 操作n;
复制代码

上边我们在演示删除列操作的时候用三条语句连着删了third_columnfourth_columnfifth_column这三个列,其实这三条语句可以合并为一条:

ALTER TABLE first_table DROP COLUMN third_column, DROP COLUMN fourth_column, DROP COLUMN fifth_column;
复制代码

这样人敲的命令也少了,服务器也不用分多次执行效率也高了,何乐而不为呢?

总结

  1. MySQL服务器可以包含若干数据库,每个数据库中可以包含若干个表。

  2. 数据库相关操作如下:

    • 展示数据库:SHOW DATABASES;
    • 创建数据库:CREATE DATABASE 数据库名;
    • 切换当前数据库:USE 数据库名
    • 删除数据库:DROP DATABASE 数据库名
  3. 表结构的相关操作如下:

    • 创建表:

      CREATE TABLE 表名 (
          列名, 列的类型, [列的属性] COMMENT '列的注释',
          ... (若干个列的信息)
      ) COMMENT '表的注释';
      复制代码
    • 删除表:DROP TABLE 表名;

    • 修改表:

      • 修改表名:

        • 方式一:

          ALTER TABLE 旧表名 RENAME TO 新表名;
          复制代码
        • 方式二:

          RENAME TABLE 旧表名1 TO 新表名1, 旧表名2 TO 新表名2, ... 旧表名n TO 新表名n;
          复制代码
      • 添加列:

        ALTER TABLE 表名 ADD COLUMN 列名 列的类型 [列的属性] [FIRST|AFTER 指定列名];
        复制代码
      • 删除列:

        ALTER TABLE DROP COLUMN 列名;
        复制代码
      • 修改列信息:

        • 方式一:

          ALTER TABLE 表名 MODIFY 列名 新数据类型 [新属性] [FIRST|AFTER 指定列名];
          复制代码
        • 方式二:

          ALTER TABLE 表名 CHANGE 旧列名 新列名 新数据类型 [新属性] [FIRST|AFTER 指定列名];
          复制代码
  4. 各种约束性条件

    • 默认值:在插入语句中没有指定该列的值的情况下,使用默认值,声明语法如下:

      列名 列的类型 DEFAULT 默认值
      复制代码
    • 非空约束:声明了该属性的列不允许插入NULL值,声明语法:

      列名 列的类型 NOT NULL
      复制代码
    • 主键:唯一标识一条记录,并且一个表中最多字能有一个主键,主键值不能为NULL,声明语法:

      • 方式一:

        列名 列的类型 PRIMARY KEY
        复制代码
      • 方式二:

        PRIMARY KEY (列名1, 列名2, ...)
        复制代码
    • 唯一性约束:唯一标识一条记录,一个表中可以有多个唯一性约束,并且值可以为NULL,声明语法:

      • 方式一:

        列名 列的类型 UNIQUE [KEY]
        复制代码
      • 方式二:

        UNIQUE [KEY] [约束名称] (列名1, 列名2, ...)
        复制代码
    • 外键:表A的某个列或列组合的值依赖表B的某个列或列组合的值,则成表A为子表,表B为父表,表A的该列或者列组合称为外键,声明外键的方式如下:

      CONSTRAINT [外键名称] FOREIGN KEY(列1, 列2, ...) REFERENCES 父表名(父列1, 父列2, ...);
      复制代码
    • 自增:在插入语句没有包含自增列的情况下,该列的值会递增,声明方式如下:

      列名 列的类型 AUTO_INCREMENT
      复制代码

小册

本系列专栏都是MySQL入门知识,想看进阶知识可以到小册中查看:《MySQL是怎样运行的:从根儿上理解MySQL》的链接 。小册的内容主要是从小白的角度出发,用比较通俗的语言讲解关于MySQL进阶的一些核心概念,比如记录、索引、页面、表空间、查询优化、事务和锁等,总共的字数大约是三四十万字,配有上百幅原创插图。主要是想降低普通程序员学习MySQL进阶的难度,让学习曲线更平滑一点~

关注下面的标签,发现更多相似文章
评论