MySQL(一)

文章目录


超大型数据的存储和管理,使用文件来持久化的局限性是很明显的

sql是一种面向集合的语言

一.概述

DB:数据库,本质就是一个文件系统
DBMS:数据库管理系统,管理数据库的大型软件,mysql就是DBMS
SQL:结构化查询语言,用来与数据库通信的语言

数据库管理系统(DBMS)可以管理多个数据库
一般开发人员会针对每一个应用创建一个数据库。
为保存应用中实体的数据,一般会在数据库创建多个表,以保存程序中实体用户的数据。
在这里插入图片描述
在这里插入图片描述

RDBMS与非RDBMS(关系型数据库管理系统)

RDBMS

这种类型的数据库是最古老的数据库类型,关系型数据库模型是把复杂的数据结构归结为简单的二元关系(即二维表格形式)
关系型数据库以行( row)和列(column)的形式存储数据
这一系列的行和列被称为表(table),一组表组成了一个库(database)。
在这里插入图片描述
所谓关系型数据库的关系指的是 表与表的数据记录有关系,比如说一张表记录了一条某老师的数据,另一张表就记录了他所在的部门的数据

优势:
(1)复杂查询,在多表之间查询
(2)事务支持,安全性能高

非关系型数据库

关系型数据库的阉割版,存储键值对。典型的使用场景是内存缓存,Redis就是键值型数据库

列式数据库
mysql是典型的行式存储,表中的一行就是一条数据;列式数据库将数据按列存储
在这里插入图片描述
优势:大量降低系统IO。考虑这样一个场景,只查询某条数据的某个字段,行式存储会将整条数据加载到内存,列式存储会将这个字段的所有数据加载到内存,冗余数据少,局部性好,自然IO就少

比如大数据的Hbase

关系型数据库设计规则

ORM思想:数据库中的一张表等价于java中的一个类,表中的一条数据等价于对象,表中的列等价于类中的一个字段(field,属性是attribute)
在这里插入图片描述
ER模型(实体联系模型):描述表之间的关系

表的关联关系

一对一关系

简单说两张表得有同一个字段以定位数据,比如说两张表都有学号这个字段,通过学号x从第一张表查数据,还能通过这个x去另一张表查到同一个人的另外一些数据

一对多关系

客户表和订单表,分类表和商品表,部门表和员工表

将一作为主表,多作为从表,和上面一样,两张表的有相同字段,比如说姓名,通过主表某条数据记录的姓名,可以在所有从表中查询相应的数据

下图是课程表和学生成绩表,两张表都有课程编号这个字段。学生成绩表一般会有很多
在这里插入图片描述

多对多关系

两张表之间插入一张中间表。中间表其实像密码本,就是设计了一层映射,中间表连接的两个表看起来是没有任何关系的
在这里插入图片描述

自我引用

这结构,链表
在这里插入图片描述

连接不同版本的Mysql服务器

mysql -uroot -p密码 -hlocalhost -P端口号

登录本机某端口号的mysql服务器,本机提供了多个版本的服务器,使用端口号来标识他们

服务器指一个管理资源并为用户提供服务的计算机软件,通常分为文件服务器、数据库服务器和应用程序服务器。严格来说,服务器本身就是计算机硬件,并在其中运行管理软件,如同电脑

这里所说的mysql服务器就是一个软件

#启动MySQL服务命令:
net start MySQL服务名(版本号)
#停止MysQL服务命令:
net stop MySQL服务名(版本号)

演示使用

show databases;
create database 数据库名;

use 数据库名;//将新建的表放到数据库
create table 表名(字段1 字段数据类型);

show tables;

MySQL目录结构与源码

在这里插入图片描述

三.基本的select语句

SQL概述

1974 年,IBM 研究员发布了一篇揭开数据库技术的论文《SEQUEL:一门结构化的英语查询语言》,直到今天这门结构化的查询语言并没有太大的变化。sequel,续集,续篇,Structured English Query Language

SQL是规范,基础
在这里插入图片描述

sql的分类

(1)DDL数据定义语言
create alter(质变,而非change所指的表象上的变化) drop rename truncate(清空)

(2)DML数据操作语言
增删改查,insert delete update select

(3)DCL数据控制语言
commit(提交),rollback(回退),savepoint(保存点),grant(赋予权限),revoke(回收权限)

(4)DQL数据查询语言,查询的使用频率太高
select

sql语言的规则与规范

规则(底层),规范(建议)

SQL 可以写在一行或者多行。为了提高可读性,各子句分行写,必要时使用缩进
这一条之所以重要,是因为查询语句会非常的长,七八行很普遍

推荐采用统一的书写规范:
数据库名、表名、表别名、字段名、字段别名等都小写
SQL 关键字、函数名、绑定变量等都大写

单行注释:#,-- 注释文字(注意有空格)
多行注释:/**/
注释整体上和java一致

数据导入的指令:
(1)在命令行中输入:source 文件的绝对路径;
绝对路径支持中文
(2)利用可视化软件
在sqlyog中,选择工具中的执行sql脚本导入数据库

基本的select语句

我纠结为什么不用query查询这个单词,而是使用select选择。以我现在的理解,select表示从某张表中选择某个字段,指令更符合英文的习惯

最基本的:SELECT 标识选择哪些列 FROM 标识从哪个表中选择

select * from table;

这个*相当于是通配符,表示所有字段

列的别名

语法很简单:
(1)字段名 字段别名
(2)字段名 as 字段别名(as是alias的缩写)
(3)字段名 “字段别名”(严格区分单双引号)
语法出现了空格,假如字段别名出现了空格呢??
所以语法(3)还是很有必要的

看这样一个查询:SELECT salary/100 "annual salary" FROM employees;结果集是salary这一列数据除以10的结果。salary/10在表中肯定是不存在的,但是字段salary是存在的。所以暂时这样理解select:从表中查询选择的字段是否存在,如果存在返回这一列数据,如果不存在,返回一个错误

去除重复行 distinct

等价于列数据的去重

场景,比如查询公司的所有部门

SELECT DISTINCT department_id FROM employees;

distinct,不同的。这条语句的意思就是从表中查询选择的字段是否存在,如果存在返回不同的数据。就目前来说我认为,选择的是字段,而不是不同的字段

PS:如果选择两个字段,其中一个字段要去重呢???
A:肯定就面临返回的两个列的长度不同的问题。事实上,这个需求就是不存在的对吧,两个字段各自去重,最后的结果没有任何关系就没有意义

空值参与运算

空值:null。就像是引用类型的变量赋值为null

当空值参与运算,结果一定为空

sql里边有个ifnull关键字,专门处理这种情况

着重号

就是ESC这个键

必须保证你的字段没有和保留字、数据库系统或常用方法冲突。如果坚持使用,请在sQL语句中使用`(着重号)引起来

虽然看起来和单引号很像,但着重号有具体的使用场景:
当表名或者是字段名和sql系统出现重名

目前还没有看到使用单引号的场景,字符串用单引号

查询常量

一般意义上的数,字符串都可以

严格上说并不是查询,而是在查询结果中增加一列固定的常量列

这也是有具体的应用场景的,想整合不同的数据源
比如说在查询额员工姓名之前加一列公司名的数据,这个数据在表中是没有收集的,但又是每一个员工都符合的

显示表结构 describe

DESCRIBE employees;

在这里插入图片描述
一方面对原表做了转置,一方面隐藏了具体的数据

就是显示表每个字段的设置,数据类型,是否允许为null,约束条件等等

describe可以缩写为desc

过滤数据

之前的去重distinct就是一种过滤,过滤掉一列数据中的重复数据

SELECT salary 
FROM employees 
WHERE salary = 24000;

SELECT * 
FROM employees 
WHERE salary = 24000;

通过比较着两条语句,大概逆向出执行的逻辑

where之前:从表中查询选择的字段是否存在,存在则返回这个字段的数据

where之后:对结果集进行过滤,选择字段数据满足条件的行x做最后输出.也可以说,其他所有列的x行都输出

如果从指针的角度来看,指针从第一行开始进行过滤,判断当前指针指向的某字段的数据是否满足条件,满足就保留,然后指针向后移动;不满足,指针直接向后移动

因此,上面两条语句的执行结果分别是
在这里插入图片描述
在这里插入图片描述
综上,还可以实现以字段A为依据,对字段B的数据进行过滤,就是说where的字段不一定是前面选择进行查询的字段

SELECT email
FROM employees
WHERE salary = 24000;

四. 运算符

前言

要尤其注意对null的处理

算术运算符

SELECT 1 + '1';

在sQL中,+没有连接的作用,就表示加法运算。此时,会将字符串转换为数值(隐式转换)
应该说没人会主动这么去写

SELECT 1 + 'a';

'a’之类的字符视作0

SELECT 1 + null;

null参与运算,结果一定为null

对于除法,sql默认除不尽。所以整数相除默认为浮点型结果

如果除0,结果为null

取模:结果的符号与被模数的符号相同

比较运算符

比较运算符用来对表达式左边的操作数和右边的操作数进行比较,比较的结果为真则返回1,比较的结果
为假则返回0,其他情况则返回NULL
不像java返回bool值

在这里插入图片描述

= 等号

相当于java的==,注意不是赋值运算符了

这里又涉及到null参与运算的情况:
只要有null参与,结果就一定为null

考虑这样一个过滤的场景:过滤字段某些值为null,要保留那些值为null的行??

SELECT employee_id,salary 
FROM employees 
WHERE salary = null;

这时就涉及到null = null的运算了,结果返回null。也就是说值为null的行无法保留

<=> 安全等于

为了补全等号对null的处理

null参与的运算就不再返回null了,说白了就将null当作一个普通的变量来处理了
使用安全等于运算符时,两边的操作数的值都为NULL时,返回的结果为1,一边的操作数为null时,返回0

剩下的运算符!=,<,>,<=,>=,只要有null参与,结果都是null

非符号类型的运算符

在这里插入图片描述
这些运算符涉及到空格,需要像别名那样,用双引号吗??
A:不需要,直接写

is null和安全等于<=>的效果一样,比较的结果不返回null,返回0或1

where salary IS NULL;

is not null:见名知意

where salary is not null;

isnull: 用法有点像函数,将字段名放到括号里面

where isnull(salary);

is null,is not null, isnull用来处理含null的运算很方便

least和greatest

第一次看到least的写法感觉非常反直觉:

SELECT LEAST(first_name,last_name)
FROM employees;

反直觉的点在与,之前选择的字段都是字符串,但这里感觉上是调用了函数,不是之前遇到的字段

实际上他的流程是:
(1)先判断表中是否存在选择的两个字段
(2)如果存在,指针从第一行开始比较两个字段的数据,保留字典序更小的那个数据
(3)如果不存在,报错

between…and

双闭区间

SELECT employee_id,last_name,salary
FROM employees
WHERE salary BETWEEN 6000 AND 8000;

SELECT employee_id,last_name,salary
FROM employees
WHERE salary NOT BETWEEN 6000 AND 8000;

(1)先判断表中是否存在选择的三个字段
(2)如果存在,指针从第一行开始过滤字段salary不在[6000,8000]的数据
(3)如果不存在,报错
在这里插入图片描述

in

属于运算符

SELECT department_id
FROM employees
WHERE department_id IN (10,20,30);

(1)先判断表中是否存在选择的一个字段
(2)如果存在,指针从第一行开始过滤字段department_id不等于10或者20或者30的数据
(3)如果不存在,报错

like(非常像正则)

模糊查询
比如查询字段中包含’a’的数据

SELECT last_name
FROM employees
WHERE last_name LIKE '%a%';

WHERE last_name LIKE 'a%';

WHERE last_name LIKE '%a';

这个百分号就像正则里边那种通配符

_下划线

代表一个不确定的字符
查询第二个字符是’a’的员工信息:

SELECT last_name
FROM employees
WHERE last_name LIKE '_a%';

查询第二个字符是下划线且第三个字符是’a’的员工信息:

SELECT last_name
FROM employees
WHERE last_name LIKE '_\_a%';

最开始我考虑的是和关键字重名,但稍微一想就不是。它是要一个符号去表达他本来的意思,那就转义

正则regexp和rlike

sql兼容正则表达式
regexp:比较结果返回1或0

逻辑运算符

在这里插入图片描述
异或:相同为0,不同为1.不同时为真,也不同时为假
不同时满足,也不同时不满足。要么这个为真,要么那个为真

SELECT salary
FROM employees
WHERE salary < 5000 XOR salary > 10000;

SELECT salary
FROM employees
WHERE salary < 5000 OR salary > 10000;

探索的时候发现这俩效果一样,从离散数学的角度分析为什么:
假设有条件A,B
AxorB = A发生B不发生 + A不发生B发生
AorB = A发生B不发生 + A不发生B发生 + A发生B发生
当A发生B发生不存在时,或就退化为了异或

与的优先级高于或

优先级

在这里插入图片描述
编号越大,优先级越高

位运算符

在这里插入图片描述

五. 排序与分页

排序数据 order by

默认情况下,查询返回的数据是按照添加顺序显示,类似于List,有重复且有序

需求:按照hire_date按升序显示员工信息

SELECT last_name, job_id, department_id, hire_date
FROM employees
ORDER BY hire_date ASC;

多个对象按照某个属性进行排序

调整排序方向
ASC(ascend): 升序
DESC(descend):降序

SELECT salary annual_sal
FROM employees
ORDER BY annual_sal ASC;

字段的别名只能在order by中使用

之前的where中不能使用字段的别名。也就是说过滤数据只能在原始的表上进行

同时呢,order by的字段也不一定是前面选择的要查询的字段

SELECT salary
FROM employees
ORDER BY last_name ASC;

执行顺序

这一块似乎还没有涉及,举个例子,这个查询语句涉及到过滤和排序

SELECT employee_id, salary
FROM employees
WHERE department_id IN (50,60,70)
ORDER BY department_id DESC;

(1)from+where,先打开表,过滤掉department_id不等于50,60,70的行
(2)select,看看选择要查询的字段存不存在
(3)order by,将要输出的字段按条件进行排序
由此,where需要声明在order by之前

多级排序

一个维度的信息不足以作为实现排序的依据了

SELECT salary
FROM employees
ORDER BY last_name ASC, first_name DESC;

这个确实是非常方便了

分页 limit

网站,app的分页,非常常见
服务器返回的数据太多,不希望就放在一页,也没有必要一次性返回所有的数据

SELECT *
FROM employees
LIMIT 0,20;

如果说将表的每一行映射到从下标0开始的连续的空间,那么第一个数字表示显示的起始位置(也可以看做是指针的位置,这个数字又叫做偏移量),第二个数字表示一共要显示的行数(也可以理解为一页包含的总行数,又叫做田条目数)

公式:每页显示pagesize条记录,此时显示第pageno页

LIMIT(PageNo - 1)*PageSize,PageSize;

之所以减1,是因为程序中页数是从0开始的,客户端是从1开始的

当我们在网站点击第几页x,这个x就发给后台,后台按照上面这个公式将数据发给网站客户端

综合where,order by,limit的声明

SELECT employee_id, salary
FROM employees
WHERE department_id IN (50,60,70)
ORDER BY department_id DESC
LIMIT 0,10;

执行顺序:
(1)from+where,先打开表,过滤掉department_id不等于50,60,70的行
(2)select,看看选择要查询的字段存不存在
(3)order by,将要输出的字段按条件进行排序
(4)limit,进行分页输出

mysql8.0引入了新关键字OFFSET,交换了上面两个数的顺序

六. 多表查询(嵌套循环)

前言

感觉上就是在多重循环中筛选出符合条件的对象
比如说一个双表查询

SELECT employee_id, department_name
FROM employees,departments
WHERE employees.`department_id` = departments.`department_id`;

换成双循环的形式

for(employees类集合emp)--------------------------------------------->对应from语句中的employees表
	for(departments类集合dep)--------------------------------------->对应from语句中的departments表
		if(emp[i].department_id == dep[i].department_id)----------->对应where语句
			print(emp[i].employee_id,emp[i].department_name)------->对应select语句

如果查询语句没有过滤,那双循环的形式就变成了:

for(employees类集合emp)
	for(departments类集合dep)
		print(emp[i].employee_id,emp[i].department_name)

这不就是笛卡尔积错误吗

但是不知道为什么对employees表的遍历总是作为主循环,我怀疑和集合的长度有关

if(emp.length() > dep.length())
for(employees类集合emp)--------------------------------------------->对应from语句中的employees表
	for(departments类集合dep)--------------------------------------->对应from语句中的departments表
		if(emp[i].department_id == dep[i].department_id)----------->对应where语句
			print(emp[i].employee_id,emp[i].department_name)------->对应select语句
else
for(departments类集合dep)--------------------------------------------->对应from语句中的employees表
	for(employees类集合emp)--------------------------------------->对应from语句中的departments表
		if(emp[i].department_id == dep[i].department_id)----------->对应where语句
			print(emp[i].employee_id,emp[i].department_name)------->对应select语句

先将集合按照长度进行降序排序,长度最长的在最外层循环,以此类推

用以处理表与表之间的关系

这一章主要用三张表来举例
在这里插入图片描述
他们的关系:第二张表有个字段在第一张表,第三张表有个字段在第二张表

需求:查询员工abel在哪个城市工作
employees表中没有城市这个字段

SELECT * 
FROM employees
WHERE last_name = 'Abel';

SELECT *
FROM departments
WHERE department_id = 80;

SELECT *
FROM locations
WHERE location_id = 2500;

这个过程就是多表查询

上面这种做法的问题是效率太低:一次查询的过程是先从客户端到web服务器,再到数据库服务器,再到数据库中去查,结果返还给web服务器。信息都是通过网络来远程传输的。上面的做法会重复这个链路三次

为什么要分表

考虑这样一个场景:一个员工所属的部门有一些字段需要记录
如果合成一张表,那么这个部门每个员工都需要记录所在部门的这些字段。很容易想到,这样一来,冗余信息就太多了

感觉上就是,有些字段被共享的话,就要进行分表了
一个部门有许多员工,他们共享了这个部门的字段,所以将部门分表。

实现方式

一种错误的实现方式:笛卡尔积的错误

SELECT employee_id, department_name
FROM employees,departments;

在这里插入图片描述
从结果上看,相当于每个员工都与每个部门匹配了一遍

通过这个结果来反推一下这种写法的执行逻辑(猜的)
(1)from,同时打开两张表,两个指针分别指向两张表的第一行
(2)select,字段的顺序和表的顺序一致,判断选择的两个字段是否分别在两张表中
(3)如果不在,返回一个error
(4)如果在,第一个表中的指针不动,第二个表的指针一次向后移动,每次输出两个指针所指向的行
(5)当第二个指针指向第二个表的末位,第一个指针后移,第二个指针重新指向第二个表的第一行

感觉很像双循环的结构

如果有员工107人,27个部门,那么一共会有2889行结果

笛卡尔积(交叉连接)

一种数学运算,感觉就是排列组合的计算。比如说从两个集合各选出一个元素进行组合,计算组合个数的过程就叫求笛卡尔积

正确实现

需要加上多表的连接条件

SELECT employee_id, department_name
FROM employees,departments
WHERE employees.`department_id` = departments.`department_id`;

通过前面对执行顺序的分析,前两句仍然会产生2889行数据
(1)from,同时打开两张表,两个指针分别指向两张表的第一行
(2)select,字段的顺序和表的顺序一致,判断选择的两个字段是否分别在两张表中。生成2889行数据
(3)where,要去判断前面生成的2889行数据哪一些要保留。

前两步骤是每一个employee_id要配27个department_name,到了第三步骤遍历2889行数据,判断

再看这张图:
在这里插入图片描述
从OO的角度看,如果把employees和departments两张表的每一行数据看做是一个对象的话:
那么上图的左边一列的每一行就是employees对象的employee_id属性,右边一列的每一行就是departments对象的department_name属性

当我进行到上述的步骤(3)时,遍历这2889个对象,每次调用employees对象的department_id和departments对象的department_id进行比较,如果返回0,那么这一行就不输出

是否可以进一步解释:
(1)from,取得employees对象集合的首地址,以及取得departments对象集合的首地址
(2)select,遍历对象的集合,分别输出对象的相关属性

同名字段

如果查询语句中出现了不同的表中的同名字段,则指明字段所在额表
看这样一个例子:

SELECT employee_id, department_name,employees.`department_id`
FROM employees,departments;

在这里插入图片描述
依照我上面的分析,也可以解释这个结果
employee_id和employees.department_id是在同一个类的对象集合中的同一个对象的属性

表的优先级

如果我改变select选择的这两个字段的顺序呢??
我猜可能会是:
payroll 100
payroll 101
payroll 102

SELECT employee_id, department_name
FROM employees,departments;

#改变顺序
SELECT department_name,employee_id
FROM employees,departments;

在这里插入图片描述
仍然是以遍历employees类对象集合为主循环,遍历departments类对象集合为内循环
为什么employees类优先级那么高呢???

表的别名

在from给表起别名

FROM employees emp;

一旦起了别名就必须使用
其原理应该就是原有的引用名被覆盖了。我猜想,假设表名是x,那么底层就是造了一个list,泛型设为x,这个list就叫x。集合里面只能放x类的对象。别名就覆盖了这个集合名x

需求:做一个三级查询

SELECT department_name,employee_id,city
FROM employees,departments,locations;

三个字段分别来自三张表
在这里插入图片描述
从结果来看,employees类对象集合的遍历仍然是主循环,对departments类对象集合的遍历是二级循环,对locations类对象集合的遍历是三级循环

如果进行的是n表查询,那么至少要有n-1个链接条件才能避免笛卡尔积错误

多表查询的分类

等值连接和非等值连接

定义:
等值连接指的是,多表查询语句中的连接条件使用的是等号
多表查询语句中的连接条件不是使用等号的称为非等值连接
在这里插入图片描述
非等值连接:以双表查询为例,两个表中没有同名的字段

SELECT last_name,salary,grade_level
FROM employees,job_grades
WHERE employees.`salary` BETWEEN job_grades.`lowest_sal` AND job_grades.`highest_sal`;

自连接和非自连接

定义:
非自连指的是在多表查询中,连接的是不同的表
自连接,顾名思义,就是自己连接自己,指的是在多表查询中连接的是同一张表

自连接:通过同一张表的不同字段进行过滤

需求:查询每个员工id,name以及上级的id和name
最开始我是这么写的:

SELECT employee_id,last_name,manager_id,employee_id
FROM employees;

这就是把employees表的四个字段给输出了,而且没有办法写过滤的语句,总感觉少了点什么,不可能拿employee_id和manager_id去做比较,看上级是不是自己???

在我写的查询语句背景下,employee_id和manager_id是同一个对象的不同属性,分析题干,应该使用不同对象的不同属性去比较。虽然说我确实是用了同一张表

因此将查询语句改为:

SELECT emp.employee_id,emp.last_name,manager.`employee_id`,manager.`last_name`
FROM employees emp, employees manager
WHERE emp.`manager_id` = manager.`employee_id`;

自连接的问题,要从from取得两个引用,虽然他们的值是一样的

内连接和外连接

内连接:合并具有同一列的两个以上的表的行, 结果集中不包含一个表与另一个表不匹配的行
说人话就是,查询结果只包含它们匹配的行,不匹配的就不要了。

在这里插入图片描述
左外连接:两个表在连接过程中除了返回满足连接条件的行以外,还返回左表中不满足条件的行
在这里插入图片描述
右外连接:两个表在连接过程中除了返回满足连接条件的行以外,还返回右表中不满足条件的行
在这里插入图片描述

for(employees类集合emp)--------------------------------------------->对应from语句中的employees表
	for(departments类集合dep)--------------------------------------->对应from语句中的departments表
		if(emp[i].department_id == dep[i].department_id)----------->对应where语句
			print(emp[i].employee_id,emp[i].department_name)------->对应select语句

就像是这样,只输出满足条件的行

外连接:两个表在连接过程中除了返回满足连接条件的行以外还返回左(或右)表中不满足条件的
行 ,这种连接称为左(或右) 外连接。没有匹配的行时, 结果表中相应的列为空(NULL)。结果集中还包含一个表与另一个表不匹配的行

这个左表中不满足条件的行怎么确定,如果不满足难道不该是左右同时不满足吗

外连接分类:(类比于交集)
如果是左外连接,则连接条件中左边的表也称为 主表 ,右边的表称为 从表 。
如果是右外连接,则连接条件中右边的表也称为 主表 ,左边的表称为 从表 。
满外连接

比如说,select了两个字段id,name,在双循环的过程中,先将id和name分别放到两个map中,初始化为false,一旦if为true,则改为true
外连接,就是在最后将value值为false的key一次性输出
比如查询员工id和部门name

for(employees类集合emp)--------------------------------------------->对应from语句中的employees表
	for(departments类集合dep)--------------------------------------->对应from语句中的departments表
		idmap.add(emp[i].employee_id, false);
		namemap.add(dep[i].department_name, false);
		if(emp[i].department_id == dep[i].department_id)----------->对应where语句
			idmap[emp[i].employee_id] = true;
			namemap[dep[i].department_name] = true;
			print(emp[i].employee_id,dep[i].department_name)------->对应select语句
//左外连接
iterator idmap x:
	if x.value = false:
		print(x.key)
//右外连接
iterator namemap x:
	if x.value = false:
		print(x.key)

要说实际意义还是很大的,比如说select的两个字段分别是员工姓名和所在部门
左外的话,就显示哪些员工没有所属的部门
右外的话,就显示哪些部门没有所属的员工

sql92实现内连接

以上所有

sql92实现外连接

使用+号

核心思路是补,
输出左表不满足条件的行,补右边
输出右表不满足条件的行,补左边

但是mysql不支持sql92

sql99实现多表查询 join on

sql99实现内连接

SELECT last_name,department_name,city
FROM employees e 
INNER JOIN departments d
ON e.`department_id` = d.`department_id`
INNER JOIN locations l
ON d.`location_id` = l.`location_id`;

sql99实现外连接

SELECT last_name,department_name
FROM employees e 
LEFT OUTER JOIN departments d
ON e.`department_id` = d.`department_id`;

RIGHT OUTER JOIN departments d
ON e.`department_id` = d.`department_id`;

employees表有107行,departments表有27行
内连接的结果有106行
左外连接的结果有107行
右外连接的结果有122行

mysql不支持full join这种写法实现满外连接

在这里插入图片描述

union关键字

将多条select语句的结果组合成单个结果集,合并
时,两个表对应的列数和数据类型必须相同

SELECT employees.`email`
FROM employees
UNION
SELECT employees.`employee_id`
FROM employees;

首先两个查询语句的结果都是单列数据,且数据类型不同,email是var char,employee_id是int。最终合并成功,行数是107+107

union:作符返回两个查询的结果集的并集,去除重复记录
在这里插入图片描述
union all:L操作符返回两个查询的结果集的并集。对于两个结果集的重复部分,不去重。
在这里插入图片描述
我上面的距离没有实际意义
需求:查询部门编号>90或邮箱包含a的员工信息

SELECT * FROM employees WHERE email LIKE '%a%'
UNION
SELECT * FROM employees WHERE department_id>90;

sql99实现满外连接

在这里插入图片描述
对于左中和右中两种情况

for(employees类集合emp)--------------------------------------------->对应from语句中的employees表
	for(departments类集合dep)--------------------------------------->对应from语句中的departments表
		idmap.add(emp[i].employee_id, false);
		namemap.add(dep[i].department_name, false);
		if(emp[i].department_id == dep[i].department_id)----------->对应where语句
			idmap[emp[i].employee_id] = true;
			namemap[dep[i].department_name] = true;
			//print(emp[i].employee_id,dep[i].department_name)------->对应select语句
//左外连接
iterator idmap x:
	if x.value = false:
		print(x.key)
//右外连接
iterator namemap x:
	if x.value = false:
		print(x.key)

删掉了双循环中的print

对于下左:使用union all合并左外连接和右中 或者 合并右外连接和左中即可

对于下右:使用union all合并左中和右中即可

sql99新特性

自然连接 natural join

需求:查询员工id,名字,所在部门,同时这些员工的领导必须是所在部门的领导(也就是二把手)

//sql99实现内连接
SELECT e.`employee_id`,e.`last_name`,d.`department_name`
FROM employees e
JOIN departments d
ON e.`department_id` = d.`department_id`
AND e.`manager_id` = d.`manager_id`;

//sql92实现内连接
SELECT e.`employee_id`,e.`last_name`,d.`department_name`
FROM employees e, departments d
WHERE e.`department_id` = d.`department_id` 
AND e.`manager_id` = d.`manager_id`;

上述查询的特点是,过滤时使用的字段在两张表中都有

自然连接会帮你自动查询两张连接表中所有相同的字段 ,然后进行等值连接

SELECT e.`employee_id`,e.`last_name`,d.`department_name`
FROM employees e
NATURAL JOIN departments d;

using连接:指定数据表里的 同名字段 进行等值连接。但是只能配合JOIN一起使用

SELECT e.`employee_id`,e.`last_name`,d.`department_name`
FROM employees e, departments d
USING(department_id);

mysql不支持sql92(+)实现外连接
mysql不支持sql99以full join实现外连接

连接

这一章出现了许多和连接相关的术语,比如说多表查询分类中提到的:
等值连接和非等值连接
自连接和非自连接
内连接和外连接
还包括自然连接,using连接
还有交叉连接(笛卡尔积),跟在where后面的连接条件

以上有所有都可以归到一个主题下,表连接

对这里所指的连接用英文表示是join

表连接的定义是:在多个表之间通过一定的连接条件,使表之间发生关联,进而能从多个表之间获取数据

简单说,我直觉上的感受是如果查询的结果是通过多张表的相关字段比较得到的,那就是进行了表连接

表连接的约束条件可以有三种方式:WHERE, ON, USING

重要理解

那 on 和where 那个更高效呢

如果是inner join, 放on和放where产生的结果一样, 但没说哪个效率速度更高? 如果有outer join (left or right), 就有区别了, 因为on生效在先, 已经提前过滤了一部分数据, 而where生效在后.

综合一下, 感觉还是放在on里更有效率, 因为它先于where执行.
先笛卡尔积, 然后再on过滤, 如果join是inner的, 就继续往下走, 如果join 是left join, 就把on过滤掉的左主表中的数据再添加回来; 然后再执行where里的过滤;

on中不是最终过滤, 因为后面left join还可能添加回来, 而where才是最终过滤.

只有当使用外连接(left, right)时, on 和 where 才有这个区别, 如果用inner join, 在哪里制定都一样, 因为on 之后就是where, 中间没有其它步骤

上面的表述有几个重要的认识:
(1)on和where都用于过滤
(2)对于内连接,on和where可以替换
(3)对于外连接,on过滤完了之后,还要将过滤掉的主表的数据再添加回来
(4)经过测试,添加的数据并非全部添加在末尾。可能是按顺序添加回来
(5)对于外连接,添加完主表被过滤掉的数据之后,再执行where的过滤

七. 单行函数

注意啊,函数不是用来过滤数据的啊

SELECT e.`last_name`,d.`department_name`
FROM employees e,departments d
WHERE e.`department_id` = d.`department_id`;
for(employees类集合emp)--------------------------------------------->对应from语句中的employees表
	for(departments类集合dep)--------------------------------------->对应from语句中的departments表
		print(emp[i].employee_id,dep[i].department_name)------->对应select语句

过滤数据相当于是在多重循环里边加if分支
函数是用来修饰输出的,也就是上面的print

前言 关于mysql的函数

通过前面的学习已经知道,不同的DBMS对sql的支持不一样,在函数这里也是一样的。

不同的DBMS定义了自己独有的函数体系,只有极少的函数是被不同的DBMS同时支持的

这就意味着采用sql函数的代码可移植性很差

内置函数从 实现的功能角度 可以分为数值函数、字符串函数、日期和时间函数、流程控制函数、加密与解密函数、获取MySQL信息函数、聚合函数等

在这里插入图片描述

数值函数

基本函数

在这里插入图片描述
ceiling天花板,floor地板
在这里插入图片描述
注意,y可以是负数。比如说143.26,保留-1位小数就是140.看小数点的位置
在这里插入图片描述
这里的y同上,也可以是负数,效果是一样的

函数的嵌套:其实就是把一个函数的结果作为另一个函数的参数

SELECT TRUNCATE(ROUND(143.25,-1),-2);

三角函数

在这里插入图片描述
角度和弧度的换算
弧度:本质上就是角度,但他的值等于 当圆的弧度和半径相等时转过的角度。这个值称为一个弧度。大概57度多一点。映射到度数上,2π弧度 = 360角度
在这里插入图片描述
跟我们平时的习惯不同,我们都是计算角度的正弦值,这里是计算弧度的正弦值,只是说多了一层映射关系

在这里插入图片描述
sin的对面是余割
cos的对面是正割
tan的对面是余切
设图中阴影三角的方向都是从上到下,则对于每个阴影三角形,底边的两个元素的平方和等于顶元素的平方

反正弦函数
正弦函数y=sin x在[-π/2,π/2]上的反函数,叫做反正弦函数。记作arcsinx,表示一个正弦值为x的角,该角的范围在[-π/2,π/2]区间内。定义域[-1,1] ,值域[-π/2,π/2]

反余弦函数
余弦函数y=cos x在[0,π]上的反函数,叫做反余弦函数。记作arccosx,表示一个余弦值为x的角,该角的范围在[0,π]区间内。定义域[-1,1] , 值域[0,π]

所以说这俩函数的参数是一个正弦或者余弦值

在这里插入图片描述

指数和对数

在这里插入图片描述

进制转换

在这里插入图片描述
注意这个conv(),f1表示x是f1进制额数,然后转换为f2进制的数

字符串函数

在这里插入图片描述
这里的length和平时编程不一样,需要考虑是字符数还是字节数
如果是前者,那和平时编程是一致的;如果是后者,随字符集的变化而变化。对于utf-8,英文字符占一个字节,中文汉字占3个字节

concat()是可以传字段进去的:

SELECT CONCAT(emp.`last_name`,'work for ',mgr.`last_name`) "details"
FROM employees emp JOIN employees mgr
WHERE emp.`manager_id` = mgr.`employee_id`;

其实就是在输出的时候把对象的属性的值拼接在一起,但说实话这种传参数的方式还是很反直觉的。

事实上,直接写字段是比较反直觉的,但是用表名.字段名这种就可以类比于java了,表对应OO的类,字段对应OO的属性。这样说不是特别严谨
我感觉是这样,假设有张表叫x,那么底层会有一个叫x的类,还有一个集合,集合的泛型设为x
类x里边声明了属性,这些属性名和字段是一致的。这些属性到时候使用每一个x类对象去调用的
在这里插入图片描述
在这里插入图片描述
在mysql中,字符串的索引从1开始
insert()这个替换我最开始没理解,我觉得replacestr要是比len长的话不就替换不了了吗
实际是这样,从idx开始,数len个字符,将他们删除,插入replacestr

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
pad有填充的意思,主要是左右对齐,填充到len那么长。python里面也有专门用于字符串补零的函数,zfill()

在这里插入图片描述
去空格
在这里插入图片描述

日期时间函数

日志,时间戳要用,使用频率很高

获取日期、时间(重要)

在这里插入图片描述
UTC:Universal Time Coordinated,世界协调时间,英国格林威治时间

日期与时间戳的转换(重要)

时间戳:可以理解为时间长度,通常以毫秒数为单位。它是唯一的,计时起点为时间戳的起点是1970-01-01 08:00:00
也叫什么unix时间戳
在这里插入图片描述
stamp:戳,印花

订单单号:时间戳+随机字符串

获取月份、星期、星期数、天数等函数

在这里插入图片描述
日期默认以横杠隔开:1995-6-30

日期的操作函数

在这里插入图片描述
type的取值
在这里插入图片描述

时间和秒钟转换的函数

在这里插入图片描述

计算日期和时间的函数(重要)

在这里插入图片描述
interval,间隔,用于增加或者减少时间,增加或减少的数量是expr

type的取值
在这里插入图片描述
在这里插入图片描述

日期的格式化与解析(重要)

在这里插入图片描述
函数中fmt参数常用的格式符:
在这里插入图片描述
GET_FORMAT函数中date_type和format_type参数取值如下:
在这里插入图片描述
日期的格式化与解析
格式化:日期—>字符串
解析:字符串---->日期

流程控制函数

在这里插入图片描述
ifnull():如果v1为null,输出v2;否则输出v1

case when then else end

SELECT CASE
WHEN 1 > 0
THEN '1 > 0'
WHEN 2 > 0
THEN '2 > 0'
ELSE '3 > 0'
END
->1 > 0

SELECT 
CASE
WHEN 1 > 0
THEN '1 > 0'
WHEN 2 > 0
THEN '2 > 0'
ELSE '3 > 0' 
END detail;
if 1 > 0:
	print '1>0'
else if 2 > 0:
	print '2>0'
else:
	print '3>0'

case exp when then else end
需求:
查询员工信息,
若部门号为10,则打印其工资的1.1倍
20号部门,则打印其工资的1.2倍
30号部门,打印其工资的1.3倍数
其他部门,打印其工资的1.4 倍数

SELECT employee_id,last_name,department_id,salary,
CASE department_id
WHEN 10
THEN salary * 1.1
WHEN 20
THEN salary * 1.2
WHEN 30
THEN salary * 1.3
ELSE salary
END "details"
FROM employees;

为什么sql的流程控制只有分支控制呢??
A:前面已经分析过了,从FROM开始,整个程序就建立在多重循环之上

加密与解密函数

数主要用于对数据库中的数据进行加密和解密处理,比如说用户密码
在这里插入图片描述
mysql8.0不支持password(),不够安全了
所以说,后台加密其实就是调用一个函数的事情。秘钥被锁在数据库的深处

在这里插入图片描述
mysql8.0也启用了这两个函数。因为MD5和SHA不需要解密了,服务器中存的就是加密后的数据。明文密码在客户端就完成加密,然后再发给WEB服务器

信息函数

以查询MySQL信息的函数,这些函数主要用于帮助数据库开发或运维人员更好地对数据库进行维护工作

在这里插入图片描述
connection_id()应该写错了,返回的是当前连接服务器的id号。我分别用sqlyog,命令行,shell连接了mysql8.0,返回的id号分别是48,52,53

其他函数

这些函数在MySQL的开发和运维过程中也是不容忽视
在这里插入图片描述
IP地址和数字的转换公式:ip地址分为四个部分,每个部分看做是一个数字的一位。然后将这个数字看做是256进制表示的一个数,将其转换为10进制

这里见到了benchmark,基准

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值