MySQL了解

MySQL

一、存储数据的方式

现阶段存储数据的方式有如下两种:

  • 瞬时状态:存在内存中(变量、对象、数组、集合)

  • 持久化状态:存在文件中(文件存储)

缺点

  • 存储时没有数据类型区分
  • 存储量级小
  • 没有访问安全限制
  • 没有备份、恢复机制

二、概念

按照数据结构组织、存储、管理数据的仓库。database,简称DB。

三、分类

  • 网状结构:以节点的形式存储和访问

  • 层次结构:定向有序的树状结构

  • 关系SQL结构,也称为关系型,以表格存储,多个表格之间建立关联关系,通过多种运算实现访问。(主流)具有代表性的有以下四种:

    • Oracle:甲骨文公司的主力产品
    • DB2:IBM公司的大型数据库
    • MySQL:免费开源的数据库。MariaDB是MySQL的一个重要分支。
    • SQL Server:微软的产品,中大型数据库。
    • 还有类似于SQLLite这种使用在手机上的数据库
  • 非关系型结构:NO-SQL(Not Only SQL),主要是使用大型的文件系统,来实现三高(海量数据,高性能,高并发)处理,主流Redis、MongoDB、ES等。

四、数据库管理系统

管理数据的软件,用于建立、管理和维护数据库。DBMS

五、Mysql

一个关系型数据库管理系统,由瑞典MySQL AB公司开发,现属于Oracle旗下产品,是当下最流行的关系型数据库之一,在Web应用方面,是最好的RDBMS之一。

官网:https://www.mysql.com/

安装配置流程:http://c.biancheng.net/view/7135.html

目录结构和配置文件

  • 核心文件
文件夹名称内容
bin命令文件
lib库文件
include头文件
ShareMySQL默认存储引擎INNODB
  • MySQL安装目录中,my.ini文件中常用配置参数
参数描述
default-character-set客户端默认字符集
character-set-server服务端默认字符集
port客户端和服务器端的端口号
default-storage-engine字符集、语言等信息

六、SQL语言

什么是SQL语言?

  • SQL(Structured Query Language)结构化查询语言
  • 用于存储数据、更新、查询和管理关系数据库系统的程序设计语言

注意:对数据库“增删改查”简称C(Create)R(Read)U(Update)D(Delete)

七、数据库连接

打开cmd命令行界面(在安装mysql后和配置好环境变量的前提下):

  • 输入’mysql -u root -p’ 回车,然后输入密码(密码为安装时所设置的密码)
  • 在数据库中操作时,必须以分号结尾

在这里插入图片描述

八、常见的数据库命令

注意:mysql、information_schema,performance_schema、sys这些都是系统数据库,不要随意操作

数据库名称描述
information_schema信息数据库,其中保存着关于所有数据库的信息(元数据)
元数据是关于数据的数据,如数据库名或者表名,列的数据类型,或访问权限等
mysql核心数据库,主要负责存储数据库的用户、权限配置、关键字等,以及需要使用的控制和管理信息,不可以删除
performance_schema性能优化的数据库,MySQL5.5版本中新增的一个性能优化的引擎
sys系统数据库,MySQL5.7版本新增的可以快速的了解元数据信息的系统库。便于发现数据库的多样信息,解决性能瓶颈问题。
-- 命令中的lmz为创建的数据库名称
-- 查看所有的数据库
show databases;

-- 创建数据库
CREATE DATABASE lmz; -- 当不指定字符集时使用默认的字符集
CREATE DATABASE lmz character set utf8; -- 创建一个指定utf-8字符集的数据库
CREATE DATABASE if not EXISTS lmz; -- 判断lmz数据库存不存在,不存在就创建

-- 查看创建数据库的信息
show create database lmz;

-- 修改数据库(将lmz数据库字符集修改为gbk)
ALTER DATABASE lmz character set gbk;

-- 删除lmz数据库
DROP DATABASE lmz;

-- 查看当前所使用的的数据库
SELECT DATABASE(); -- 调用一个database()函数查看

-- 切换使用的数据库
use lmz;

经典面试题:utf8与utf8mb4的区别?

utf8只支持普通的文字信息,utf8mb4支持4个字节的一个特殊的文字,例如emoji等。

九、常见的客户端工具

Navicat:收费工具,功能比较强大。

SQLyog:免费工具

十、数据查询

10.1 导入预备数据

可以使用资料文件夹(数据随便输入的)下的(school.sql文件)

三种方法:

  • 前提:查看导入的脚本文件(后缀名为.sql)中是否存在创建和使用数据库的命令,如果没有则先创建数据库

  • 使用mysql中的内部命令导入

    • 使用use命令切换数据库
    • 使用source命令导入脚本文件(sorce 脚本文件路径
  • 使用命令行命令

    • 在下载目录路径下下打开cmd窗口,输入以下命令即可 mysql -uroot -p -t < 脚本文件
  • 使用客户端工具导入数据库

在这里插入图片描述

在这里插入图片描述

10.2 基本查询

关系型数据库由多个表组成,每个表由行和列组成,类似于Excel

在表中可以进行查询,查询返回的结果集(结果的集合)以虚拟表的形式显示

10.2.1 查询所有
  • 语法
    • select 列名 from 表名;
    • select:指定要查询的列;
    • from:指定要查询的表

查询所有列的两种方式:

  • select * from 表名;
  • select 所有列的列名 from 表名;

注意:生产环境下,优先使用列名查询

  • 因为*的方式需要转换成全列名,效率低,可读性差。(如下图,该表数据284万条数据,两个不同查询方式效率差距接近一倍
-- 进入指定的库
use employees;
-- 查看所有表
show tables;
-- 查看指定表的所有数据
select * from salaries;
select emp_no,to_date from salaries;

在这里插入图片描述

10.2.2 对查询的列进行运算

支持+、-、*、/加减乘除运算,不支持%作求模运算(因为%是占位符),也不支持用+拼接字符串。

注意:运算时,必须列的类型可以运算

10.2.3 别名

可以对查询的列名起别名。使用AS关键字,也可以省略

use school;
-- 将每个学生的年龄增加10岁
-- 此时字段名就是age+10,可以使用as来修改显示的名称(也可以不使用as,字段名可以不用引号)
select id,name,age+10 from student; 
select id,name 姓名,age+10 as 年龄 from student; 

-- 当别名或者列名表名用到关键字时,应该使用`符号引用起来
select id,name `SELECT` from student;
10.2.4 对查询结果去重

注意:

  • distinct用来去重,必须写在所有列的最前面。

  • DIStinct必须要求所有的列完全相同,才能去重,如果有些列值不同,则不能去重

use school;
-- 对查询结果去重(例如求学生信息表中记录了多少个班级,需要对grand_id去重)
select distinct grand_id from student;
-- 如果此时添加了多个字段,那么就不是根据班级去重,而是根据所有字段相同去重
select distinct * from student;

在这里插入图片描述

10.3 排序查询

对查询结果进行排序

  • 语法:select 列名 from 表名 order by 排序列 排序规则

  • 排序规则:asc 升序(默认升序);desc 降序

排序分为两种:

  • 单列排序:只对一个字段排序
  • 多列排序:当对多个字段进行排序时,字段之间用逗号分隔,
    • 排序规则:从前往后执行,写在前面的排序规则先排,当有重复的数据这时就会根据第二个排序规则来排重复的数据顺序
use school;
-- 对学生信息根据班级排序
select * from student ORDER BY grand_id;
-- 对学生信息根据班级排序,然后再根据年龄进行降序排列(多个字段用逗号分隔)
select * from student ORDER BY grand_id,age desc;

在这里插入图片描述

在这里插入图片描述

10.4 条件查询

语法:select 列名 from 表名 where 条件

10.4.1 等值判断

使用=号判断是否相等,与java不同

10.4.2 不等值判断

使用>,<,>=,<=,!=,<>来进行不等值判断

注意:!=和<>都表示不等于,没有区别,早期版本使用<>表示不等于,后来为了和其他数据库语法通用,新增!=

10.4.3 逻辑判断

使用and(&&),or(||),not,早期版本不能使用括号中的运算符。

use school;
-- 查询学生信息中年龄为15的学生(如果查询字段为字符类型则要加引号)
select * from student where age = 15;
-- 查询学生信息中年龄在10-13岁之间的学生
select * from student where age >= 10 && age<=13;
10.4.4 区间判断

使用between 小值 and 大值

经典面试题:between and >=和<=的区别:

  • 使用>=和<=没有要求顺序,可以先写大值或小值
  • 使用between and 只能小值在前面,否则没有查询结果
use school;
-- 查询学生信息中年龄在10-13岁之间的学生
select * from student where age >= 10 && age<=13;
select * from student WHERE age BETWEEN 10 and 13;
-- 使用>= <= 顺序无所谓
select * from student where age<=13 && age >= 10;
-- 使用between and 顺序错误则运行为空
-- select * from student WHERE age BETWEEN 13 and 12;
-- 使用between and 顺序错误则无法运行
10.4.5 Null判断

要判断Null,需要使用is Null或者IS NOT NULL

注意:不能使用 = Null

use school;
-- 查询学生信息中还没有班级的学生(新生)
SELECT * from student where grand_id is null;
10.4.6 枚举值判断

当需要查询多个=值时,可以使用in来枚举

use school;
-- 查询学生信息中年龄为10岁、12岁、15岁的学生
select * from student where age = 10 or age = 12 or age = 15;

-- 可以使用in来枚举(枚举时字符串必须用单引号引起来,int型可以加可以不加)
select * from student where age in (10,'12',15);
10.4.7 模糊查询

使用like查询

  • 例如:Like '张_',匹配张后面一个字符,可以匹配张三,但是不能匹配张三丰。
  • 例如:Like '张%',可以匹配张后面任意个字符,可以匹配张,也可以匹配张三,也可以匹配张三丰等。
  • 如果匹配任意位置包含一个张字,可以使用LIKE '%张%'

注意:模糊查询只能结合like关键字使用

use school;
-- 查询学生信息中姓张的,且只有俩字的名称
select * from student where name like '张_';
-- 查询姓刘的,且后面有任意字符的名称
select * from student where name like '刘%';

-- 查询名称中包含一的名称
select * from student where name like '%一%';

在这里插入图片描述

10.4.8 分支结构查询

语法:

case 

	WHEN 条件1 then 结果1

	WHEN 条件2 then 结果2

	WHEN 条件3 then 结果3

	else 结果4

end
use school;
-- 查询学生信息,根据所在班级分为'优秀班级','进步班级'和'待进步班级'且将这个字段名设置为'班级评定'
select *,CASE
	WHEN grand_id = 1 THEN '优秀班级'
	when grand_id = 3 Then '优秀班级'
	when grand_id = 2 then '进步班级'
	when grand_id > 3 and grand_id <=5 then '进步班级'
	ELSE
		'待进步班级'
END '班级评定'
 from student; 

在这里插入图片描述

10.5 时间(函数)查询

可以在查询的列中,也可以在查询的条件中。

use school;
-- 当前时间
select SYSDATE();
SELECT now();
-- 当前日期
select curdate();
select CURRENT_DATE();
-- 当前时间
SELECT curtime();
select current_time();
-- 获得第几周
select WEEK('2021-12-31');
SELECT week(now());
-- 获得年份
SELECT year('2021-12-31');
-- 查询2010年之后出生的学生,用作条件
select * from student where YEAR(birther) >= 2010;
SELECT * from student where birther >= '2010-1-1';
-- 获得小时,分钟,秒
select HOUR(now());
select minute(now());
select SECOND(now());
-- 获得两个日期之间相隔的天数,前面的日期-后面的日期,(大日期写前,小日期写后,不然得到负天数)
select DATEDIFF(now(),'2020-1-1');
-- 获得3天以后的日期
SELECT ADDDATE(now(),3);
select ADDDATE(now(),-3); -- 三天前的日期

10.6 字符串函数

字符串函数,与Java中类似,不推荐在sql中使用太多函数,影响性能

use school;

-- 拼接字符串 (将姓名和年龄拼接起来显示)
select id,CONCAT(name,age) from student;

-- 添加、删除、替换字符串
-- 位置从1开始
select INSERT('Mysql',2,0,'aaa'); -- 添加 Maaaysql
SELECT insert('Mysql',2,3,'');-- 删除 Ml
select insert('Mysql',2,3,'bbb');-- 替换 Mbbbl
-- 转换大小写
select LOWER('Mysql'); -- mysql
select UPPER('Mysql'); -- MYSQL

-- 截取字符串
select SUBSTR('hello Mysql',4,5); -- lo My
select SUBSTRING('hello Mysql',4,5); -- lo My

10.7 聚合函数

语法: select 聚合函数(列名) from 表名;

聚合函数如下

  • sum():求所有行中单列结果的总和
  • avg():平均值
  • max():最大值
  • min():最小值
  • count():总行数

注意:

  • 执行聚合函数查询,会自动生成一张虚拟表(一行一列)。
  • null值不参与聚合运算
  • 聚合函数使用时,一般不能查询其他字段,除非使用分组,并且查询的内容和分组有关。
use school;
-- 统计数量(第一种统计效率会高一些)
SELECT count(1) 总人数 from student;
select count(*) from student;
select count(id) from student;

-- 求和(求所有班级总年龄)
select sum(age) 总年龄 from student;

-- 求平均值(求所有班级的年龄平均值)
select AVG(age) 平均年龄 from student;

-- 求最大值
SELECT max(age) 最大年龄 from student;
-- 求最小值
SELECT min(age) 最小年龄 from student;
-- 一起查询
select sum(age) 总年龄,AVG(age) 平均年龄,max(age) 最大年龄,min(age) 最小年龄,count(1) 总人数 from student;

-- 不正确的写法(不能查询其他字段)
-- select id 编号,name 姓名,max(age) 最大年龄 from student; -- 结果为 张一 年龄15
-- select * from student where name = '张一'; -- 张一年龄7

10.8 分组查询

**注意:**分组一定会聚合

use school;
-- 按班级分组,统计每个班级的人数
SELECT grand_id 班级,count(1) 总人数 from student GROUP BY grand_id;
-- 按班级分组,统计每个班级的最大年龄和最小年龄
select grand_id 班级,max(age) 最大年龄,min(age) 最小年龄 from student GROUP BY grand_id; 

**注意:**当使用聚合函数时,前面查询的列必须在聚合函数中或者是后面的分组的列,以及与分组的列有一对一关系的。

-- 错误写法
SELECT grand_id 班级,name 姓名, count(1) 总人数 from student GROUP BY grand_id;

-- 正确写法 查询每个班级的人数
SELECT grand_id 班级, count(1) 总人数 from student GROUP BY grand_id;

10.9 分组过滤查询

使用having关键字

**注意:**一般情况下,having关键字是要以聚合函数作为条件时使用,如果条件没有聚合函数,应将该条件写在where后面,而不应该使用having

use school;
-- 按班级分组,找出每个班最大年龄,且最大年龄 > 14 的班级(两种写法,且效率不同)
-- 先分出每个班级并筛选出最大年龄(对所有人分组,然后再筛选大于14的人)
SELECT grand_id 班级id,max(age) 最大年龄 from student GROUP BY grand_id HAVING max(age)>14;
-- 先找出年龄大于14的人,在根据班级分组筛选(该方法效率高一些,因为先筛选出了大于14的人,在对这些人分组)
SELECT grand_id 班级id,max(age) 最大年龄 from student where age > 14 GROUP BY grand_id ;

10.10 限定查询

限制查询结果的数量:【mysql专用,其他数据库不一样】

使用limit关键字

  • limit 10表示显示前10条
  • limit 20,10 表示跳过20条,显示10条,即显示第21-30条

所有根据上面的规则,如果需要实现分页效果limit skipsize

skip的公式应该是skip = (page - 1) * size

use school;
-- 显示前5条记录
SELECT * from student limit 5;
-- 跳过5条,显示10条记录
select * from student limit 5,10;

10.11 基础查询总结

  • SQL语句编写顺序
    • select 表名
    • where 条件
    • group by 分组
    • having 过滤条件
    • order by 排序列(asc|desc)
    • limit 起始行,总条数
  • SQL语句执行顺序
    • from:指定数据来源表
    • where:对查询数据做第一次过滤
    • group by:分组
    • having:对分组后数据做第二次过滤
    • select:查询各字段的值
    • order by:排序
    • limit:限定查询结果

十一、复杂查询

11.1 子查询

将一个查询结果作为一个查询语句中的一部分

11.1.1 将查询结果作为条件(普通等值或不等值)

查询age高于张三的所有学生信息

分析:

  • 表:学生信息表
  • 查询内容:学生信息,查询列为所有列
  • 条件:age高于
  • 子查询:age高于张三,并非一个固定值,所有需要先查询张三的年龄
use school;
-- 先查询张三的年龄
select age from student WHERE name = '张三';
-- 在查询高于张三age的学生信息
select * from student where age > 9;
-- 将两条语句组合成一条,使用子查询
select * from student where age > (select age from student WHERE name = '张三');

**注意:**上面的写法,要求查询张三的age结果必须是单行单列的。

当查询结果不是单行时,会出错,此时就有3种选择:等于、大于、小于。

11.1.2 将查询结果作为条件(枚举值)

当要使用等于时,如果只需要等于其中一个值。使用in

use school;
-- 查询李四的年龄
select age from student WHERE name = '李四'; -- 此时因为学校中有两个李四,所以有两行年龄信息,所以此时需要用枚举值方式
-- 在查询高于张三age的学生信息
select * from student where age in(select age from student WHERE name = '李四'); -- 此时机就会获得等于那两个李四年龄的所有学生信息

在这里插入图片描述

在这里插入图片描述

11.1.3 将查询结果作为条件(使用ALL或ANY)

当要使用大于或小于,需要决定是大于或小于所有的记录,还是大于或小于其中的一条(部分)记录。

ALL表示所有,ANY表示部分

use school;
-- 查询李四的年龄
select age from student WHERE name = '李四';
-- all 大于后面查询的所有结果,即大于最大
select * from student where age > all (select age from student WHERE name = '李四');
-- 等同于
select * from student where age >  (select max(age) from student WHERE name = '李四');

-- any 大于后面查询的所有结果,即大于最小
select * from student where age > any (select age from student WHERE name = '李四');
-- 等同于
select * from student where age >  (select min(age) from student WHERE name = '李四');
11.1.4 将查询结果作为临时表

查询结果也是有行有列的,与表结构一样,所以查询的结果可以看做一个虚拟的表,可以对其进行再次的查询

使用查询结果座位表临时的常见场景:

  • 需要将查询表中设置的别名作为条件
  • 需要将查询的结果作为临时表与其他表进行关联
  • 需要在查询结果中再次查询的
use school;
-- 求出所有学生18年后大于30岁的学生信息(此例子无实际意义,只是让其理解查询结果作为临时表)
-- 需要将查询后的表设置别名才可以运行成功
select * from (select id,name,age + 18 18年后,grand_id from student) as t where 18年后 > 30;

-- 也可以使用如下(不使用查询结果作为临时表)
SELECT id,name,age + 18 18年后,grand_id from student where age+18 > 30;

**注意:**将查询结果作为临时表再次查询,一定要给结果起个别名

11.2 合并查询

使用union 或者 union all 实现将多个查询结果合并到一起,以第一个查询为基础,将后面的查询结果合并到第一次的结果上。

  • 查询的列数要一样
  • 查询的列名或者类型可以不一样
  • union会去掉重复记录,union all 会保留重复记录,所有列都相同才叫重复记录
use school;
-- 获取id<5的学生,与id>10的人合并
SELECT id,name from student where id < 5
union
select id,name from student  where id > 10;

-- 不使用union合并查询也能完成,但是在大数据时性能会差一些
SELECT id,name from student where id < 5 or id > 10;

-- 不同列也可合并(必须列数相同,类型不同也可以合并)
SELECT id,name from student where id < 5
union
select gender,grand_id from student;

-- 错误示范
-- SELECT id,name from student where id < 5
-- union
-- select gender from student; -- The used SELECT statements have a different number of columns

-- union all不会去重
SELECT id,name from student where id < 5 union all SELECT id,name from student where id < 5;
-- union 会去重
SELECT id,name from student where id < 5 union SELECT id,name from student where id < 5;

11.3 连接查询

将多个表一起查

11.3.1 直接查询多个表

直接查询多个表,在where里面写关联条件,如果不写,则形成笛卡尔积。

在MySQL老版本里只支持此连接,不支持INNER JOIN内连接写法等

select 列名 from 表1,表2 where 表1和表2连接条件

use school;
-- 不写条件,笛卡尔积,也就是后面的交叉连接
select * from student s,grade g;
-- 写条件
select * from student s,grade g where s.grand_id = g.id;
11.3.2 内连接

结果相当于上面的查询结果

语法:

select 列名 from1
inner join2
on 连接条件
inner join3
on 连接条件
use school;
-- 内连接
select * from student s inner join 
grade g on s.grand_id = g.id; -- 31 条数据 
-- student里面有32个学生,但是有一人未分班级,所以不会显示,因为该学生是无法关联的记录

-- 老版本写法
select * from student s,grade g where s.grand_id = g.id;

注意:内连接会将连接条件相等的数据显示,如果有无法关联的记录,则不会显示。

  • 内连接写法属于SQL的标准,其他关系型数据库通用,而老版本写法虽然也可以作为内连接查询,但不符合SQL标准,无法兼容其他数据库
11.3.3 外连接

以一张表为基准,连接另一张表。分为左外连接和右外连接

左外连接:以左边表为基准

右外连接:以右边表为基准

LEFT OUTER JOIN ROGHT OUTER JOIN,可以不写OUTER

  • 会将基准表中的所有数据查询,另一张表的数据有连接的显示连接,没有连接的显示null
use school;
-- 左外连接
select * from student s left join 
grade g on s.grand_id = g.id; -- 32条记录
-- student有32个学生,此处使用左外连接,将另一张表的数据有连接的显示,没有连接的显示为null

-- 右外连接
select * from student s right join 
grade g on s.grand_id = g.id; -- 31条记录
-- student有32个学生,此处使用右外连接,将另一张表的数据有连接的显示,没有连接的显示为null,所以只会显示student中分了班的学生,那个没有班级的学生则不会显示
  • 用的最多的是左外连接,因为右外连接也可以变成左外连接
11.3.4 交叉连接

会得到笛卡尔积

use school;
-- 不写条件,笛卡尔积,也就是后面的交叉连接
select * from student s,grade g;

十二、复杂查询案例

12.1 案例1

查询student表中相同姓名的学生

use school;
-- 查询student表中相同姓名的学生

-- 1.查询student表的所有学生姓名
select name from student; -- 32条
-- 去重时可以发现只有31条学生姓名,表示有一个重复的
select distinct name from student; -- 31条

-- 2.我们可以根据姓名来分组,然后计算每一个组有多少个人就可以知道哪一个重复
select name,count(1) from student GROUP BY name; -- 可以看到所有信息中李四次数为2

-- 3.我们可以根据出现次数来筛选大于1的姓名,可以得到姓名重复的
SELECT name from student GROUP BY name HAVING count(1) > 1;

-- 4.我们将得到姓名重复的查询作为查询条件,就可以找出信息表中重复的所有学生信息
SELECT * from student where name = (SELECT name from student GROUP BY name HAVING count(1) > 1);

在这里插入图片描述

12.2 案例2

查询各班级最大年龄的学生信息

use school;
-- 查询各班级最大年龄的学生信息
-- 1.查询各班级最大年龄
select grand_id,max(age) from student GROUP BY grand_id;

-- 2.查询所有学生信息
select * from student;

-- 3.将两次查询使用内连接连接起来,条件为grand_id相等
select * from student t1
inner join
(select grand_id,max(age) from student GROUP BY grand_id) t2
ON
t1.grand_id = t2.grand_id;

-- 4.我们只需要在3的步骤的增加一个条件,当age = 班级最大年龄时就显示(注意:max(age)必须设置别名)
select id,name,gender,age,t1.grand_id,t2.班级最大年龄 from student t1 inner join
(select grand_id,max(age) 班级最大年龄 from student GROUP BY grand_id) t2
ON t1.grand_id = t2.grand_id 
where t1.age = t2.班级最大年龄;

在这里插入图片描述

12.3 案例3

查询各班级(显示班级名称)不低于平均年龄的人数。

use school;
-- 查询各班级(显示班级名称)不低于平均年龄的人数。

-- 1.查询各班级(显示班级名称)的人数(根据班级来使用左连接学生信息表)
select s.id 学生id,s.name,s.age,s.grand_id,g.grade_name from grade g
left join student s
on g.id = s.grand_id;

-- 2.查询各班级的平均年龄
select grand_id,AVG(age) from student group by grand_id;

-- 3.综合1,2使用左连接连接1,2然后,根据条件 学生年龄>平均年龄 且根据班级分组,并计数显示
select g.grade_name,COUNT(1) from grade g
left join student s
on g.id = s.grand_id
left join (select grand_id,AVG(age) avgAge from student group by grand_id) t
on g.id = t.grand_id
where s.age > avgAge GROUP BY g.id;

在这里插入图片描述

12.4 案例4(行转列)

查询所有学生的每科目成绩,且将显示的列名改为每一科名称,每一行代表一个学生所有科目的成绩

use school;
-- 查询所有学生的每科目成绩,且将显示的列名改为每一科名称,每一行代表一个学生所有科目的成绩

-- 1.查询所有学生的每科目成绩
select r.id,s.name,r.course,r.score from student s
left join results r
on s.id = r.id;

-- 2.我们挑选一个学生的成绩来单独测试
select r.id,s.name,r.course,r.score from student s
left join results r
on s.id = r.id
where name = '张三';

-- 3.使用分支结构查询得到每一科的成绩,并设置成一个列,如下,张三同学
select r.id,s.name, 
(case WHEN course = 'China' THEN score else 0 end) China,
(case WHEN course = 'Math' THEN score else 0 end) Math,
(case WHEN course = 'English' THEN score else 0 end) English
from student s
left join results r
on s.id = r.id
where name = '张三';

-- 4.在3的基础上根据id分组(因为名称可能相同,所以使用唯一标识id来分组),就只会得到一个列,但是成绩只会显示第一行的成绩,所以我们需要将分组前的每一列进行求和,就能得到每一科的成绩,然后就可以去掉where name = '张三' 得到所有学生每个成绩,至此行转列结束(行转列中tom成绩为0,因为tom是新生没有考试成绩)
select s.id,s.name, 
SUM(case WHEN course = 'China' THEN score else 0 end) China,
SUM(case WHEN course = 'Math' THEN score else 0 end) Math,
SUM(case WHEN course = 'English' THEN score else 0 end) English
from student s
left join results r
on s.id = r.id GROUP BY s.id;

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

十三、DML操作

DML:数据操作语言,对数据表中的数据进行增删改操作

13.1 添加

语法:

  • 添加单行:insert into 表名(列名1,列名2,列名3) values(值1,值2,值3);
    • 如果要对所有列进行操作,可以不指定列名。
  • 添加多行:inset into 表名(列名1,列名2,列名3) values (值1,值2,值3),(值1,值2,值3);
use school;
-- 添加学生信息(指定列)
insert into student(id,name,age) VALUES (100,'刘某',20);
select * from student where id = 100;
-- 添加学生信息(全部列)
insert into student VALUES (200,'王某','男',30,'2001-1-1',6);
select * from student where id = 200;

-- 添加多个数据(用逗号分割)
INSERT into student values (300,'申某','女',20,'2011-1-1',6),(301,'李某','男',20,'2011-1-20',3);

在这里插入图片描述

13.2 修改

语法:

update 表名 set 列名1=值1,列名2=值2 where 条件

**注意:**如果没有写条件,会对所有数据进行修改

use school;
select * from student where id = 100; -- 查出 age为20
UPDATE student set age = 18 where id = 100;
select * from student where id = 100; -- age被修改为18

-- 修改性别为男
update student set gender = '男' where id = 100;

-- 不添加条件就是对全部数据进行修改
update student set gender = '男' ;

13.3 删除

语法:

delete from 表名 where 条件

**注意:**若没有写条件,将会删除所有数据

use school;
-- 删除指定的数据
DELETE from student where id = 200;
select * from student where id = 200;

-- 不添加条件时删除所有数据
DELETE from student;

13.4 清空

清空整张表

语法:

truncate table 表名;

use school;
-- 清空表
truncate table student;

经典面试题:

TRUNCATEDELETE的区别:

  • delete是删除表中的数据,不会删除表结构,也不会重置表中的设置(自动增长列)。
  • truncate相当于删除了表,然后重新创建了一个新表,里面的所有设置都被重置。

在这里插入图片描述

use school;
-- 此时我们只添加name字段
insert into aaa(name) VALUES('刘'),('王'),('李');
select * from aaa; -- 查询时可发现 id 字段自动递增,从1开始

delete from aaa; -- 此时删除aaa表中所有数据
select * from aaa; -- 发现表中没有数据了

insert into aaa(name) VALUES('刘'),('王'),('李'); -- 再次添加数据
select * from aaa; -- 发现表中 id 字段从4开始递增,因为之前添加了三个字段后id递增到了4,就算用delete删除数据,id递增还是4

truncate table aaa; -- 此时清空表
insert into aaa(name) VALUES('刘'),('王'),('李'); -- 再次添加数据
select * from aaa; -- 发现表中 id 字段又从1开始,因为truncate重置了自动递增的次数,从1开始递增了

十四、数据表操作

14.1 数据类型

数据类型的作用时约束当前列的数据

大体分为三类:

  • 数值
  • 时间日期
  • 字符串

常见数值类型:

  • INT
  • DOUBLE
  • DECIMAL(M,D),M为总长度,0为小数位,例如DECIMAL(5,2),范围为 -999.99-999.99

常见的日期时间类型:

  • DATE:只包含日期
  • TIME:只包含时间
  • DATETIME:包含日期和时间
  • TIMESTAMP:混合了日期和时间,时间戳

技巧:如果项目中的时间需要时区,可以使用TIMESTAMP或者干脆使用数值,记录距离1970-1-1的毫秒数。

常见的字符串类型:

  • CHAR:固定长度的字符串。需要设置长度。
  • VARCHAR:可变长度,但是总长度固定的字符串。需要设置长度。
  • BLOB:二进制内容,长文本数据

注意:无论是CHAR还是VARCHAR对应Java中都是String不要使用JAVA中的char类型

**经典面试题:**CHAR和VARCHAR的区别:

  • CHAR是固定长度,例如设置长度为10,如果添加的数据长度不为10,那么会在后面添加对应的空格补充到10
  • VARCHAR是可变长度,例如设置长度为10,如果添加的数据长度不为10,也不会在后面添加空格
  • CHARVARCHAR都是必须设置长度的,如果不设置就是默认的255
  • 当设置长度后,内容不能超出了长度

14.2 创建表

语法:(可以不指定字符编码,不指定就是默认)

create table 表名(

	列名1 类型 [约束],

	列名2 类型 [约束],

	列名3 类型 [约束][charset=utf-8];
use school;

-- 创建aaa表,设置字段 id,name,age,sex
CREATE TABLE aaa(id int,`name` VARCHAR(10),age int,sex char(5));
select * from aaa;

14.3 修改表

修改表操作比较多:

  • 添加列
  • 修改列(类型和约束)
  • 修改列名
  • 删除列
  • 修改表名
use school;
SELECT * from aaa;

-- 查看表结构
desc aaa;
-- 添加列
alter TABLE aaa add phone VARCHAR(20);
-- 修改列(类型或约束) 设置约束条件为非空
alter table aaa MODIFY phone VARCHAR(30) not null;
-- 删除列
alter table aaa drop phone;
-- 修改表名
alter table aaa RENAME bbb;

SELECT * from bbb;

经典面试题:

当一个表中有海量数据(亿级)时,需要在生产环境不关闭的情况下,需要添加一个列,如何实现?

  • 创建一个新表,新表和原表的格式一致,并添加新的列
  • 复制原表的所有数据到新表中
  • 将原表改名,将新表改为原表表名
  • 删除原表

**技巧:**可以在新建表时预留几个字段,类型推荐varchar,长度自行定义,当遇到以上业务场景时,可以直接使用预留字段,那样就不用多余操作了。

14.4 删除表

语法:

drop table 表名;

十五、约束

限制存入表中的数据的类型和规范叫做约束

15.1 三范式

数据表设计的规范一共有5+1规范,一般要求至少满足三范式

第一范式:要求满足域完整性。主要是要求列不可再分

第二范式:要求满足实体完整性。主要是要求必须要有主键,且其他字段都由主键决定

第三范式:满足引用完整性。

经典面试题:什么是三范式?

15.2 域完整性

  • 列不可再分,不能使用一个列写多个内容。例如:学生表中班级字段中不能写”云计算技术与应用19751班“,应该将其分开,创建一个中间表来存放“云计算技术与应用”,当这个班级换专业后,可以直接修改中间表的名称,学生表不做任何修改就能完成需求。
  • 非空约束
    • not null ,该列在添加时必须要有值,否则报错
    • default,默认值
use school;
-- 添加字段,设置为not null
alter TABLE aaa add phone VARCHAR(20) not null;
-- 修改sex字段,并设置默认值为男
alter TABLE aaa MODIFY sex char(5) DEFAULT '男';

select * from aaa;

-- 添加数据 第一次不设置phone字段
-- INSERT into aaa(id,name,sex,age) VALUES(1,'张三','女',15); 
-- Field 'phone' doesn't have a default value 报错显示phone没有设置默认值,不能为null

-- 添加数据,不设置sex字段
INSERT into aaa(id,name,age,phone) VALUES(1,'张三',15,'111'); 
select * from aaa; -- 查询到张三默认sex为男

-- 当指定sex时,sex为指定的值
INSERT into aaa(id,name,age,sex,phone) VALUES(2,'张三',15,'女','155'); 
select * from aaa; 

15.3 实体完整性

  • 主键约束
    • primary key,该列的值不能重复,且不能为null,且唯一。
  • 唯一约束
    • unique,该列的值不能重复,但是可以为null,而且允许多条数据为null。一般情况下会设置为not null
  • 自动增长列(仅针对数字)(自动增长:auto_increment
    • 只能设置在主键上,且只能有一个
    • 如果不设置主键值,会自动使用自动增长列
    • 设置了自增列,还是可以手动添加主键
    • 如果手动设置了主键大于当前使用的自动增长列值,那么下一次自动增长会在手动设置的主键最大值后+1
    • 经典面试题:当使用int类型时,如果使用自动增长,主键已经达到了最大值后,再添加会出现什么情况? 会一直使用最大值,不再自增,如果表中最大值的记录被删除了,那么就会继续使用最大值,如果还存在,则出现主键重复的错误

注意

  • 当手动设置了主键值大于自动增长列后,此时自动增长列已经变为设置的主键值+1,此时再次设置主键值小于自动增长列,那么自动增长列不会改变,因为自动增长列只会变成比他大的值。
  • 如果添加了一条数据,没有设置主键值,那么此时添加数据失败后,再次添加数据,那么此时主键值 +2了,因为+1的主键值已经被使用了,但是数据添加失败了。(例如:此时自动增长列到了5,此时不设置主键值添加数据,数据添加失败后,不设置主键值再次添加数据,若此时成功添加,那么此时主键值为7,因为每次添加数据时会先获取系统库此时的自动增长列值,获取后系统库自动增长列值+1,然后再去判断数据是否添加成功,添加失败自动增长列也不会返回系统库中,所以才会造成成功添加数据主键值不是6的情况)

代码印证以下结论:

  • 主键只能设置一个,但是可以将两个列设置成组合主键。
  • 表中该字段设置了主键,那么能否修改主键的值?
  • 实体完整性:当表中使用了一个主键,当把这条数据删除后,再次创建相同的主键数据,那么还能否创建?
    • 能,下面代码印证结论
  • 当表中使用了自动增长列,添加数据时,让其自动增长后,增长数字为9,再把这条数据删除,那么此时自动增长的数字到达了几?是9还是10?
    • 10
use school;

-- 创建aaa表格,将id设置为主键,且自动增长。 设置name字段不为null
create table aaa(id int PRIMARY KEY auto_increment,name VARCHAR(10) not null,age int);

-- 在不指定id的情况下添加数据
insert into aaa(name,age) values('张三',15),('李四',20),('王五',18);
select * from aaa;  -- 查看表数据可看到id字段自动从1开始递增,这就是自动增长序列

-- 指定id添加数据
insert into aaa(id,name,age) values(10,'赵六',17);
-- 在不指定id的情况下再次添加数据(观察id自动增长)
insert into aaa(name,age) values('李七',10);
select * from aaa;  -- 可发现李七id为11,印证了自动增长列的第4点结论

-- 此时指定id为5添加数据后,再次不指定id的情况下添加数据(观察id是否变化)
insert into aaa(id,name,age) values(5,'赵六1',17);
-- 在不指定id的情况下再次添加数据(观察id自动增长)
insert into aaa(name,age) values('李七1',10);
select * from aaa; -- 可发现‘李七1’的id是12,并没有变成6,因为自动增长列只会变成比他大的值,当设置一个主键值小于自动增长列,自动增长列并不会改变


-- 添加一列phone字段,且设置为unique(不可重复)
alter table aaa add phone VARCHAR(20) UNIQUE;
-- 添加两条数据,将phone设置成相同的
insert into aaa(name,age,phone) values('李七2',10,'111');
-- insert into aaa(name,age,phone) values('李七3',10,'111'); -- 报错:因为不能phone重复 Duplicate entry '111' for key 'phone'
select * from aaa;


-- 清空aaa表,且重置自动增长序列
TRUNCATE table aaa;
-- 添加数据
insert into aaa(id,name,age,phone) values(1,'张三',18,'111');
insert into aaa(id,name,age,phone) values(2,'张三2',15,'155');

-- 此时不指定id添加一条不成功的数据,不设置name值就会不成功,因为name字段时not null
insert into aaa(age,phone) values(15,'155'); -- 添加失败 Field 'name' doesn't have a default value

-- 再次添加成功数据
insert into aaa(age,name,phone) values(15,'张三3','1661');
select * from aaa; -- 可以看到'张三3'增长列id到达了4,并不是3  印证了注意的第2点结论

-- 删除id为1的数据,然后再次添加id=1的数据,能否添加成功
delete from aaa where id = 1;
insert into aaa(id,name,age,phone) values(1,'张三4',18,'111'); -- 添加成功

-- 将id=1修改为id=10(看是否能修改)
UPDATE aaa set id = 10 where id = 1;
select * from aaa;  -- 可以看到id主键修改成功

-- 新建一个表,设置组合主键
create table bbb(id int,`name` VARCHAR(10),age int(10),PRIMARY KEY(id,`name`));
desc bbb; -- 运行后可发现key有两个,因为这是组合主键,并不能直接设置两个key

-- 以下语句就会报错 Multiple primary key defined
 create table ccc(id int PRIMARY key,`name` VARCHAR(10) PRIMARY key,age int(10));

15.4 引用完整性

使用外键来引用另一个表的主键,避免出现无效的数据。

语法:

constraint 外键名 foreign key(外键列) references 对应表(主键列)

use school;
drop TABLE aaa,bbb;
-- 创建表添加外键
create table aaa(id int,name VARCHAR(10),age int,
-- 添加外键
grand_id int(5), PRIMARY key(id),CONSTRAINT grade_fk FOREIGN key(grand_id) REFERENCES grade(id));

desc aaa; -- 可以发现grand_id列key那里为MUL,此时外键创建成功

-- 使用修改表添加外键
alter table 表名 add CONSTRAINT 外键名 FOREIGN key(当前表外键列) REFERENCES 对应表(主键列)

在这里插入图片描述

十六、表关系

16.1 一对一

两个表中的数据可以一一对应。例如:用户基本信息表和详细信息表

外键可以在任意一张表中建立

可以将经常访问的数据做成一张表,不经常访问的数据但是和这张表一一对应的我们可以创建另一张表存放,当访问经常看的数据时,不需要加载多余的数据。

16.2 一对多(多对一)

例如:班级和学生就是一对多关系,反之则是多对一的关系,一个班有多个学生,每个学生只属于一个班级。

**注意:**一对多或者多对一的关系,外键需要建立在多方。例如上面的例子,外键就需要建立在学生表中,因为学生有很多,班级只有固定的几个,所以学生属于多方。

16.3 多对多

例如:学生和课程就是多对多的关系。一个学生可以学习多门课程,一门课程可以给多个学生学习。

**注意:**多对多关系必须建立中间表来建立关系。例如上面例子,我们就可以创建一个中间表,两个字段(一个学生编号字段,一个课程编号字段),将两个字段可以设置外键来关联那两张表。因为一个学生不会重复学习同一门课程。

use school;
create table stu(
		id int auto_increment PRIMARY KEY,
		name VARCHAR(20));

create table cou(
	cid int PRIMARY key auto_increment,
	cname VARCHAR(10));
		
CREATE table stu_cou(
	sid int,
	cid int,
	PRIMARY key(sid,cid), -- 联合主键
	CONSTRAINT stu_fk FOREIGN key(sid) REFERENCES stu(id), -- 创建外键连接stu
	CONSTRAINT cou_fk FOREIGN key(cid) REFERENCES cou(cid)); -- 创建外键连接cid
	
	-- 多表关联查询
	SELECT s.name,s.id,c.cid,c.cname from stu s
	inner join stu_cou sc on s.id = sc.sid -- 查询时需要将中间表放中间查,因为需要这张表才能使stu和cou相关联
	inner join cou c on c.cid = sc.cid;

十七、事务

17.1 概念

事务是指由一条或多条SQL语句组成的一个最小的操作单元,当该事务中有一个操作失败了,则所有的SQL执行全部失败,只有所有的都成功,才成功。

17.2 原理

数据库会为每一个客户端连接建立一个临时的缓存区,事务中多个SQL的操作会暂存在缓存区,可以通过commit来提交最终结果(成功),或者使用roliback撤销(回滚)操作(失败)

17.3 流程

模拟银行转账操作:

  1. 模拟没有事务情形

    1. 减钱,不加钱,发现无法撤销(回滚)
  2. 模拟有事务的情形

    • 失败回滚情形

      • 设置自动提交失效
      • 减钱,不加钱
      • 回滚,发现回到初始状态
      • 设置自动提交生效
    • 成功提交情形

      • 设置自动提交失效
      • 减钱
      • 加钱
      • 提交
      • 设置自动提交生效
use school;
drop TABLE bank;
-- 创建银行表
create table bank(id int PRIMARY KEY,money int);
-- 给两个初始账户
insert into bank values(1,1500),(2,500);

-- 模拟没有事务
UPDATE bank set money = money -1000 where id = 1;
-- 此时2账户不加钱
select * from bank; -- 发现钱没有转账成功,但是1号账户扣钱了

-- 模拟有事务,但是没有成功提交
-- 设置自动提交失效
select @@autocommit; -- 查看当前自动提交是否开始,1开启,0没开启
set autocommit = 0; -- 设置自动提交失效
UPDATE bank set money = money - 1000 where id = 1;
-- 模拟没有转账成功
select * from bank; -- 查看1账户减钱了,但是此时没有转账成功
ROLLBACK; -- 当转账失败后就触发回滚事务,因为没有设置自动提交所以可以回滚到转账前数据
select * from bank;  -- 再次查看发现账户没有减钱
set autocommit = 1; -- 回滚结束后开启自动提交


-- 模拟有事务,提交成功
set autocommit = 0; -- 设置自动提交失效
-- 模拟转账成功
update bank set money = money - 1000 where id = 1;
UPDATE bank set money = money + 1000 where id = 2;
select * from bank; -- 发现转账成功
-- 转账成功后触发提交事务
COMMIT;
-- 提交后就无法回滚
ROLLBACK;
-- 提交后开启自动提交
set autocommit = 1;

**注意:**在实际代码中,我们可以将这些操作放入try...catch中,在catch中执行ROLLback事务,在try执行结束之前执行commit提交事务,执行时就会如同上面代码操作

17.4 事务的特征

ACID

**经典面试题:**事务的特征

  • 原子性 Atomiciry:一个事务内多个SLQ形成一个整体,要么全部成功,要么全部失败
  • 一致性 Consistency:一个事务内有一个SQL失败,所有状态回滚到之前
  • 隔离性 Isolation:两个事务是相互隔离的,只能看到操作前后的状态,无法看到中间状态
  • 持久性 Duration:事务执行后,影响是永久的

谐音记忆:一(一)元(原)九(久)个(隔)

十八、 权限管理

创建用户:

CREATE 用户名 IDENTIFIED BY 密码

授权:所有权限可以使用ALL,所有数据库以及所有表,可以使用*.*

GRANT 权限名 ON 数据库名.表名 to 用户名;

撤销权限

REVOKE 权限名 ON 数据库名.表名 FROM 用户名;

删除用户

drop user 用户名;

刷新权限

FLUSH PRIVILEGES;

**注意:**所有的上述操作应该在有权限操作的用户登录时操作

-- 创建用户lmz
create USER 'lmz' IDENTIFIED by '123'; -- 此时可以登录lmz用户,但是该用户没有赋予任何表权限,无法操作任何表

-- 赋予权限
GRANT ALL on school.stu to 'lmz'; -- 给lmz赋予school.stu这一个表所有权限,此时可以增删改查
	
-- 撤销权限
REVOKE select on school.stu from 'lmz'; -- 此时撤销lmz对该表的查询权限

-- 删除用户
drop user 'lmz'; -- 此时就无法通过lmz用户名连接数据库

-- 在每一次操作权限后,我们需要刷新一下权限防止权限修改未成功
FLUSH PRIVILEGES;

十九、视图

19.1 概念

视图是一个虚拟的表,作为与真实表相似,但是实际是查询出来的结果集。

作用:

  • 以此为基础进行查询,简化查询操作
  • 还可以有限的做一些增删改操作

19.2 基本操作

语法:

创建:create view 视图名 as 查询语句

修改:

  • create or replace view 视图名 as 查询语句
  • alter view 视图名 as 查询语句

删除:drop view 视图名

注意:视图删除不会影响原表

use school;
-- 行转列(使用左连接),且将该查询作为视图
CREATE view a as
select s.id,s.name,
sum(case when r.course = 'China' then r.score
		 else 0 end) china,
sum(case when r.course = 'English' then r.score
			else 0 end) english,
sum(case when r.course = 'Math' then r.score
			else 0 end) math,
sum(score)	总分		
from student s
left join results r on s.grand_id = r.id GROUP BY id;

-- 在视图的基础上,想查看李四的成绩
SELECT * from a where name = '李四';
-- 在视图的基础上,想查看全校总分前三的信息
select * from a ORDER BY 总分 desc limit 3;
-- 查看全校平均分
select avg(总分) from a ;

drop view a; -- 删除视图
select avg(总分) from a ; -- 删除后无法使用

注意:

  • 视图不会存储数据,原表发生变化,视图内容也会发生变化

  • 没有性能优化

  • 如果视图包含以下内容,则不能通过操作视图中内容影响原表(修改数据)

    • 聚合函数的结果
    • 使用distinct去重
    • group by
    • having
    • union和union all

    注意:不推荐对视图的数据进行修改

二十、SQL语句的分类

经典面试题:SQL的分类有几种

  • DDL:DATA Definition LANGUAGE 数据定义语言,包含create、alter、drop
  • DQL:Query 数据查询语言,包含select、where、group by、order by、Limit
  • DCL:Control 数据控制语言,权限控制,包含GRANT、Revoke等
  • DML:Manipulatiion 数据操纵语言,数据修改,包含insert、delete、update
  • TPL:Trabsaction Process 事务处理语言,包含commit、rollback
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值