MySQL 数据库设计及步骤

实体和实体之间的关系

一对多 1:N(多对一 N:1)

主表中的一条记录对应从表中的多条记录

1、 主键和非主键之间的关系就能实现一对多。
2、 一对多和多对一是一样的。

问题:几个一对多的关系?
1、 客户和订单
2、 省和市的关系
3、 商品类别和商品

一对一(1:1)

主表中的一条记录对应从表中的一条记录

主键和主键建关系就能实现一对一。

思考:一对一两个表完全可以用一个表实现,为什么还要分成两个表?
答:在字段数量很多情况下,数据量也就很大,每次查询都需要检索大量数据,这样效率低下。我们可以将所有字段分成两个部分,“常用字段”和“不常用字段”,这样对大部分查询者来说效率提高了。【表的垂直分割】

多对多(N:M)

班级和讲师的关系
科目和学生的关系

只要是多对多,必须有第三张关系表来保存关系

数据库设计的步骤

目的:
数据冗余、空间浪费—》节省数据的存储空间
内存空间浪费、数据不完整–》保证数据的完整性
数据更新与插入异常–》方便进行数据库应用系统的开发

需求分析=>建模=>模型转换=>规范化

需求分析阶段:分析客户的业务和数据处理需求;
概要设计阶段:设计数据库的E-R模型图,确认需求信息的正确和完整;
详细设计阶段:将E-R图转换为多张表,进行逻辑设计,并应用数据库设计的三大范式进行审核;
代码编写阶段:选择具体数据库进行物理实现,并编写代码实现前端应用;
软件测试阶段:……
安装部署:……

数据库设计具体步骤

1、 收集信息:与该系统有关人员进行交流、坐谈,充分理解数据库需要完成的任务
2、 标识对象(实体-Entity)标识数据库要管理的关键对象或实体
3、 标识每个实体的属性(Attribute)
4、 标识对象之间的关系(Relationship)

项目需求
BBS论坛的基本功能:
1、用户注册和登录,后台数据库需要存放用户的注册信息和在线状态信息;
2、用户发贴,后台数据库需要存放贴子相关信息,如贴子内容、标题等;
3、用户可以对发帖进行回复【跟帖】;
4、论坛版块管理:后台数据库需要存放各个版块信息,如版主、版块名称、贴子数等;

标识实体
实体一般是名词:
用户:论坛普通用户、各版块的版主。
用户发的主贴
用户发的跟贴(回贴)
版块:论坛的各个版块信息

标识实体的属性
论坛用户:
昵称、密码、邮件、生日、性别、等级、备注、注册日期、状态、积分
主贴:
发帖人、发帖表情、回复数量、标题、正文、发帖时间、点击数、状态
回帖:
帖子编号、回帖人、表情、标题、正文、回帖时间、点击数
板块:
板块名称、版主、本版格言、点击率、发帖数

标识实体和实体之间的关系
一个版块有多个主贴
一个主贴有多个跟帖
一个用户可以管理多个版块
一个用户可以发多个主贴
一个用户可以回复多个跟帖

绘制E-R图

E-R(Entity-Relationship)实体关系图

E-R图的语法
方形:实体,一般是名词
椭圆:属性,一般是名词
菱形:关系,一般是动词

将E-R图转成表
1、 实体转成表,属性转成字段
2、 如果没有合适的字段做主键,给表添加一个自动增长列做主键。

数据规范化

Codd博士定义了6个范式来规范化数据库,范式由小到大来约束,范式越高冗余越小,但表的个数也越多。实验证明,三范式是性价比最高的

第一范式确保每个字段不可再分

说明:Address由country和city构成,将Address分成country和city。

思考:地址包含省、市、县、地区是否需要拆分?
答:如果仅仅起地址的作用,不需要统计,可以不拆分;如果有按地区统计的功能需要拆分。
在实际项目中拆分!

第二范式:非键字段必须依赖于键字段

一个表只能描述一件事
非键字段必须依赖于键字段

第三范式:消除传递依赖

在所有的非键字段中,不能有传递依赖

例如在非主键中,“学号”和”姓名“有依赖,将”姓名“删除
例如顾客编号依赖于顾客姓名
总分数依赖于语文和数学的分数
在非主键字段中,要消除传递依赖

上面的设计不满足第三范式,但是高考分数表就是这样设计的,为什么?
答:高考分数峰值访问量非常大,这时候就是性能更重要。当性能和规范化冲突的时候,我们首选性能。这就是“反三范式”。

例题演练

需求

假设某建筑公司要设计一个数据库。公司的业务规则概括说明如下:
1、公司承担多个工程项目,每一项工程有:工程号、工程名称、施工人员等
2、公司有多名职工,每一名职工有:职工号、姓名、性别、职务(工程师、技术员)等
3、公司按照工时和小时工资率支付工资,小时工资率由职工的职务决定(例如,技术员的小时工资率与工程师不同)

设计异常

1、 插入异常:一个职工入职,由于“工程名称”不能为空,所以必须虚拟一个工程名称。
2、 更新异常:“李思岐”从“技术员”升级“工程师”,结果“小时工资率”没有从60调整到65。
3、 删除异常:“李思岐”和“葛宇洪”离职,将二人删除,结果把“临江饭店”这个工程也删除了。

优化表结构

工程表:工程号、项目名称
员工表:职工号、姓名、职务
职务表:职务、小时工资
工时表:工程号、职工号、工时

查询语句

语法:select [选项] 字段名 from 表名 [where 条件] [group by 分组] [having 条件] [order by 排序] [limit 限制]

1
2
3
4
5
6
7
8
9
10
11
mysql> create table student(
-> id int auto_increment primary key,
-> name char(16) not null,
-> `语文` int(5),
-> `数学` int(5)
-> )charset=utf8;
Query OK, 0 rows affected (0.03 sec)

mysql> insert into student values(null,'张宝',56,67),(null,'李青霞',89,92),(null,'兰丁丁',29,96);
Query OK, 3 rows affected (0.01 sec)
Records: 3 Duplicates: 0 Warnings: 0

字段表达式

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
mysql> select 20;
+----+
| 20 |
+----+
| 20 |
+----+
1 row in set (0.00 sec)

mysql> select 20*20;
+-------+
| 20*20 |
+-------+
| 400 |
+-------+
1 row in set (0.01 sec)

mysql> select id,name,`语文`,`数学`,(`语文`+`数学`) from student;
+----+-----------+--------+--------+---------------------+
| id | name | 语文 | 数学 | (`语文`+`数学`) |
+----+-----------+--------+--------+---------------------+
| 1 | 张宝 | 56 | 67 | 123 |
| 2 | 李青霞 | 89 | 92 | 181 |
| 3 | 兰丁丁 | 29 | 96 | 125 |
+----+-----------+--------+--------+---------------------+
3 rows in set (0.01 sec)

时间戳

1
2
3
4
5
6
7
mysql> select unix_timestamp();
+------------------+
| unix_timestamp() |
+------------------+
| 1472347068 |
+------------------+
1 row in set (0.01 sec)

随机数

1
2
3
4
5
6
7
mysql> select rand();
+--------------------+
| rand() |
+--------------------+
| 0.8859497913028578 |
+--------------------+
1 row in set (0.00 sec)

通过as给字段取别名

1
2
3
4
5
6
7
mysql> select unix_timestamp() as '时间戳',rand() as '随机数';
+------------+--------------------+
| 时间戳 | 随机数 |
+------------+--------------------+
| 1472347205 | 0.8530956766131294 |
+------------+--------------------+
1 row in set (0.01 sec)

as可以省略

1
2
3
4
5
6
7
mysql> select unix_timestamp() '时间戳',rand() '随机数';
+------------+--------------------+
| 时间戳 | 随机数 |
+------------+--------------------+
| 1472347268 | 0.6076290398907187 |
+------------+--------------------+
1 row in set (0.00 sec)

from 子句

from:来自,from后面跟的是数据源。数据源可以有多个。返回笛卡尔积(排列组合)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
mysql> create table teacher(
-> id int auto_increment primary key,
-> name char(16) not null,
-> `评分` int(3) zerofill
-> )charset=utf8;
Query OK, 0 rows affected (0.03 sec)

mysql> insert into teacher values (null,'贾老师',98),(null,'杜老师',59);
Query OK, 2 rows affected (0.01 sec)
Records: 2 Duplicates: 0 Warnings: 0

mysql> select * from student,teacher;
+----+-----------+--------+--------+----+-----------+--------+
| id | name | 语文 | 数学 | id | name | 评分 |
+----+-----------+--------+--------+----+-----------+--------+
| 1 | 张宝 | 56 | 67 | 1 | 贾老师 | 098 |
| 1 | 张宝 | 56 | 67 | 2 | 杜老师 | 059 |
| 2 | 李青霞 | 89 | 92 | 1 | 贾老师 | 098 |
| 2 | 李青霞 | 89 | 92 | 2 | 杜老师 | 059 |
| 3 | 兰丁丁 | 29 | 96 | 1 | 贾老师 | 098 |
| 3 | 兰丁丁 | 29 | 96 | 2 | 杜老师 | 059 |
+----+-----------+--------+--------+----+-----------+--------+
6 rows in set (0.01 sec)

返回笛卡尔积

可以通过as给表取别名(给表取别名,as可以省略)

1
2
3
4
5
6
7
8
9
10
11
12
mysql> select i.name,m.name from student as i,teacher as m;
+-----------+-----------+
| name | name |
+-----------+-----------+
| 张宝 | 贾老师 |
| 张宝 | 杜老师 |
| 李青霞 | 贾老师 |
| 李青霞 | 杜老师 |
| 兰丁丁 | 贾老师 |
| 兰丁丁 | 杜老师 |
+-----------+-----------+
6 rows in set (0.01 sec)

dual表(伪表)

dual表是一个伪表。在有些特定情况下,没有具体的表的参与,但是为了保证select语句的完整又必须要一个表名,这时候就使用伪表。
dual表是用来保证select语句的完整性

1
2
3
4
5
6
7
mysql> select 20*30 as `积` from dual;
+-----+
| 积 |
+-----+
| 600 |
+-----+
1 row in set (0.00 sec)
1
2
3
mysql> select * from student where `语文`>59;
mysql> select * from student where 1; --返回所有数据
mysql> select * from student where 0; --返回空

思考:数据库中的表是一个二维表,返回的结果是一张二维表,既然能在数据库的二维表中进行查询,能否在结果集的二维表上继续进行查询?
答:可以,使用having条件就是在结果集上继续进行筛选。

1
2
3
4
5
6
7
8
mysql> select * from student where name in ('兰丁丁','张宝');
+----+-----------+--------+--------+
| id | name | 语文 | 数学 |
+----+-----------+--------+--------+
| 1 | 张宝 | 56 | 67 |
| 3 | 兰丁丁 | 29 | 96 |
+----+-----------+--------+--------+
2 rows in set (0.02 sec)

查询名字是兰丁丁和张宝的信息

1
2
3
4
5
6
7
mysql> select * from teacher where name not in ('杜老师');
+----+-----------+--------+
| id | name | 评分 |
+----+-----------+--------+
| 1 | 贾老师 | 098 |
+----+-----------+--------+
1 row in set (0.00 sec)

查询不包含杜老师的信息

in:在…里面
not in:不在…里面

between…and|not between…and

between…and:在…之间

1
2
3
4
5
6
7
8
mysql> select * from student where id between 2 and 3;
+----+-----------+--------+--------+
| id | name | 语文 | 数学 |
+----+-----------+--------+--------+
| 2 | 李青霞 | 89 | 92 |
| 3 | 兰丁丁 | 29 | 96 |
+----+-----------+--------+--------+
2 rows in set (0.00 sec)

id在2和之间包含2和3

not between …and 不在…之间

1
2
3
4
5
6
7
8
mysql> select * from student where id not between 2 and 2;
+----+-----------+--------+--------+
| id | name | 语文 | 数学 |
+----+-----------+--------+--------+
| 1 | 张宝 | 56 | 67 |
| 3 | 兰丁丁 | 29 | 96 |
+----+-----------+--------+--------+
2 rows in set (0.01 sec)

id不在2和2之间

is null | is not null

1
2
3
4
5
6
7
8
9
mysql> insert into student values (null,'赵哥',null,89);
Query OK, 1 row affected (0.01 sec)

+----+--------+--------+--------+
| id | name | 语文 | 数学 |
+----+--------+--------+--------+
| 4 | 赵哥 | NULL | 89 |
+----+--------+--------+--------+
1 row in set (0.00 sec)

查询语文或数学为空的信息;

查询一个为空的字段不能用等于,必须用is null

标量子查询

= :结果就是一个字
select * from stu where id=(select id from class where name='jack');

列子查询

in :一个字段对应多个值
select * from stu where id in(select id from class where name in('php','java'));

行子查询

= :n个字段名对n个值
select * from stu where(id,name)=(select id,name from class where name='php');

聚合函数

聚合函数 描述
sum() 求和
avg() 求平均值
max() 求最大值
min() 求最小值
count() 求记录数
1
2
3
4
5
6
7
mysql> select sum(`语文`) as `语文总分`,avg(`语文`) as `语文平均分`,max(`语文`) as `语文最高分`,min(`语文`) as `语文最低分`,count(*) as `总人数` from student;
+--------------+-----------------+-----------------+-----------------+-----------+
| 语文总分 | 语文平均分 | 语文最高分 | 语文最低分 | 总人数 |
+--------------+-----------------+-----------------+-----------------+-----------+
| 174 | 58.0000 | 89 | 29 | 4 |
+--------------+-----------------+-----------------+-----------------+-----------+
1 row in set (0.01 sec)

通配符

通配符 描述
_ [下划线] 表示任意一个字符
% 表示任意字符

练习
1、满足“T_m”的有(A、C)
A:Tom B:Toom C:Tam D:Tm E:Tmo
2、满足“T_m_”的有(B、C)
A:Tmom B:Tmmm C:T1m2 D:Tmm E:Tm
3、满足“张%”的是(A、B、C、D)
A:张三 B:张三丰 C:张牙舞爪 D:张 E:小张
4、满足“%诺基亚%”的是(A、B、C、D)
A:诺基亚2100 B:2100诺基亚 C:把我的诺基亚拿过来 D:诺基亚

如果要匹配6个字符,且已ing结尾则使用“___ing” 3个下划线

使用“\”转移特殊字符 “\%”;

模糊查询(like)

1
2
3
4
5
6
7
mysql> select * from student where name like '兰%';
+----+-----------+--------+--------+
| id | name | 语文 | 数学 |
+----+-----------+--------+--------+
| 3 | 兰丁丁 | 29 | 96 |
+----+-----------+--------+--------+
1 row in set (0.01 sec)

查询兰姓小伙伴

group by 【分组查询】

将查询的结果分组,分组查询目的在于统计数据

1
2
3
4
5
6
7
8
mysql> select id,name,sex,avg(`数学`) as `数学平均分数` from student group by sex;
+----+-----------+------+--------------------+
| id | name | sex | 数学平均分数 |
+----+-----------+------+--------------------+
| 2 | 李青霞 | 女 | 92.0000 |
| 1 | 张宝 | 男 | 84.0000 |
+----+-----------+------+--------------------+
2 rows in set (0.01 sec)

分组查询中,字段有普通字段会怎么样?
只显示分组后第一条记录的字段值

注意内容插入的时候使用引号 ‘’ ,查询字段信息时使用 ;

通过group_concat()函数将同一组的值连接起来显示

1
2
3
4
5
6
7
8
mysql> select group_concat(id),group_concat(name),sex,avg(`语文`) as `语文平均分`,avg(`数学`) as `数学平均分` from student group by sex;
+------------------+-------------------------+------+-----------------+-----------------+
| group_concat(id) | group_concat(name) | sex | 语文平均分 | 数学平均分 |
+------------------+-------------------------+------+-----------------+-----------------+
| 2 | 李青霞 | 女 | 89.0000 | 92.0000 |
| 1,3,4 | 张宝,兰丁丁,赵哥 | 男 | 42.5000 | 84.0000 |
+------------------+-------------------------+------+-----------------+-----------------+
2 rows in set (0.01 sec)

实现分组后按降序排列

1
2
3
4
5
6
7
8
9
10
mysql> select * from student group by `语文` desc;
+----+-----------+------+--------+--------+
| id | name | sex | 语文 | 数学 |
+----+-----------+------+--------+--------+
| 2 | 李青霞 | 女 | 89 | 92 |
| 1 | 张宝 | 男 | 56 | 67 |
| 3 | 兰丁丁 | 男 | 29 | 96 |
| 4 | 赵哥 | 男 | NULL | 89 |
+----+-----------+------+--------+--------+
4 rows in set (0.00 sec)

分组后降序排列

多列分组(注意:聚合函数*号的位置->count(*))

1
2
3
4
5
6
7
8
mysql> select group_concat(name),sex,count(*) from student group by sex;
+-------------------------+------+----------+
| group_concat(name) | sex | count(*) |
+-------------------------+------+----------+
| 李青霞 | 女 | 1 |
| 张宝,兰丁丁,赵哥 | 男 | 3 |
+-------------------------+------+----------+
2 rows in set (0.01 sec)

回溯统计【with rollup】

在分组统计的基础上再进行相同的统计

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
mysql> update student set sex=null where id=3;
Query OK, 1 row affected (0.01 sec)
Rows matched: 1 Changed: 1 Warnings: 0

mysql> select * from student;
+----+-----------+------+--------+--------+
| id | name | sex | 语文 | 数学 |
+----+-----------+------+--------+--------+
| 1 | 张宝 | 男 | 56 | 67 |
| 2 | 李青霞 | 女 | 89 | 92 |
| 3 | 兰丁丁 | NULL | 29 | 96 |
| 4 | 赵哥 | 男 | NULL | 89 |
+----+-----------+------+--------+--------+
4 rows in set (0.00 sec)

mysql> select group_concat(name),sex,count(*) from student group by sex with rollup;
+-----------------------------------+------+----------+
| group_concat(name) | sex | count(*) |
+-----------------------------------+------+----------+
| 兰丁丁 | NULL | 1 |
| 李青霞 | 女 | 1 |
| 张宝,赵哥 | 男 | 2 |
| 兰丁丁,李青霞,张宝,赵哥 | NULL | 4 |
+-----------------------------------+------+----------+
4 rows in set (0.00 sec)

在分组统计的基础上再进行相同的统计

having条件

having和where的区别:
where是对原始数据进行筛选,having是对记录集(结果集)进行筛选。

1
2
3
4
5
6
7
8
mysql> select * from student having `语文`<59;
+----+-----------+------+--------+--------+
| id | name | sex | 语文 | 数学 |
+----+-----------+------+--------+--------+
| 1 | 张宝 | 男 | 56 | 67 |
| 3 | 兰丁丁 | NULL | 29 | 96 |
+----+-----------+------+--------+--------+
2 rows in set (0.00 sec)
1
2
3
4
5
6
7
8
9
10
11
12
mysql> select name from student where `数学`>80;
+-----------+
| name |
+-----------+
| 李青霞 |
| 兰丁丁 |
| 赵哥 |
+-----------+
3 rows in set (0.00 sec)

mysql> select name from student having `数学`>80;
ERROR 1054 (42S22): Unknown column '数学' in 'having clause'

因为在表中查询,表中有 `数学` ,所以where可以查到结果,反之having是查询不到结果的;

1
2
3
4
5
6
7
mysql> select group_concat(name),sex,count(*) as total from student group by sex having total>1; 
+--------------------+------+-------+
| group_concat(name) | sex | total |
+--------------------+------+-------+
| 张宝,赵哥 | 男 | 2 |
+--------------------+------+-------+
1 row in set (0.00 sec)

对select出的数据,使用having进行条件设置

####【where子句】
主要是用于根据一个条件表达式从硬盘上将数据读到内存中时筛选记录。
既然where根据条件表达式进行记录的筛选,where的运算符
= 判断两个值是否相等
= 判断n个字段与n个值是否相等

order by排序

asc:升序【默认】
desc:降序

1
2
3
mysql> select * from student order by \`语文`;			--按语文升序
mysql> select * from student order by \`语文` asc; --按语文升序
mysql> select * from student order by \`语文` desc; --按语文降序

limit

语法:limit 起始位置,显示长度
起始位置可以省略,默认是从0开始

找出班级总分前两名

1
2
3
4
5
6
7
8
mysql> select name,(`语文`+`数学`) as total from student order by (`语文`+`数学`) desc limit 2;
+-----------+-------+
| name | total |
+-----------+-------+
| 李青霞 | 181 |
| 兰丁丁 | 125 |
+-----------+-------+
2 rows in set (0.00 sec)

查询语句中的选项

查询语句中的选项有两个:
1、 all:显示所有数据 【默认】
2、 distinct:去除结果集中重复的数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
mysql> select all sex from student;
+------+
| sex |
+------+
| 男 |
| 女 |
| NULL |
| 男 |
+------+
4 rows in set (0.00 sec)

mysql> select distinct sex from student;
+------+
| sex |
+------+
| 男 |
| 女 |
| NULL |
+------+
3 rows in set (0.01 sec)

insert…select…

选择一个表的数据插入到另外的表中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
mysql> create table stu_boy like student;
Query OK, 0 rows affected (0.03 sec)

mysql> insert into stu_boy select * from student where sex='男';
Query OK, 2 rows affected (0.02 sec)
Records: 2 Duplicates: 0 Warnings: 0

mysql> select * from stu_boy;
+----+--------+------+--------+--------+
| id | name | sex | 语文 | 数学 |
+----+--------+------+--------+--------+
| 1 | 张宝 | 男 | 56 | 67 |
| 4 | 赵哥 | 男 | NULL | 89 |
+----+--------+------+--------+--------+
2 rows in set (0.00 sec)

on duplicate key update

在插入数据的时候,如果插入的数据不满足主键约束或唯一约束则执行更新操作。
测试数据

1
2
3
4
5
6
7
1	–创建表
2 create table emp(
3 id int primary key,
4 name varchar(20) unique
5 );
6 – 插入测试数据
7 insert into emp values (1,'tom');

mysql>insert into emp values (1,'berry') on duplicate key update name='berry'

编号1和主键冲突,将name改成berry

mysql>insert into emp values (2,'berry') on duplicate key update id=2;

berry 和唯一键冲突,将id改为2

union

作用:将多个select语句结果集纵向联合起来
语法:select 语句 union [选项] select 语句 union [选项] select 语句

例题:查找上海的男生和北京的女生

1
2
3
4
5
--方法一:
mysql> select * from stu where (stuaddress='上海' and stusex='男') or (stuaddress='北京' and stusex='女');

--方法二:
mysql> select * from stu where (stuaddress='上海' and stusex='男') union select * from stu where (stuaddress='北京' and stusex='女');

例题:联合多个表的数据
mysql> select stuname from stu union select name from stu_info;

例题:将多个表的数据插入到新表中
mysql> create table stu2 like stu; --创建和stu一样的表结构

–将上海的男生和北京的女生插入到stu2表中

insert into stu2 select * from stu where (stuaddress='上海' and stusex='男') union select * from stu where (stuaddress='北京' and stusex='女');

union的选项

union的选项有两个
1、 all:显示所有数据
2、 distinct:去除重复的数据【默认】

默认情况下,取出重复的记录
union all 显示联合的数据,不去重

union的注意事项

  • union两边的select语句的字段个数必须一致
1
2
mysql> select name from student union select name,sex from stu_boy;
ERROR 1222 (21000): The used SELECT statements have a different number of columns
  • union两边的select语句的字段名可以不一致,最终按第一个select语句的字段名。
1
2
3
4
5
6
7
8
9
10
11
12
mysql> select id,name from student union select name,sex from stu_boy;
+--------+-----------+
| id | name |
+--------+-----------+
| 1 | 张宝 |
| 2 | 李青霞 |
| 3 | 兰丁丁 |
| 4 | 赵哥 |
| 张宝 | 男 |
| 赵哥 | 男 |
+--------+-----------+
6 rows in set (0.00 sec)
  • union两边的select语句中的数据类型可以不一致。

union和order by一起使用

1、 每条select语句必须用括号括起来。
2、 每条select语句的order by必须配合limit才能生效。

例题:男生按数学的降序,女生按语文的升序排列

1
2
3
4
5
6
7
8
9
mysql> (select * from student where sex='男' order by 数学 desc limit 999) union (select * from student where sex='女' order by 语文 asc limit 999);
+----+-----------+------+--------+--------+
| id | name | sex | 语文 | 数学 |
+----+-----------+------+--------+--------+
| 4 | 赵哥 | 男 | NULL | 89 |
| 1 | 张宝 | 男 | 56 | 67 |
| 2 | 李青霞 | 女 | 89 | 92 |
+----+-----------+------+--------+--------+
3 rows in set (0.00 sec)