数据库(Database)是按照数据结构来组织、存储和管理数据的仓库,它的产生距今已有六十多年。随着信息技术和市场的发展,数据库变得无处不在:它在电子商务、银行系统等众多领域都被广泛使用,且成为其系统的重要组成部分。

数据库用于记录数据,使用数据库记录数据可以表现出各种数据间的联系,也可以很方便地对所记录的数据进行增、删、改、查等操作。

结构化查询语言(Structured Query Language)简称 SQL,是上世纪 70 年代由 IBM 公司开发,用于对数据库进行操作的语言。更详细地说,SQL 是一种数据库查询和程序设计语言,用于存取数据以及查询、更新和管理关系数据库系统,同时也是数据库脚本文件的扩展名。

本篇主要学习GitHub上Python-100-Days数据库部分,主要用于学习。

1 什么是SQL

SQL(发音为字母S-Q-L或sequel)是结构化查询语言(Structured QueryLanguage)的缩写。SQL是一种专门用来与数据库通信的语言。与其他语言(如,英语以及Java和Visual Basic这样的程序设计语言)不一样,SQL由很少的词构成,这是有意而为的。设计SQL的目的是很好地完成一项任务,即提供一种从数据库中读写数据的简单有效的方法。

SQL有如下的优点:

  • SQL不是某个特定数据库供应商专有的语言。几乎所有重要的DBMS都支持SQL,所以,学习此语言使你几乎能与所有数据库打交道。
  • SQL简单易学。它的语句全都是由描述性很强的英语单词组成,而且这些单词的数目不多。
  • SQL尽管看上去很简单,但它实际上是一种强有力的语言,灵活使用其语言元素,可以进行非常复杂和高级的数据库操作。

我们通常可以将 SQL 分为四类,分别是 DDL(数据定义语言)、DML(数据操作语言)、DQL(数据查询语言)和 DCL(数据控制语言)。DDL 主要用于创建、删除、修改数据库中的对象,比如创建、删除和修改二维表,核心的关键字包括createdropalter;DML 主要负责数据的插入、删除和更新,关键词包括insertdeleteupdate;DQL 负责数据查询,最重要的一个关键词是select;DCL 通常用于授予和召回权限,核心关键词是grantrevoke

在大多数系统中,SQL 语句都是不区分大小写的,但是出于严谨,而且便于区分保留字(保留字(reserved word):指在高级语言中已经定义过的字,使用者不能再将这些字作为变量名或过程名使用。)和变量名,我们把保留字大写,把变量和数据小写。这里为了便于书写和识别方便,下面的 SQL 都使用了小写字母来书写。

2 SQL详解

2.1 DDL(数据定义语言)

下面我们来实现一个选课系统的数据库,如下所示的 SQL 创建了名为school的数据库和五张表,分别是学院表(tb_college)、学生表(tb_student)、教师表(tb_teacher)、课程表(tb_course)和选课记录表(tb_record),其中学生和教师跟学院之间是多对一关系,课程跟老师之间也是多对一关系,学生和课程是多对多关系,选课记录表就是维持学生跟课程多对多关系的中间表。

-- 如果存在名为school的数据库就删除它
drop database if exists `school`;-- 创建名为school的数据库并设置默认的字符集和排序方式
create database `school` default character set utf8mb4 collate utf8mb4_general_ci;-- 切换到school数据库上下文环境
use `school`;-- 创建学院表
create table `tb_college`
(
`col_id` int unsigned auto_increment comment '编号',
`col_name` varchar(50) not null comment '名称',
`col_intro` varchar(500) default '' comment '介绍',
primary key (`col_id`)
) engine=innodb auto_increment=1 comment '学院表';-- 创建学生表
create table `tb_student`
(
`stu_id` int unsigned not null comment '学号',
`stu_name` varchar(20) not null comment '姓名',
`stu_sex` boolean default 1 not null comment '性别',
`stu_birth` date not null comment '出生日期',
`stu_addr` varchar(255) default '' comment '籍贯',
`col_id` int unsigned not null comment '所属学院',
primary key (`stu_id`),
constraint `fk_student_col_id` foreign key (`col_id`) references `tb_college` (`col_id`)
) engine=innodb comment '学生表';-- 创建教师表
create table `tb_teacher`
(
`tea_id` int unsigned not null comment '工号',
`tea_name` varchar(20) not null comment '姓名',
`tea_title` varchar(10) default '助教' comment '职称',
`col_id` int unsigned not null comment '所属学院',
primary key (`tea_id`),
constraint `fk_teacher_col_id` foreign key (`col_id`) references `tb_college` (`col_id`)
) engine=innodb comment '老师表';-- 创建课程表
create table `tb_course`
(
`cou_id` int unsigned not null comment '编号',
`cou_name` varchar(50) not null comment '名称',
`cou_credit` int not null comment '学分',
`tea_id` int unsigned not null comment '授课老师',
primary key (`cou_id`),
constraint `fk_course_tea_id` foreign key (`tea_id`) references `tb_teacher` (`tea_id`)
) engine=innodb comment '课程表';-- 创建选课记录表
create table `tb_record`
(
`rec_id` bigint unsigned auto_increment comment '选课记录号',
`stu_id` int unsigned not null comment '学号',
`cou_id` int unsigned not null comment '课程编号',
`sel_date` date not null comment '选课日期',
`score` decimal(4,1) comment '考试成绩',
primary key (`rec_id`),
constraint `fk_record_stu_id` foreign key (`stu_id`) references `tb_student` (`stu_id`),
constraint `fk_record_cou_id` foreign key (`cou_id`) references `tb_course` (`cou_id`),
constraint `uk_record_stu_cou` unique (`stu_id`, `cou_id`)
) engine=innodb comment '选课记录表';

1. 注释
        单行注释可以使用#注释符,#注释符后直接加注释内容。单行注释也可以使用–注释符,–注释符后需要加一个空格,注释才能生效。#和–的区别就是:#后面直接加注释内容,而--的第 2 个破折号后需要跟一个空格符在加注释内容。多行注释使用/* */注释符。/*用于注释内容的开头,*/用于注释内容的结尾。

任何注释(单行注释和多行注释)都可以插在 SQL 语句中,且注释可以放在 SQL 语句中的任意位置。注释可以写在任何 SQL 语句当中,且 SQL 语句中对注释的数量没有限制。MySQL 注释能够帮助阅读者更好地理解 SQL 语句,特别是在使用复杂的 SQL 语句时,所以应该尽量多添加一些简明易懂的注释。

2. 字符集
        创建数据库时,我们通过default character set utf8mb4指定了数据库默认使用的字符集为utf8mb4(最大4字节的utf-8编码),我们推荐使用该字符集,它也是 MySQL 8.x 默认使用的字符集,因为它能够支持国际化编码,还可以存储 Emoji 字符。可以通过下面的命令查看 MySQL 支持的字符集以及默认的排序规则。

show character set;

3. 存储引擎
        在创建表的时候,可以自行选择底层的存储引擎。MySQL 支持多种存储引擎,可以通过show engines命令进行查看。MySQL 5.5 以后的版本默认使用的存储引擎是 InnoDB,它是我们推荐大家使用的存储引擎(因为更适合当下互联网应用对高并发、性能以及事务支持等方面的需求),为了 SQL 语句的向下兼容性,我们可以在建表语句结束处右圆括号的后面通过engine=innodb来指定使用 InnoDB 存储引擎。

show engines\G

下面的表格对MySQL几种常用的数据引擎进行了简单的对比。

特性 InnoDB MRG_MYISAM MEMORY MyISAM
存储限制 没有
事务 支持
锁机制 行锁 表锁 表锁 表锁
B树索引 支持 支持 支持 支持
哈希索引 支持
全文检索 支持(5.6+) 支持
集群索引 支持
数据缓存 支持 支持
索引缓存 支持 支持 支持 支持
数据可压缩 支持
内存使用
存储空间使用
批量插入性能
是否支持外键 支持

通过上面的比较我们可以了解到,InnoDB 是唯一能够支持外键、事务以及行锁的存储引擎,所以我们之前说它更适合互联网应用,而且在较新版本的 MySQL 中,它也是默认使用的存储引擎。

4. 数据类型
        在定义表结构为每个字段选择数据类型时,如果不清楚哪个数据类型更合适,可以通过 MySQL 的帮助系统来了解每种数据类型的特性、数据的长度和精度等相关信息。

? data types

在数据类型的选择上,保存字符串数据通常都使用VARCHARCHAR两种类型,前者通常称为变长字符串,而后者通常称为定长字符串;对于 InnoDB 存储引擎,行存储格式没有区分固定长度和可变长度列,因此VARCHAR类型和CHAR类型没有本质区别,后者不一定比前者性能更好。如果要保存的很大字符串,可以使用TEXT类型;如果要保存很大的字节串,可以使用BLOB(二进制大对象)类型。在 MySQL 中,TEXTBLOB又分别包括TEXTMEDIUMTEXTLONGTEXTBLOBMEDIUMBLOBLONGBLOB三种不同的类型,它们主要的区别在于存储数据的最大大小不同。保存浮点数可以用FLOATDOUBLE类型,FLOAT已经不推荐使用了,而且在 MySQL 后续的版本中可能会被移除掉。而保存定点数应该使用DECIMAL类型。如果要保存时间日期,DATETIME类型优于TIMESTAMP类型,因为前者能表示的时间日期范围更大。

5. 数据库操作
        在 MySQL 中,可以使用 CREATE DATABASE 语句创建数据库,语法格式如下:

CREATE DATABASE [IF NOT EXISTS] <数据库名>
[[DEFAULT] CHARACTER SET <字符集名>]
[[DEFAULT] COLLATE <校对规则名>];

[]中的内容是可选的。语法说明如下:

  • <数据库名>:创建数据库的名称。MySQL 的数据存储区将以目录方式表示 MySQL 数据库,因此数据库名称必须符合操作系统的文件夹命名规则,不能以数字开头,尽量要有实际意义。注意在 MySQL 中不区分大小写。
  • IF NOT EXISTS:在创建数据库之前进行判断,只有该数据库目前尚不存在时才能执行操作。此选项可以用来避免数据库已经存在而重复创建的错误。
  • [DEFAULT] CHARACTER SET:指定数据库的字符集。指定字符集的目的是为了避免在数据库中存储的数据出现乱码的情况。如果在创建数据库时不指定字符集,那么就使用系统的默认字符集。
  • [DEFAULT] COLLATE:指定字符集的默认校对规则。

可以使用 ALTER DATABASE 来修改已经被创建或者存在的数据库的相关参数。修改数据库的语法格式为:

ALTER DATABASE [数据库名] {
[ DEFAULT ] CHARACTER SET <字符集名> |
[ DEFAULT ] COLLATE <校对规则名>}

语法说明如下:

  • ALTER DATABASE 用于更改数据库的全局特性。
  • 使用 ALTER DATABASE 需要获得数据库 ALTER 权限。
  • 数据库名称可以忽略,此时语句对应于默认数据库。
  • CHARACTER SET 子句用于更改默认的数据库字符集。

当需要删除已创建的数据库时,可以使用 DROP DATABASE 语句。其语法格式为:

DROP DATABASE [ IF EXISTS ] <数据库名>

语法说明如下:

  • <数据库名>:指定要删除的数据库名。
  • IF EXISTS:用于防止当数据库不存在时发生错误。
  • DROP DATABASE:删除数据库中的所有表格并同时删除数据库。使用此语句时要非常小心,以免错误删除。如果要使用 DROP DATABASE,需要获得数据库 DROP 权限。

温馨提示: MySQL 安装后,系统会自动创建名为 information_schema 和 mysql 的两个系统数据库,系统数据库存放一些和数据库相关的信息,如果删除了这两个数据库,MySQL 将不能正常工作。

2.2 DML(数据操作语言)

1. 添加数据

use school;-- 插入学院数据
insert into `tb_college` (`col_name`, `col_intro`)
values ('计算机学院', '计算机学院1958年设立计算机专业,1981年建立计算机科学系,1998年设立计算机学院,2005年5月,为了进一步整合教学和科研资源,学校决定,计算机学院和软件学院行政班子合并统一运作、实行教学和学生管理独立运行的模式。 学院下设三个系:计算机科学与技术系、物联网工程系、计算金融系;两个研究所:图象图形研究所、网络空间安全研究院(2015年成立);三个教学实验中心:计算机基础教学实验中心、IBM技术中心和计算机专业实验中心。'),('外国语学院', '外国语学院设有7个教学单位,6个文理兼收的本科专业;拥有1个一级学科博士授予点,3个二级学科博士授予点,5个一级学科硕士学位授权点,5个二级学科硕士学位授权点,5个硕士专业授权领域,同时还有2个硕士专业学位(MTI)专业;有教职员工210余人,其中教授、副教授80余人,教师中获得中国国内外名校博士学位和正在职攻读博士学位的教师比例占专任教师的60%以上。'),('经济管理学院', '经济学院前身是创办于1905年的经济科;已故经济学家彭迪先、张与九、蒋学模、胡寄窗、陶大镛、胡代光,以及当代学者刘诗白等曾先后在此任教或学习。');-- 插入学生数据
insert into `tb_student` (`stu_id`, `stu_name`, `stu_sex`, `stu_birth`, `stu_addr`, `col_id`)
values(1001, '杨过', 1, '1990-3-4', '湖南长沙', 1),(1002, '任我行', 1, '1992-2-2', '湖南长沙', 1),(1033, '王语嫣', 0, '1989-12-3', '四川成都', 1),(1572, '岳不群', 1, '1993-7-19', '陕西咸阳', 1),(1378, '纪嫣然', 0, '1995-8-12', '四川绵阳', 1),(1954, '林平之', 1, '1994-9-20', '福建莆田', 1),(2035, '东方不败', 1, '1988-6-30', null, 2),(3011, '林震南', 1, '1985-12-12', '福建莆田', 3),(3755, '项少龙', 1, '1993-1-25', '四川成都', 3),(3923, '杨不悔', 0, '1985-4-17', '四川成都', 3);-- 插入老师数据
insert into `tb_teacher` (`tea_id`, `tea_name`, `tea_title`, `col_id`)
values (1122, '张三丰', '教授', 1),(1133, '宋远桥', '副教授', 1),(1144, '杨逍', '副教授', 1),(2255, '范遥', '副教授', 2),(3366, '韦一笑', default, 3);-- 插入课程数据
insert into `tb_course` (`cou_id`, `cou_name`, `cou_credit`, `tea_id`)
values (1111, 'Python程序设计', 3, 1122),(2222, 'Web前端开发', 2, 1122),(3333, '操作系统', 4, 1122),(4444, '计算机网络', 2, 1133),(5555, '编译原理', 4, 1144),(6666, '算法和数据结构', 3, 1144),(7777, '经贸法语', 3, 2255),(8888, '成本会计', 2, 3366),(9999, '审计学', 3, 3366);-- 插入选课数据
insert into `tb_record` (`stu_id`, `cou_id`, `sel_date`, `score`)
values (1001, 1111, '2017-09-01', 95),(1001, 2222, '2017-09-01', 87.5),(1001, 3333, '2017-09-01', 100),(1001, 4444, '2018-09-03', null),(1001, 6666, '2017-09-02', 100),(1002, 1111, '2017-09-03', 65),(1002, 5555, '2017-09-01', 42),(1033, 1111, '2017-09-03', 92.5),(1033, 4444, '2017-09-01', 78),(1033, 5555, '2017-09-01', 82.5),(1572, 1111, '2017-09-02', 78),(1378, 1111, '2017-09-05', 82),(1378, 7777, '2017-09-02', 65.5),(2035, 7777, '2018-09-03', 88),(2035, 9999, '2019-09-02', null),(3755, 1111, '2019-09-02', null),(3755, 8888, '2019-09-02', null),(3755, 9999, '2017-09-01', 92);

注意:上面的insert语句使用了批处理的方式来插入数据,这种做法插入数据的效率比较高。

2.3 DQL(数据查询语言)

-- 查询所有学生的所有信息
select * from `tb_student`;-- 查询学生的学号、姓名和籍贯(投影)
select `stu_id`, `stu_name`, `stu_addr` from `tb_student`;-- 查询所有课程的名称及学分(投影和别名)
select `cou_name` as 课程名称, `cou_credit` as 学分 from `tb_course`;-- 查询所有女学生的姓名和出生日期(筛选)
select `stu_name`, `stu_birth` from `tb_student` where `stu_sex`=0;-- 查询籍贯为“四川成都”的女学生的姓名和出生日期(筛选)
select `stu_name`, `stu_birth` from `tb_student` where `stu_sex`=0 and `stu_addr`='四川成都';-- 查询籍贯为“四川成都”或者性别为“女生”的学生
select `stu_name`, `stu_birth` from `tb_student` where `stu_sex`=0 or `stu_addr`='四川成都';-- 查询所有80后学生的姓名、性别和出生日期(筛选)
select `stu_name`, `stu_sex`, `stu_birth` from `tb_student`
where `stu_birth`>='1980-1-1' and `stu_birth`<='1989-12-31';select `stu_name`, `stu_sex`, `stu_birth` from `tb_student`
where `stu_birth` between '1980-1-1' and '1989-12-31';-- 补充:将表示性别的 1 和 0 处理成 “男” 和 “女”
select `stu_name` as 姓名, if(`stu_sex`, '男', '女') as 性别, `stu_birth` as 出生日期
from `tb_student`
where `stu_birth` between '1980-1-1' and '1989-12-31';select `stu_name` as 姓名, case `stu_sex` when 1 then '男' else '女' end as 性别, `stu_birth` as 出生日期
from `tb_student`
where `stu_birth` between '1980-1-1' and '1989-12-31';-- 查询学分大于2的课程的名称和学分(筛选)
select `cou_name`, `cou_credit` from `tb_course` where `cou_credit`>2;-- 查询学分是奇数的课程的名称和学分(筛选)
select `cou_name`, `cou_credit` from `tb_course` where `cou_credit`%2<>0;select `cou_name`, `cou_credit` from `tb_course` where `cou_credit` mod 2<>0;-- 查询选择选了1111的课程考试成绩在90分以上的学生学号(筛选)
select `stu_id` from `tb_record` where `cou_id`=1111 and `score`>90;-- 查询名字叫“杨过”的学生的姓名和性别
select `stu_name`, `stu_sex` from `tb_student` where `stu_name`='杨过';-- 查询姓“杨”的学生姓名和性别(模糊)
-- % - 通配符(wildcard),它可以匹配0个或任意多个字符
select `stu_name`, `stu_sex` from `tb_student` where `stu_name` like '杨%';-- 查询姓“杨”名字两个字的学生姓名和性别(模糊)
-- _ - 通配符(wildcard),它可以精确匹配一个字符
select `stu_name`, `stu_sex` from `tb_student` where `stu_name` like '杨_';-- 查询姓“杨”名字三个字的学生姓名和性别(模糊)
select `stu_name`, `stu_sex` from `tb_student` where `stu_name` like '杨__';-- 查询名字中有“不”字或“嫣”字的学生的姓名(模糊)
select `stu_name` from `tb_student` where `stu_name` like '%不%' or `stu_name` like '%嫣%';-- 将“岳不群”改名为“岳不嫣”,比较下面两个查询的区别
update `tb_student` set `stu_name`='岳不嫣' where `stu_id`=1572;select `stu_name` from `tb_student` where `stu_name` like '%不%'
union
select `stu_name` from `tb_student` where `stu_name` like '%嫣%';select `stu_name` from `tb_student` where `stu_name` like '%不%'
union all
select `stu_name` from `tb_student` where `stu_name` like '%嫣%';-- 查询姓“杨”或姓“林”名字三个字的学生的姓名(正则表达式模糊查询)
select `stu_name` from `tb_student` where `stu_name` regexp '[杨林].{2}';-- 查询没有录入籍贯的学生姓名(空值处理)
select `stu_name` from `tb_student` where `stu_addr` is null;select `stu_name` from `tb_student` where `stu_addr` <=> null;-- 查询录入了籍贯的学生姓名(空值处理)
select `stu_name` from `tb_student` where `stu_addr` is not null;-- 下面的查询什么也查不到,三值逻辑 --> true / false / unknown
select `stu_name` from `tb_student` where `stu_addr`=null or `stu_addr`<>null;-- 查询学生选课的所有日期(去重)
select distinct `sel_date` from `tb_record`;-- 查询学生的籍贯(去重)
select distinct `stu_addr` from `tb_student` where `stu_addr` is not null;-- 查询男学生的姓名和生日按年龄从大到小排列(排序)
-- 升序:从小到大 - asc,降序:从大到小 - desc
select `stu_id`, `stu_name`, `stu_birth` from `tb_student`
where `stu_sex`=1 order by `stu_birth` asc, `stu_id` desc;-- 补充:将上面的生日换算成年龄(日期函数、数值函数)
select `stu_id` as 学号,`stu_name` as 姓名, floor(datediff(curdate(), `stu_birth`)/365) as 年龄
from `tb_student`
where `stu_sex`=1 order by 年龄 desc, `stu_id` desc;-- 查询年龄最大的学生的出生日期(聚合函数)
select min(`stu_birth`) from `tb_student`;-- 查询年龄最小的学生的出生日期(聚合函数)
select max(`stu_birth`) from `tb_student`;-- 查询编号为1111的课程考试成绩的最高分(聚合函数)
select max(`score`) from `tb_record` where `cou_id`=1111;-- 查询学号为1001的学生考试成绩的最低分(聚合函数)
select min(`score`) from `tb_record` where `stu_id`=1001;-- 查询学号为1001的学生考试成绩的平均分(聚合函数)
select avg(`score`) from `tb_record` where `stu_id`=1001;select sum(`score`) / count(`score`) from `tb_record` where `stu_id`=1001;-- 查询学号为1001的学生考试成绩的平均分,如果有null值,null值算0分(聚合函数)
select sum(`score`) / count(*) from `tb_record` where `stu_id`=1001;select avg(ifnull(`score`, 0)) from `tb_record` where `stu_id`=1001;-- 查询学号为1001的学生考试成绩的标准差(聚合函数)
select std(`score`), variance(`score`) from `tb_record` where `stu_id`=1001;-- 查询男女学生的人数(分组和聚合函数)
select case `stu_sex` when 1 then '男' else '女' end as 性别,count(*) as 人数
from `tb_student` group by `stu_sex`;-- 查询每个学院学生人数(分组和聚合函数)
select `col_id` as 学院,count(*) as 人数
from `tb_student` group by `col_id` with rollup;-- 查询每个学院男女学生人数(分组和聚合函数)
select `col_id` as 学院,if(`stu_sex`, '男', '女') as 性别,count(*) as 人数
from `tb_student` group by `col_id`, `stu_sex`;-- 查询每个学生的学号和平均成绩(分组和聚合函数)
select `stu_id`, round(avg(`score`), 1) as avg_score
from `tb_record` group by `stu_id`;-- 查询平均成绩大于等于90分的学生的学号和平均成绩
-- 分组以前的筛选使用where子句,分组以后的筛选使用having子句
select `stu_id`, round(avg(`score`), 1) as avg_score
from `tb_record`
group by `stu_id` having avg_score>=90;-- 查询1111、2222、3333三门课程平均成绩大于等于90分的学生的学号和平均成绩
select `stu_id`, round(avg(`score`), 1) as avg_score
from `tb_record` where `cou_id` in (1111, 2222, 3333)
group by `stu_id` having avg_score>=90;-- 查询年龄最大的学生的姓名(子查询/嵌套查询)
-- 嵌套查询:把一个select的结果作为另一个select的一部分来使用
select `stu_name` from `tb_student`
where `stu_birth`=(select min(`stu_birth`) from `tb_student`
);-- 查询选了两门以上的课程的学生姓名(子查询/分组条件/集合运算)
select `stu_name` from `tb_student`
where `stu_id` in (select `stu_id` from `tb_record` group by `stu_id` having count(*)>2
);-- 查询学生的姓名、生日和所在学院名称
select `stu_name`, `stu_birth`, `col_name`
from `tb_student`, `tb_college`
where `tb_student`.`col_id`=`tb_college`.`col_id`;select `stu_name`, `stu_birth`, `col_name`
from `tb_student` inner join `tb_college`
on `tb_student`.`col_id`=`tb_college`.`col_id`;select `stu_name`, `stu_birth`, `col_name`
from `tb_student` natural join `tb_college`;-- 查询学生姓名、课程名称以及成绩(连接查询/联结查询)
select `stu_name`, `cou_name`, `score`
from `tb_student`, `tb_course`, `tb_record`
where `tb_student`.`stu_id`=`tb_record`.`stu_id`
and `tb_course`.`cou_id`=`tb_record`.`cou_id`
and `score` is not null;select `stu_name`, `cou_name`, `score` from `tb_student`
inner join `tb_record` on `tb_student`.`stu_id`=`tb_record`.`stu_id`
inner join `tb_course` on `tb_course`.`cou_id`=`tb_record`.`cou_id`
where `score` is not null;select `stu_name`, `cou_name`, `score` from `tb_student`
natural join `tb_record`
natural join `tb_course`
where `score` is not null;-- 补充:上面的查询结果取前5条数据(分页查询)
select `stu_name`, `cou_name`, `score`
from `tb_student`, `tb_course`, `tb_record`
where `tb_student`.`stu_id`=`tb_record`.`stu_id`
and `tb_course`.`cou_id`=`tb_record`.`cou_id`
and `score` is not null
order by `score` desc
limit 0,5;-- 补充:上面的查询结果取第6-10条数据(分页查询)
select `stu_name`, `cou_name`, `score`
from `tb_student`, `tb_course`, `tb_record`
where `tb_student`.`stu_id`=`tb_record`.`stu_id`
and `tb_course`.`cou_id`=`tb_record`.`cou_id`
and `score` is not null
order by `score` desc
limit 5 offset 5;-- 补充:上面的查询结果取第11-15条数据(分页查询)
select `stu_name`, `cou_name`, `score`
from `tb_student`, `tb_course`, `tb_record`
where `tb_student`.`stu_id`=`tb_record`.`stu_id`
and `tb_course`.`cou_id`=`tb_record`.`cou_id`
and `score` is not null
order by `score` desc
limit 5 offset 10;-- 查询选课学生的姓名和平均成绩(子查询和连接查询)
select `stu_name`, `avg_score`
from `tb_student` inner join (select `stu_id` as `sid`, round(avg(`score`), 1) as avg_score from `tb_record` group by `stu_id`
) as `t2` on `stu_id`=`sid`;-- 查询学生的姓名和选课的数量
select `stu_name`, `total` from `tb_student` as `t1`
inner join (select `stu_id`, count(*) as `total`from `tb_record` group by `stu_id`
) as `t2` on `t1`.`stu_id`=`t2`.`stu_id`;-- 查询每个学生的姓名和选课数量(左外连接和子查询)
-- 左外连接:左表(写在join左边的表)的每条记录都可以查出来,不满足连表条件的地方填充null。
select `stu_name`, coalesce(`total`, 0) as `total`
from `tb_student` as `t1`
left outer join (select `stu_id`, count(*) as `total`from `tb_record` group by `stu_id`
) as `t2` on `t1`.`stu_id`=`t2`.`stu_id`;-- 修改选课记录表,去掉 stu_id 列的外键约束
alter table `tb_record` drop foreign key `fk_record_stu_id`;-- 插入两条新纪录(注意:没有学号为 5566 的学生)
insert into `tb_record`
values(default, 5566, 1111, '2019-09-02', 80),(default, 5566, 2222, '2019-09-02', 70);-- 右外连接:右表(写在join右边的表)的每条记录都可以查出来,不满足连表条件的地方填充null。
select `stu_name`, `total` from `tb_student` as `t1`
right outer join (select `stu_id`, count(*) as `total`from `tb_record` group by `stu_id`
) as `t2` on `t1`.`stu_id`=`t2`.`stu_id`;-- 全外连接:左表和右表的每条记录都可以查出来,不满足连表条件的地方填充null。
-- 说明:MySQL不支持全外连接,所以用左外连接和右外连接的并集来表示。
select `stu_name`, `total`
from `tb_student` as `t1`
left outer join (select `stu_id`, count(*) as `total`from `tb_record` group by `stu_id`
) as `t2` on `t1`.`stu_id`=`t2`.`stu_id`
union
select `stu_name`, `total` from `tb_student` as `t1`
right outer join (select `stu_id`, count(*) as `total`from `tb_record` group by `stu_id`
) as `t2` on `t1`.`stu_id`=`t2`.`stu_id`;

知识点补充

  1. MySQL目前的版本不支持全外连接,上面我们通过union操作,将左外连接和右外连接的结果求并集实现全外连接的效果。大家可以通过下面的图来加深对连表操作的认识。

  2. MySQL 中支持多种类型的运算符,包括:算术运算符(+-*/%)、比较运算符(=<><=><<=>>=BETWEEN...AND...、INIS NULLIS NOT NULLLIKERLIKEREGEXP)、逻辑运算符(NOTANDORXOR)和位运算符(&|^~>><<),我们可以在 DML 中使用这些运算符处理数据。

  3. 在查询数据时,可以在SELECT语句及其子句(如WHERE子句、ORDER BY子句、HAVING子句等)中使用函数,这些函数包括字符串函数、数值函数、时间日期函数、流程函数等,如下面的表格所示。

1. 常用字符串函数

函数 功能
CONCAT 将多个字符串连接成一个字符串
FORMAT 将数值格式化成字符串并指定保留几位小数
FROM_BASE64 / TO_BASE64 BASE64解码/编码
BIN / OCT / HEX 将数值转换成二进制/八进制/十六进制字符串
LOCATE 在字符串中查找一个子串的位置
LEFT / RIGHT 返回一个字符串左边/右边指定长度的字符
LENGTH / CHAR_LENGTH 返回字符串的长度以字节/字符为单位
LOWER / UPPER 返回字符串的小写/大写形式
LPAD / RPAD 如果字符串的长度不足,在字符串左边/右边填充指定的字符
LTRIM / RTRIM 去掉字符串前面/后面的空格
ORD / CHAR 返回字符对应的编码/返回编码对应的字符
STRCMP 比较字符串,返回-1、0、1分别表示小于、等于、大于
SUBSTRING 返回字符串指定范围的子串

2. 常用数值函数

函数 功能
ABS 返回一个数的绝度值
CEILING / FLOOR 返回一个数上取整/下取整的结果
CONV 将一个数从一种进制转换成另一种进制
CRC32 计算循环冗余校验码
EXP / LOG / LOG2 / LOG10 计算指数/对数
POW 求幂
RAND 返回[0,1)范围的随机数
ROUND 返回一个数四舍五入后的结果
SQRT 返回一个数的平方根
TRUNCATE 截断一个数到指定的精度
SIN / COS / TAN / COT / ASIN / ACOS / ATAN 三角函数

3. 常用时间日期函数

函数 功能
CURDATE / CURTIME / NOW 获取当前日期/时间/日期和时间
ADDDATE / SUBDATE 将两个日期表达式相加/相减并返回结果
DATE / TIME 从字符串中获取日期/时间
YEAR / MONTH / DAY 从日期中获取年/月/日
HOUR / MINUTE / SECOND 从时间中获取时/分/秒
DATEDIFF / TIMEDIFF 返回两个时间日期表达式相差多少天/小时
MAKEDATE / MAKETIME 制造一个日期/时间

4. 常用流程函数

函数 功能
IF 根据条件是否成立返回不同的值
IFNULL 如果为NULL则返回指定的值否则就返回本身
NULLIF 两个表达式相等就返回NULL否则返回第一个表达式的值

5. 其他常用函数

函数 功能
MD5 / SHA1 / SHA2 返回字符串对应的哈希摘要
CHARSET / COLLATION 返回字符集/校对规则
USER / CURRENT_USER 返回当前用户
DATABASE 返回当前数据库名
VERSION 返回当前数据库版本
FOUND_ROWS / ROW_COUNT 返回查询到的行数/受影响的行数
LAST_INSERT_ID 返回最后一个自增主键的值
UUID / UUID_SHORT 返回全局唯一标识符

2.4 DCL(数据控制语言)

数据控制语言用于给指定的用户授权或者从召回指定用户的指定权限,这组操作对数据库管理员来说比较重要,将一个用户的权限最小化(刚好够用)是非常重要的,对数据库的安全至关重要。

-- 创建名为 wangdachui 的账号并为其指定口令,允许该账号从任意主机访问
create user 'wangdachui'@'%' identified by '123456';-- 授权 wangdachui 可以对名为school的数据库执行 select 和 insert 操作
grant select, insert on `school`.* to 'wangdachui'@'%';-- 召回 wangdachui 对school数据库的 insert 权限
revoke insert on `school`.* from 'wangdachui'@'%';

说明:创建一个可以允许任意主机登录并且具有超级管理员权限的用户在现实中并不是一个明智的决定,因为一旦该账号的口令泄露或者被破解,数据库将会面临灾难级的风险。

MySQL基础篇(一)-- SQL基础相关推荐

  1. SQL实战篇:SQL基础及执行顺序

    对于数据分析师来说,不管什么行业,大多数岗位要求都需要熟练使用SQL,尤其在互联网行业要求更是严格,海量的数据存储在数据库中,不懂SQL就寸步难行,更何谈数据分析了,而写的一手好SQL,却可以让自己提 ...

  2. MySQ快速基础篇_MySQL数据库基础

    MySQ快速基础篇_MySQL数据库基础 文章目录 MySQ快速基础篇_MySQL数据库基础 MySQL数据库基础 学习目录 学习目标 一.数据库概述 1.数据库介绍 2.数据库分类 ☆ 关系型数据库 ...

  3. 【基础篇】C#基础知识——面向对象

    [基础篇]C#基础知识--面向对象 第一章 C#基础知识--入门 第二章 C#基础知识--面向对象 文章目录 [基础篇]C#基础知识--面向对象 前言 一.C#面向对象的特性 二.命名空间 2.1 n ...

  4. java玩转区块链-基础篇-solidity语法-基础类型

    java玩转区块链-基础篇-solidity语法-基础类型 java环境配置 代码准备 maven 完整solidity 执行步骤 基础类型 布尔类型 类型标识: 字面常量值: 运算符: 短路规则: ...

  5. 产品经理基础篇 之 产品基础技能

    产品经理基础篇 之 产品基础技能 一.文档 1.BRD(Business Requirements Document,商业需求文档):市场分析.营销策略.盈利预测--给老板看的商业计划PPT,短小精悍 ...

  6. MySql基础篇---002 SQL之SELECT使用篇: 基本的SELECT语句,运算符,排序与分页,多表查询,单行函数,聚合函数,子查询

    第03章_基本的SELECT语句 讲师:尚硅谷-宋红康(江湖人称:康师傅) 官网:http://www.atguigu.com 1. SQL概述 1.1 SQL背景知识 1946 年,世界上第一台电脑 ...

  7. SQL Server 2012入门T-SQL基础篇:(2)SQL的两个基础概念

    这里介绍两个SQL的两个基本的概念,以方便后期的学习: (1)SQL全称structured query language,中文为结构化查询语言;它可以在各类数据库上使用,例如,mysql,oracl ...

  8. 一步一步教你使用AgileEAS.NET基础类库进行应用开发-基础篇-通过SQL实现特殊业务...

    前面的四篇文章演示ORM的一些常规操作与配置,通过前面的文章,应用开发人员要可以使用ORM开发出简单的应用,但是,ORM也不是万能钥匙,在业务应用中,还有很多是ORM解决不了的问题,对于这些问题,我们 ...

  9. 基础篇 | 01 机器学习基础

    本系列概述 核心内容是深度学习,只不过我们会进行代码实践,来巩固我们讲的概念的基础.会使用机器学习开发可以实际使用的工程项目,这也是我们这个系列跟其它机器学习最大区别的地方. 深度学习的意义 当然,不 ...

  10. 超详细图解!【MySQL进阶篇】SQL优化-索引-存储引擎

    1. Mysql的体系结构概览 整个MySQL Server由以下组成 Connection Pool : 连接池组件 Management Services & Utilities : 管理 ...

最新文章

  1. linux视频教程之进程管理
  2. SQL数据库学习之路(五)
  3. 华为电话面试题java_华为java面试题(含电话面试)
  4. Screenshot of a full element in Selenium C#
  5. Android框架式编程之MVP架构
  6. hibernate jpa_JPA / Hibernate:基于版本的乐观并发控制
  7. 傅里叶变换matlab案例,基于matlab的傅里叶变换
  8. 随想录(关于pthread的使用方法)
  9. LeetCode 209. 长度最小的子数组 (滑动窗口)
  10. 期货价格与即期价格?
  11. Atitit.通过null 参数 反射  动态反推方法调用
  12. 使用Ehome协议将设备接入EasyCVR无法注册成功原因排查
  13. 神舟飞天,与有荣焉!麒麟信安操作系统筑牢神舟十四号发射软件基石!
  14. 通过c语言求得一个英文句子中的单词数量
  15. Triple-Speed Ethernet(tse)FPGA软核MAC测试
  16. OV7725摄像头之OV7725芯片
  17. 2021年全球支付现状及发展趋势分析:亚太电子商务数字支付将超过3.1万亿美元[图]
  18. 什么是数据流图 Data Flow Diagram (DFD)
  19. 2017寒假作业 计科1501 李俊01
  20. 2020计算机保研经验贴:清华,复旦,上交,北航

热门文章

  1. windows 键盘消息的机制
  2. C4D模型工具—笔刷
  3. 高性能计算机几cpu,高性能计算机与多cpu电脑处理器哪种牌子的最好?买手机应该注...
  4. ORACLE插入字符串换行
  5. opencv 图像像素比较
  6. 技巧丨哔哩哔哩转正答题技巧
  7. 作为word文档的二级标题,如何自动排序
  8. Failed to move cursor on screen HDMI1
  9. layui的分页功能
  10. 003、pip配置国内源