MySQL学习-第一部分

文章目录

  • MySQL数据库
  • 1 概述
    • 1.1概述
    • 1.2安装MYSQL(Windows下)
      • 1.2.1需要注意的事项:
    • 1.3 Mysql的卸载
    • 1.4查看Mysql服务
    • 1.5启动/关闭SQL的服务
    • 1.6客户端登录/使用MySql
      • 登录
      • 退出
      • 问题:ERROR 1045 (28000)
        • 解决方案
          • 第一步:关闭Mysql服务
          • 第二步:跳过Mysql密码验证
          • 第三步:无密码方式进入Mysql
          • 第四步:将登陆密码设置为空
          • 第五步:更改自己的登陆密码
          • 最后一步:验证密码是否修改成功
    • 1.7常用的mysql命令
      • 登录mysql
      • 退出mysql
      • 查看数据库
      • 使用某个数据库
      • 创建数据库
      • 查看数据库中的表
      • 查看数据库版本号
      • 查看当前使用的数据库?
    • 1.8数据库中最基本的单元:表(table)
      • 1.8.1什么是表(table)?为什么使用table来存储数据?
    • 1.9 SQL语句的分类(重要)
    • 1.10 导入SQL文件
    • 1.11查看表内信息
    • 1.12 查看表结构
  • 2 SQL语句
    • 2.1 查询语句
      • 2.1.1 简单查询 select
      • 2.1.2 查询多个字段
      • 2.1.3 查询所有字段
      • 2.1.4 别名 as
      • 2.1.5 数学表达式+-/*
      • 2.1.6 select 后跟字符/数据
      • 2.1.7 **distinct** 去除重复记录
    • 2.2 条件查询
      • =
      • >= and <=
      • between and
      • is null
      • is not null
      • and
      • or
      • and 和 or 的优先级问题
      • in
      • not in
      • like
    • 2.3 排序 order by (asc/desc)
      • 单字段排序
      • 多字段排序
      • mysql order by 实现中文排序
      • 根据字段位置进行排序(不建议)
      • 综合案例
  • 3 数据处理函数
    • 3.1单行处理函数
      • lower
      • upper
      • substr
        • 例子:实现查询结果首字母大写
      • length
      • trim
      • str_to_date(必须严格按照标准输出)
      • data_format
      • format
      • round
      • rand
      • ifnull
      • case...when...then...else...end
      • TimestampDiff(时间差计算函数)
    • 3.2 分组函数(多行处理函数)
      • 注意事项(注意事项一定要写在开头)
      • count
      • max
      • min
      • avg
  • 4 分组查询(据说非常重要) group by
    • 4.1 什么是分组查询?
    • 4.2 执行顺序(非常重要)
    • 4.3 having
    • 4.4 实际的案例
    • 4.5 总结
  • 5 连接查询
    • 5.1 什么是连接查询?
    • 5.2 连接查询的分类?
    • 5.3 笛卡尔积现象
    • 5.4 给表起别名
    • 5.5 内连接 inner join
      • 5.5.1 等值连接
      • 5.5.2 非等值连接
      • 5.5.3 自连接
    • 5.6 外连接
      • 5.6.1 右外连接 right outer join
      • 5.6.2 左外连接 left outer join
      • 5.6.3 全连接
    • 5.7 连接两张表以上
  • 6 子查询
    • 6.1 什么是子查询?
    • 6.2 子查询可以出现在哪些位置?
    • 6.3 where子句中的子查询
    • 6.4 from子句中的子查询
    • 6.5 select 子句中的子查询
  • 7 union合并查询结果集
    • 7.1 什么是union?
    • 7.2 union如何使用?
    • 7.3 union使用时的注意事项
  • 8 limit (非常重要)
    • 8.1 limit是什么?
    • 8.2 limit的用法
    • 8.3 limit 的起始位置
    • 8.4 limit 的第二个参数
    • 8.5 分页的原理
  • 9 DQL的总结

MySQL数据库

1 概述

1.1概述

MySQL数据库

MySQL是一个**关系型数据库管理系统****,**由瑞典[MySQL AB](https://baike.baidu.com/item/MySQL AB/2620844) 公司开发,属于 Oracle 旗下产品。MySQL 是最流行的关系型数据库管理系统之一,在 WEB 应用方面,MySQL是最好的 RDBMS (Relational Database Management System,关系数据库管理系统) 应用软件之一。

MySQL是一种关系型数据库管理系统,关系数据库将数据保存在不同的表中,而不是将所有数据放在一个大仓库内,这样就增加了速度并提高了灵活性。

MySQL所使用的 SQL 语言是用于访问数据库的最常用标准化语言。MySQL 软件采用了双授权政策,分为社区版和商业版,由于其体积小、速度快、总体拥有成本低,尤其是开放源码这一特点,一般中小型和大型网站的开发都选择 MySQL 作为网站数据库。

(以上介绍来源于百度)

什么是数据库?什么是数据库管理系统?什么是SQL?它们之间的关系是什么?

数据库,全称为DataBase,简称DB
	按照一定格式存储数据的一些文件的组合。
	文件中存储了具有特定格式的数据。
数据库管理系统,全称为DataBaseManagementSystem,简称DBMS
	专门用来管理数据库中的数据的系统,可以对数据库中的数据进行增删改查等操作。
	常见的DBMS:MySQL,Oracle,MS SqlServer,DB2,sybase等
SQL,全称为Structure Query Language(结构化查询语言)
	程序员编写SQL语句,然后通过DBMS来执行SQL语句,最终完成对数据库中数据的操作
	
	SQL是一套标准,SQL能在大部分数据操作系统中都可以使用,如SQL,DB2,ORACLE等
	
三者的关系:
	使用DBMS=》执行SQL=》操作DB

需要先安装MySql数据库管理系统,然后学习SQL语句的写法,然后使用DBMS,执行SQL,完成数据的操作。

DBAData Base Administrator 数据库管理员

CRUD:(增删改查)
create		创建
retrieve	查询
update		更新
delete		删除

1.2安装MYSQL(Windows下)

1.2.1需要注意的事项:

端口号:
	端口号port是任何一个软件/应用都会有的,端口号是应用的唯一代表
	端口号通常和IP地址在一块,IP地址是用来定位计算机的,端口号port是用来定位某台计算机上的某个应用/服务的
	在同一台计算机上,端口号不能重复,具有唯一性
	
	Mysql数据库启动的时候,这个服务占用的默认端口是3306
	(可以在my.ini里面更改)

字符集:
	设置MYSQL数据的字符集为UTF8
	
服务名:
	最好不要更改,默认是mysql
	
环境变量:
	将bin目录加入到环境变量path中
	
设置管理员的密码:
	用户名默认root不能改,然后设置超级管理员的密码
	
	设置密码的同时,可以激活root账户远程访问
	激活远程访问:
		激活:root账户可以远程登录
		不激活:root账户只能在本机上使用


1.3 Mysql的卸载

【MySQL卸载方法】https://blog.csdn.net/qq_45776730/article/details/123258877

注意:每个版本可能不一致,按照个人需求和实际卸载


1.4查看Mysql服务

此电脑–>右键–>管理–>服务和应用程序–>服务–>mysql

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CNSmNYjb-1690357313819)(MySQL.assets/image-20220727160202370.png)]

mysql的服务默认是“自动启动”的状态,只有启动了mysql的服务,mysql才能使用。

可以右键更改启动方式


1.5启动/关闭SQL的服务

启动

net start mysql

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-T6k3rWJa-1690357313824)(MySQL.assets/image-20220727163947608.png)]

注:start后面跟的是你配置的mysql服务的服务名称,若在之前更改了服务名称的,这里需要更改为对应的名称

如配置的服务名称为 mysql80 则应该是: net start mysql80

关闭

net stop mysql

1.6客户端登录/使用MySql

使用Mysql安装目录下bin目录下的mysql.exe打开,就可以进行登录

如果将mysql配置到了环境变量,就可以直接执行mysql命令

登录

mysql -u root -p 
回车
然后输入你的密码

退出

exit
或者
quit

问题:ERROR 1045 (28000)

Access denied for user ‘root’@‘localhost’ (using password: YES)

解决方案

【解决Mysql:ERROR 1045 (28000):Access denied for user ‘root‘@‘localhost‘ (using password: NO)的方法】https://www.jb51.net/article/250474.htm

以下命令行代码均在管理员权限下运行:

第一步:关闭Mysql服务
net stop mysql
第二步:跳过Mysql密码验证

注意:mysql8.0无法直接在my.ini中添加–skip-grant-tables来进行跳过密码验证,需要使用命令行的方式

mysqld -console --skip-grant-tables --shared-memory

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iue8BTkm-1690357313829)(MySQL.assets/image-20220727171008963.png)]

第三步:无密码方式进入Mysql

在上述步骤之后,再打开一个管理员模式运行的cmd.exe

不需要通过net start mysql打开mysql服务

然后输入

mysql -u root -p

直接回车,就可以进入mysql界面

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tnDOHJ7i-1690357313834)(MySQL.assets/image-20220727171126037.png)]

第四步:将登陆密码设置为空
use mysql; (使用mysql数据表)
update user set authentication_string='' where user='root';(将密码置为空)
quit; (然后退出Mysql)
第五步:更改自己的登陆密码

1.关闭前两个cmd窗口(一定要关闭!);

2.在第三个窗口中输入代码;

然后输入

cd D:\mysql-8.0.19-winx64\bin  (此处输入自己电脑上的安装目录)
mysql -u root -p
(此处会显示输入密码,直接回车就好了,第四步我们已经将他置为空了)
ALTER USER 'root'@'localhost' IDENTIFIED BY 'root';(By 后面跟的字符串就是你想要更新的密码)
最后一步:验证密码是否修改成功
quit(退出mysql)
mysql -u root -p 
(输入新密码,再次登录)

记录时间:2022-7-27-17:13 --好像是老问题了,之前一直没有改正过来,正好在复习的时候将其改正,一定要跟着步骤慢慢来。


1.7常用的mysql命令

注意:所有命令都需要用分号结尾(登录除外

​ 命令不区分大小写(也可以大小写混用)

​ 不见分号不执行

​ \c 指令用来终止

​ ctrl+c 退出MYSQL

登录mysql

mysql -u root -p 回车
输入密码

退出mysql

exit;

查看数据库

show databases;

使用某个数据库

use [数据库名称];

创建数据库

create database [数据库名称];
//然后查看数据库
show databases;

查看数据库中的表

show tables;

查看数据库版本号

select version()

查看当前使用的数据库?

select database();

1.8数据库中最基本的单元:表(table)

1.8.1什么是表(table)?为什么使用table来存储数据?

数据库中的表是以表格的方式来表示数据的

任何一张表都有行和列:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nEIvR8pY-1690357313839)(MySQL.assets/image-20220727174653459.png)]

每行(row)称为一个记录(数据)。

每列(column)称为一个字段。

数据类型:字符串、数字、日期等。

约束:对每个字段进行约束,有不同的规则对每个字段唯一性等建立约束


1.9 SQL语句的分类(重要)

SQL语句有很多,根据功能和作用进行分类,更方便记忆和使用。

【数据库中DQL、DML、DDL、DCL、TCL概述】https://blog.csdn.net/weixin_44169484/article/details/119255935

DQL 数据查询语言Data Query Language
	select 查询
	
DML 数据操作语言Data Manipulation Language
	insert 插入
	delete 删除(数据)
	update 更新
	
DDL 数据定义语言Data Definition Language
	create 创建
	drop 删除(表、数据库、约束、触发程序等)
	alter 修改
	Truncate语句:清空表里的数据。
		
DCL 数据控制语言Data Control Language
	grant 给与权限
	revoke 回收权限
	
TCL 事务控制语言Transaction Control Language
	commit 事务提交
	rollback 事务回滚
	Savepoint语句:为回退而存在,个数没有限制,与虚拟机中快照类似。savepoint是事务中的一点。用于取消部分事务,当结束事务时,会自动的删除该事务中所定义的所有保存点
	Set transaction语句:设置事务的各种状态,比如只读、读/写、隔离级别

1.10 导入SQL文件

xxx.sql这种文件被称为SQL脚本文件。
脚本文件中编写的是大量的SQL语句。
#我们执行SQL脚本文件时,其中的所有SQL语句都会被执行。
批量的执行SQL语句时,可以采用SQL脚本文件。
MySQL中执行脚本文件的方法:
在DOS窗口中输入
source + 脚本路径
或者是通过工具进行导入

当需要导入大量的SQL数据的时候,可以使用导入SQL脚本的形式进行写入。

注意:

​ 路径中不能带有中文、空格

​ 注意观察文件内是否有关于创建数据库的语句,若没有,需要手动创建并选择数据库

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-b0ekhbBW-1690357313843)(MySQL.assets/image-20220729143806393.png)]

然后执行 source + SQL脚本文件路径 如下:

  • 注意,一定要是没有中文且没有空格的目录下

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0ibRSMmM-1690357313848)(MySQL.assets/image-20220729143920722.png)]

1.11查看表内信息

导入成功之后,查看表:

show tables;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BgYaSKCQ-1690357313852)(MySQL.assets/image-20220729144134866.png)]

然后查看表内的数据:

select * from dept;   //查看dept表内的数据

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gFNjXGBL-1690357313857)(MySQL.assets/image-20220729144212701.png)]

select * from emp; //查看emp表中的数据

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZrWYviZI-1690357313861)(MySQL.assets/image-20220729144307147.png)]

select * from salgrade; //查看salgrade表中的数据

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-v9QvbvVn-1690357313866)(MySQL.assets/image-20220729144359838.png)]

1.12 查看表结构

使用 desc + 表名;  //查询表的结构
分别是 字段名 类型 是否可以为空 主键约束 默认数值 

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VXJ7LgB8-1690357313870)(MySQL.assets/image-20220729145451258.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3Dx5h2zN-1690357313875)(MySQL.assets/image-20220729145515221.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UoOXKO2t-1690357313879)(MySQL.assets/image-20220729145524244.png)]

2 SQL语句

2.1 查询语句

2.1.1 简单查询 select

select 字段名 from 表名;

select 和 from 是关键字

字段名 和 表名 是标识符

对于大部分数据库管理软件来说SQL结构查询语言是通用的,所有的SQL语句都应该以“ ; ”分号结尾,且SQL语句不区分大小写(Windows系统下)。(Linux 系统默认是要区分的,需要在my.ini中加入lower_case_table_name=1可以设置为不区分大小写)

2.1.2 查询多个字段

使用逗号隔开

例如:查询deptno和dname两个字段

select deptno , dname from dept;

2.1.3 查询所有字段

第一种方式:把所有的字段写上

第二种方式:用*号代替所有字段(会把 “ * ”号转为字段名,效率低,可读性差,且无法按照自己想要的顺序进行排序

select * from dept;

2.1.4 别名 as

使用 原字段名 as 你想要更改成的名称(注意,若是中文字符,则需要使用单引号进行包裹)

注:只是将表头显示的列名改为对应的名称,原字段名不会发生改变,因为select语句只负责查询。

select deptno as '序号',dname as NAME from dept;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tzb9KHfC-1690357313884)(MySQL.assets/image-20220729174241425.png)]

可以省略 as 关键字,使用空格进行分割,MYSQL依然会将你后面那个名称作为别名,如:

select deptno '序号',dname NAME from dept;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RNLPupB2-1690357313889)(MySQL.assets/image-20220729174549719.png)]

假设起别名的时候,别名中有空格怎么办?

解决方法:

使用单引号进行包裹(双引号在部分数据库中不支持,如Oracle,但是在mysql中可以使用)

select deptno as 'maka 序号',dname 'baka 名称' from dept;

2.1.5 数学表达式±/*

可以使用数学表达式对数据进行计算

select ename, sal*12 as '年薪' from emp;
select ename, sal/12 as '年薪' from emp;
select ename, sal+12 as '年薪' from emp;
select ename, sal+12 as '年薪' from emp;

2.1.6 select 后跟字符/数据

使用select时,可以若select没有的字段时,就会报错

如:

mysql> select abc from emp;
ERROR 1054 (42S22): Unknown column 'abc' in 'field list'

但是若select 的目标是一个字符串,那么就会将这个字符串按照表记录的数量,打印对应数量的行,详细如下所示:

mysql> select 'abc' as '表中的abc' from emp;
+-----------+
| 表中的abc |
+-----------+
| abc       |
| abc       |
| abc       |
| abc       |
| abc       |
| abc       |
| abc       |
| abc       |
| abc       |
| abc       |
| abc       |
| abc       |
| abc       |
| abc       |
+-----------+
14 rows in set (0.00 sec)

同理,1000也被视为字符串

select 1000 as '表中的abc' from emp;

2.1.7 distinct 去除重复记录

把查询结果去除重复记录【distinct】
distinct只能出现在所有字段的最前方。
distinct出现在job,deptno两个字段之前,表示两个字段联合起来去重。

select distinct job from emp;
select distinct job,deptno from emp;

//可以对分组函数使用distinct
select distinct round(avg(sal),2),job from emp group by job;

select job , destinct deptno from emp;//错误

2.2 条件查询

查询符合条件的数据

select 字段名 from 表名 where 条件
= 等于
<> 或者 != 不等于
< 小于
<= 小于等于
> 大于
>= 大于等于
between ... and ... 两个值之间,等同于 >= and <=
is null 判断是否为空 (is not null)判断是否不为空
and 并且 
or 或者
in 包含 相当于多个 or (not in 不在这个范围中)
not 取非,主要用于 is 或者 in 中
like 模糊查询,使用各种通配符进行模糊匹配
		% 或者 _ 匹配
		% 匹配任意多个字符
		_ 下划线只匹配一个字符

例举:

查询薪资小于等于5000的所有员工的编号和名称

select empno as '编号', ename as '名称',sal as '薪资'  from emp where sal <= 5000;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BVRiVnjF-1690357313893)(MySQL.assets/image-20220729180724662.png)]

=

‘ = ’ 符号也可以使用在字符串对比上

比如查询姓名为‘smith’的所有信息

select * from emp where ename = 'smith';

>= and <=

查询大于2000小于6000的sal

select empno as '编号',ename as '名称',sal as '薪资' from emp where sal >= 2000 and sal <= 6000; 

或者

between and

select empno as '编号',ename as '名称',sal as '薪资' from emp where sal between 2000 and 6000;

is null

数据库中 null 不能使用 ‘ = ‘ 进行比对,需要使用 is null 进行比对

select empno '编号',ename '姓名',sal '薪资',comm '额外薪资' from emp where comm is null;

is not null

select empno '编号',ename '姓名',sal '薪资',comm '额外薪资' from emp where comm is not null;

and

用于两个条件语句的连接,意为 条件A和条件B 同时满足

select empno '编号',ename '姓名',sal '薪资',comm '额外薪资' from emp where job = 'Manager' and sal > 2000;

or

用于两个条件语句的连接,意为 条件A和条件B 满足其中一个

select empno '编号',ename '姓名',sal '薪资',comm '额外薪资',job '工作' from emp where job = 'Manager' or job = 'salesman';

and 和 or 的优先级问题

工资大于2500,且部门编号为10或者20的员工。

select * from emp where sal > 2500 and deptno = 10 or deptno = 20;

and 语句的优先级比 or 的优先级高不论位置

所以以上语句表示的是 工资大于2500和编号为10的 员工,或者编号为20的员工。

所以应该使用括号将低优先级的or进行包裹,让其优先级变高。

select * from emp where sal > 2500 and (deptno = 10 or deptno = 20);

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8b2Tavv0-1690357313897)(MySQL.assets/image-20220730084233185.png)]

绿色为正确结果,红色为错误的结果。

in

包含,相当于多个 or

例如:查询工作岗位是Manager 和 salesMan 的员工:

select empno '编号',ename '姓名',sal '薪资',comm '额外薪资',job '工作' from emp where job = 'Manager' or job = 'salesman';

用 in 的方式

select empno '编号',ename '姓名',sal '薪资',comm '额外薪资',job '工作' from emp where job in ('Manager' , 'salesman');

注意:in 不是区间,是具体值,相当于job = ‘具体值’ or job = ‘具体值’;

not in

具体同上, 只不过是相反,不等于这几个值的意思。

select empno '编号',ename '姓名',sal '薪资',comm '额外薪资',job '工作' from emp where job not in ('Manager' , 'salesman');

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pWa2t3gc-1690357313902)(MySQL.assets/image-20220730084902404.png)]

like

模糊查询

支持%或者_匹配

%匹配多个字符,_匹配一个字符

例如:

查询名字中含有 ‘ t ’ 的:

select empno '编号',ename '姓名',sal '薪资',comm '额外薪资',job '工作' from emp where ename like '%t%';

名字中第二位是 ’ m ’ 的:

select empno '编号',ename '姓名',sal '薪资',comm '额外薪资',job '工作' from emp where ename like '_m%';

若需要找 名字中带有下划线的:

使用 ’ \ ’ 转义字符,若名字中找带有 ‘ \ ’ 的:/ \

select empno '编号',ename '姓名',sal '薪资',comm '额外薪资',job '工作' from emp where ename like '\_%';

2.3 排序 order by (asc/desc)

排序总是在最后,如

select … from … where … order by

单字段排序

默认排序是升序,也就是默认带有order by [字段名] asc

select empno '编号',ename as '姓名',sal as '薪资' from emp order by sal (后面默认带一个asc表示为升序);

降序 desc

select empno '编号',ename as '姓名',sal as '薪资' from emp order by sal desc;

多字段排序

如:薪资升序的同时,名称按照A-Z的顺序排

使用 ’ , ’ 分隔不同的

select empno '编号',ename as '姓名',sal as '薪资' from emp order by sal asc , ename asc;

排序出来的结果,首先是按照第一个条件进行排序的,然后值相同的情况下,按照第二个条件进行排序。

mysql order by 实现中文排序

在mysql 中,我们经常会对一个字段进行排序,若不是中文字段则可以使用order by ColumnName,但进行中文字段排序,对汉字的排序结果往往都是错误的。 这是因为order by是根据对应字符的ASCII码排序。

如要实现按照中文拼音的排序,不想改变数据库结构的话,简单的做法是,在sql语句内加入CONVERT 函数即可。

select * from table order by CONVERT(列名 USING gbk);

https://blog.csdn.net/weixin_44948683/article/details/105842081

根据字段位置进行排序(不建议)

根据查询出来结果的第一个字段进行排序:(不建议,列顺序容易发送变化,且没有健壮性。)

select empno '编号',ename as '姓名',sal as '薪资' from emp order by 1 asc;

程序健壮性就是你的程序在遇到异常的情况下还能运行或者给予用户友好的提示。所以写代码的时候尽量考虑周全。

该做异常处理的就做异常处理,性能和正确性比起来一文不值。

综合案例

寻找工资在2000到3000之间,且job不为Manager,按照工资的大小降序排序,相同的薪资以名称升序排序。

select ename as '姓名',sal as '薪资' from emp where sal between 2000 and 3000 order by sal desc,ename asc;

3 数据处理函数

函数名 作用
lower 转换小写
upper 转换大写
length 取长度
substr 取子串的长度
str_to_date 字符串转为日期
date_format 格式化日期
format 设置千分位
round 四舍五入
rand() 生成随机数
ifnull 可以将null转换成一个具体值

3.1单行处理函数

数据处理函数又被称为单行处理函数

单行处理函数的特点是:一个输入对应一个输出

和单行处理函数相对的是:多行处理函数(多个输入对应一个输出,例如统计所有员工的工资总和,select sum(sal) as ‘总薪资’ from emp;

lower

例如将显示的所有名称转为小写:

select lower(ename) as '姓名' from emp order by ename asc;

upper

将所有的字母转为大写

select upper(ename) as '姓名' from emp order by ename asc;

substr

方法体:substr(被截取的字符串, 起始下标, 截取的长度)

substr的起始下标从1开始

select substr(ename,1,2) as '截取之后的ename' from emp;

例子:找出员工名字第一个字母是A的员工信息?

第一种:模糊查询

select ename from emp where ename like 'A%';

第二种:substr方式

select ename from emp where substr(ename,1,1) = 'A';

例子:实现查询结果首字母大写

concat() 字符串的拼接

select concat(substr(upper(ename),1,1),lower(substr(ename,2,length(ename)))) as '首字母大写' from emp;

length

获取字符串的长度

select ename as '名称', length(ename) as '名称的长度' from emp;

trim

去除前后空白

例如 前端传过来的数据中前后带有空白,此时就需要使用trim

select ename from emp where ename = trim('   king  ');

但是中间的空格就不会去除

select ename from emp where ename = trim('   k i n g  ');
结果:Empty set (0.00 sec)

str_to_date(必须严格按照标准输出)

把字符串转为日期

 select * from emp where hiredate=str_to_date(1981-02-20);
 
 drop table if exists t_user;
 create table if not exists t_user(
 	user_id int,
     user_name varchar(30),
     user_birth date
 );
 
 #插入数据时:
 insert into t_user values(1,'makabaka','1990-10-1'); 
 #执行成功....
 Query OK, 1 row affected (0.01 sec)
 
 #但是如果插入时,没有按照对应的以 年-月-日 来插入,如下:
 insert into t_user values(1,'makabaka','10-1-1990'); 
 #结果:
 ERROR 1292 (22007): Incorrect date value: '10-1-1990' for column 'user_birth' at row 1
 #执行错误,因为'19990-10-1'不是一个date类型的数据
 
 语法格式:
 	str_to_date(日期的字符串,日期的格式)
 mysql 的日期格式:
 	%Y	年
 	%m	月
 	%d	日
 	%h	时
 	%i	分
 	%s	秒
 
  insert into t_user values(2,'wuxidixi',str_to_date('10-1-1990','%m-%d-%Y')); 
  #此时:
  Query OK, 1 row affected (0.00 sec)
  #执行成功

str_to_date函数可以把字符串varchar转为日期date类型的数据

通常使用在insert into数据插入方面,因为某些字段会被定义为date类型的数据

需要通过该函数将字符串转为date类型

但是若插入时,字符串匹配了 [ 年-月- 日 ] 格式,就不用使用该函数。

了解一下:

#java中的日期格式
yyyy-HH-dd HH:mm:ss SSS

data_format

将date类型转换成一定格式的varchar类型字符串

用法:

date_format(日期类型的数据,需要显示的日期格式)
一般用在查询日期方面,设置展示的日期格式。

select user_id as '编号',user_name as '姓名',date_format(user_birth,'%m/%d/%Y')  as '生日' from t_user;
#结果:
+------+----------+------------+
| 编号 | 姓名     | 生日       |
+------+----------+------------+
|    1 | makabaka | 10/01/1990 |
|    2 | wuxidixi | 10/01/1990 |
+------+----------+------------+
2 rows in set (0.00 sec)

#select查询date型的数据时,会自动将date转为varchar类型显示在dos命令窗口中,并且采用磨人的mysql日期格式:'%Y-%m-%d'

format

数字的格式化,给数字添加千分位,第二个参数是保留的小数位数,且函数自带四舍五入

select ename as '员工名',format(sal,2) as '工资' from emp;
#结果:
+--------+----------+
| 员工名 | 工资     |
+--------+----------+
| SMITH  | 800.00   |
| ALLEN  | 1,600.00 |
...
| FORD   | 3,000.00 |
| MILLER | 1,300.00 |
+--------+----------+
14 rows in set (0.00 sec)
#当第二个参数为0时,表示取整
SELECT FORMAT(100.7654,3);//四舍   100.765
SELECT FORMAT(100.7655,3);//五入   100.766

round

四舍五入

round(数字, 需要保留的小数位数)

select round(1234.567,0) as 'result' from dept;
结果为:
+--------+
| result |
+--------+
|   1230 |
|   1230 |
|   1230 |
|   1230 |
+--------+
4 rows in set (0.01 sec)

第二个参数若为负数,则就往前四舍五入(以小数点的位置,负数为往前,往前进行四舍五入

如:

select round(1234.567,-1) as 'result' from dept;
结果为:
+--------+
| result |
+--------+
|   1230 |
|   1230 |
|   1230 |
|   1230 |
+--------+
4 rows in set (0.00 sec)

若数字为1235.666,此时再去取-1

mysql> select round(1235.666,-1) as 'result' from dept;
结果为:
+--------+
| result |
+--------+
|   1240 |
|   1240 |
|   1240 |
|   1240 |
+--------+
4 rows in set (0.00 sec)

可以看到,此时是按小数点的前一位,也就是10位作为四舍五入的位置。

rand

生成一个范围0-1的随机数

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hwAma4Cr-1690357313907)(MySQL.assets/image-20220730180017319.png)]

生成100以内的随机数

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GzkBehNQ-1690357313911)(MySQL.assets/image-20220730184244969.png)]

ifnull

空处理函数,专门处理空数据

在所有数据中,只要有null参与运算,则最终结果一定为null

如:计算年薪

select ename as '姓名', (sal + comm) * 12 as '年薪' from emp;
结果为:
+--------+----------+
| 姓名   | 年薪     |
+--------+----------+
| SMITH  |     NULL |
| ALLEN  | 22800.00 |
| WARD   | 21000.00 |
| JONES  |     NULL |
| MARTIN | 31800.00 |
| BLAKE  |     NULL |
| CLARK  |     NULL |
| SCOTT  |     NULL |
| KING   |     NULL |
| TURNER | 18000.00 |
| ADAMS  |     NULL |
| JAMES  |     NULL |
| FORD   |     NULL |
| MILLER |     NULL |
+--------+----------+
14 rows in set (0.00 sec)

为了避免这个现象,则需要使用ifnull函数。

用法:

ifnull(数据, 若为空时赋的值)

还是以上的例子,如果补助为null时,把补助设置为0

select ename as '姓名', (sal + ifnull(comm,0)) * 12 as '年薪' from emp;
结果为:
+--------+----------+
| 姓名   | 年薪     |
+--------+----------+
| SMITH  |  9600.00 |
| ALLEN  | 22800.00 |
| WARD   | 21000.00 |
| JONES  | 35700.00 |
| MARTIN | 31800.00 |
| BLAKE  | 34200.00 |
| CLARK  | 29400.00 |
| SCOTT  | 36000.00 |
| KING   | 60000.00 |
| TURNER | 18000.00 |
| ADAMS  | 13200.00 |
| JAMES  | 11400.00 |
| FORD   | 36000.00 |
| MILLER | 15600.00 |
+--------+----------+
14 rows in set (0.00 sec)

case…when…then…else…end

例子:当员工工作岗位为Manager的时候,工资上涨10%,当工作岗位为Salesman的时候,工资上调50%

select ename as '名称',job as '工作',sal as'薪资', 
		(case job 
         	when 'Manager' then sal*1.1
         	when 'salesman' then sal*1.5
         	else sal end) as '真实薪资'
from emp where sal > 1000;

TimestampDiff(时间差计算函数)

TimestampDiff(间隔类型,前一个日期,后一个日期)

TimestampDiff(YEAR,hiredate,now())

间隔类型:
	second	秒
	minute	分钟
	hour	小时
	day		天
	week	星期
	month	月
	quarter	季度
	year	年

3.2 分组函数(多行处理函数)

多行处理函数的特点是,输入多行,最终输出一行

名称 作用
count 计数
sum 求和
avg 平均值
max 最大值
min 最小值

分组函数使用时,必须先进行分组(通过where或者其它语句筛选过后),才能使用。

若没有对数据进行分组,则整张表作为一个分组。

在分组函数后面使用where进行条件筛选的话,效率会变低

分组函数不能直接使用在where子句中

例子:

找出比最低工资高的员工信息

select ename as '姓名' ,sal as '薪资' from emp where sal > min(sal);
结果:
ERROR 1111 (HY000): Invalid use of group function :分组函数的使用无效
invalid:无效的    (valid:有效的)

所有的分组函数可以组合在一起使用

select sum(sal) as '工资总和',max(sal) as '最高工资',min(sal) as '最低工资',avg(sal) as '平均工资',count(*) as '数据总数' from emp; 

结果:
+----------+----------+----------+-------------+----------+
| 工资总和 | 最高工资 | 最低工资 | 平均工资    | 数据总数 |
+----------+----------+----------+-------------+----------+
| 29025.00 |  5000.00 |   800.00 | 2073.214286 |       14 |
+----------+----------+----------+-------------+----------+

注意事项(注意事项一定要写在开头)

在comm中,有数据为null的话,分组函数会自动忽略null

例如,数据为

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HtYJ3xE6-1690357313916)(MySQL.assets/image-20220730192707185.png)]

当我们使用sum函数进行累加时:

因为其中有null,所以正常的运算符进行计算的话,则会返回null(因为null参加了运算,详情见上一章单行处理函数。

但是使用分组函数时,函数会自动忽略其中的null,不需要提前对null进行处理。

select sum(comm) as '补助总和' from emp;
结果:
+----------+
| 补助总和 |
+----------+
|  2200.00 |
+----------+
1 row in set (0.00 sec)

count

统计出符合条件的记录数

select count(ename) as 'emp表内的总数据数' from emp;

注意:count不会统计null的数量,会自动忽略null

select count(comm) as '补助总数' from emp;
结果为:
+----------+
| 补助总数 |
+----------+
|        4 |
+----------+
1 row in set (0.00 sec)

count(*) 和 count(具体字段)的区别:

count(*):查询所有的记录的数量,只要一条记录中有一个字段不为空,这条记录就是有效的,统计所有有效的字段。
count(具体字段):表示统计该字段下所有不为null的记录数量,当其中有为null的数据时,会跳过该记录。

max

找出数据中最大的一项。

例子:

找出最高工资?

select max(sal) as '最高工资' from emp;

min

找出数据中最小的一项

select min(sal) as '最低工资' from emp;

avg

取出所有数据的平均值

select avg(sal) as '平均工资' from emp;

4 分组查询(据说非常重要) group by

4.1 什么是分组查询?

在实际的应用中,可能需要对所有数据中的某一部分数据进行操作,这时候我们就要需要使用分组查询。

select ....
from ...
group by ...

举例:计算每个部门的工资和?
	计算每个岗位的平均薪资?
	计算某个部门的最高薪资?

4.2 执行顺序(非常重要)

select ...
from ...
where ...
group by...
order by ...

以上是书写顺序

执行顺序:

1.from ...
2.where ...
3.group by ...
4.select ...
5.order by ...
select ename,sal from emp where sal > min(sal);
结果:
ERROR 1111 (HY000): Invalid use of group function
分组函数的无效使用

注意,此时的where在group by之前执行,也就是说,在分组之前会执行条件筛选,如此一来,按照正常逻辑写的数据查询,可能会出错,得到错误的结果。

为什么分组函数不能直接使用在where后面?

因为分组函数在使用的时候必须先进行分组,where执行的时候还没有分组,所以where后面不能出现分组函数。

select sum(sal) from emp;

这个没有分组,为啥sum()函数可以用呢?

因为 select 在 group by 之后执行 (按照如上的执行顺序) 。

4.3 having

having 是在group by分组之后,进一步对分组的数据进行筛选的关键字,不能脱离order by 单独使用,一定是紧跟在group by 后面的。

4.4 实际的案例

  1. 找出每个工作岗位的工资和?

思路:按照岗位分组,然后对每个分组进行求和

所以 group by job 然后会用到 sum() 函数

select job as '工作',sum(sal) as '岗位薪资'
from emp 
group by job;

分析:

执行顺序:从emp中查询数据,根据job字段进行分组,然后对每个分组执行sum()求和

思考:可以将ename加入select的条件吗?

可以,但是结果不符合需求或者输出错误的值

select ename,job,sum(sal) from emp group by job;

以上语句在mysql中可以执行,但是毫无意义,

以上语句在oracle中执行,会产生错误。

(说明mysql中的sql语法相对于oracle来说更加的松散)

结论:

​ 在一条select查询语句当中,若其中有group by语句,select 后面只能跟 参加分组的字段,以及分组的函数。

  1. 找出每个部门的最高薪资

思路:先按照每个部门的部门编号进行分组,然后使用max()分组函数进行取值。

select deptno as '部门编号', max(sal) as '部门最高薪资' from emp group by deptno;
结果为:
+----------+--------------+
| 部门编号 | 部门最高薪资 |
+----------+--------------+
|       20 |      3000.00 |
|       30 |      2850.00 |
|       10 |      5000.00 |
+----------+--------------+
3 rows in set (0.00 sec)
  1. 找出每个部门不同工作的最高薪资

思路:先按照每个部门进行分组,然后按照工作进行分组,最后使用max()

按照多个字段进行分组,不同字段之间用逗号隔开

select deptno as '部门编号',job as '工作',max(sal) as '最高工资' from emp group by deptno,job order by deptno;
结果:
+----------+-----------+----------+
| 部门编号 | 工作      | 最高工资 |
+----------+-----------+----------+
|       10 | CLERK     |  1300.00 |
|       10 | MANAGER   |  2450.00 |
|       10 | PRESIDENT |  5000.00 |
|       20 | ANALYST   |  3000.00 |
|       20 | CLERK     |  1100.00 |
|       20 | MANAGER   |  2975.00 |
|       30 | CLERK     |   950.00 |
|       30 | MANAGER   |  2850.00 |
|       30 | SALESMAN  |  1600.00 |
+----------+-----------+----------+
9 rows in set (0.00 sec)
  1. 找出每个部门最高的薪资,且只显示最高薪资大于3000的

思路:先按照每个部门进行分组,然后使用max函数显示最高的工资,然后通过having进行对分组后的数据进一步筛选。

select deptno as '部门编号',max(sal) as '最高工资' from emp group by deptno having max(sal)>3000;
执行结果:
+----------+----------+
| 部门编号 | 最高工资 |
+----------+----------+
|       10 |  5000.00 |
+----------+----------+
1 row in set (0.00 sec)

思考:以上的SQL语句的执行效率是否过低?

比较低,实际可以先将大于3000的数据找出来之后再进行分组。

参考:https://blog.csdn.net/weixin_34377065/article/details/94023858

select 
	deptno as '部门编号',max(sal) as '最高工资' 
from
	emp
where
	sal>3000
group by
	deptno;

优化策略:where和having 优先选择 where,where完成不了的,再选择having

  1. 找出每个部门的平均薪资,且只显示平均薪资高于2500的。

思路:按照每个部门进行分组,然后用avg()函数取平均值,然后筛选出2500以下的。

select deptno as '部门编号',round(avg(sal),1) as '最高工资' from emp group by deptno having avg(sal)>2500;

4.5 总结

单表查询章节结束

select ....
from ...
where ...
group by ...
having ...
order by ...

执行顺序:
1.from
2.where
3.group by
4.having
5.select
6.order by

案例:

找出每个岗位的平均薪资,要求显示平均薪资大于1500的,除去工作岗位为Manager的,且按照平均薪资进行降序排序。

思路:按照每个岗位进行分组,然后使用having去进行第二次平均薪资的筛选,然后使用not in manager 除去工作岗位为manager的,然后使用order by 平均薪资 desc 进行排序。

select 
	job as '工作岗位',round(avg(sal),1) as '平均工资' 
from
	emp
where
	job not in ('manager') // job != 'manager'也可以
group by
	job
having
	avg(sal)>1500
order by
	avg(sal) desc;
	
+-----------+----------+
| 工作岗位  | 平均工资 |
+-----------+----------+
| PRESIDENT |     5000 |
| ANALYST   |     3000 |
+-----------+----------+
2 rows in set (0.00 sec)

5 连接查询

5.1 什么是连接查询?

从一张表中单独查询称为单表查询

多张表的数据,从A表中取出一部分,从B表中去除一部分,将这两部分连接起来,查询,称为连接查询。

5.2 连接查询的分类?

年份进行分类

SQL92:1992年出现的语法
SQL99:1999年出现的语法

连接方式分类

内连接
	等值连接
	非等值连接
	自连接
外连接
	左外连接(左连接
	右外连接(右连接
全连接

5.3 笛卡尔积现象

案例:查询每个员工所在部门的名称?

select ename,deptno from emp;
select denptno,dname from dept;

select emp.ename as '员工姓名',emp.deptno as '部门编号',dept.dname as '部门名称'
from emp,dept; //没有任何条件限制
+----------+----------+------------+
| 员工姓名 | 部门编号 | 部门名称   |
+----------+----------+------------+
| SMITH    |       20 | OPERATIONS |
| SMITH    |       20 | SALES      |
| SMITH    |       20 | RESEARCH   |
| SMITH    |       20 | ACCOUNTING |
| ALLEN    |       30 | OPERATIONS |
| ALLEN    |       30 | SALES      |
| ALLEN    |       30 | RESEARCH   |
| ALLEN    |       30 | ACCOUNTING |
...
| MILLER   |       10 | OPERATIONS |
| MILLER   |       10 | SALES      |
| MILLER   |       10 | RESEARCH   |
| MILLER   |       10 | ACCOUNTING |
+----------+----------+------------+
56 rows in set (0.00 sec)//共有56条  笛卡尔积现象

当两张表进行连接查询,没有任何限制的时候,最终的结果是两张表记录的乘积,A表有5条,B表有6条,则结果就又5*6=30条。

这个现象叫做笛卡尔积现象。

得出结论:在进行表连接时,需要在连接时将条件增加到后面:

select emp.ename as '员工姓名',emp.deptno as '部门编号',dept.dname as '部门名称'
from emp,dept 
where dept.deptno=emp.deptno;//指定相同字段
结果:
+----------+----------+------------+
| 员工姓名 | 部门编号 | 部门名称   |
+----------+----------+------------+
| SMITH    |       20 | RESEARCH   |
| ALLEN    |       30 | SALES      |
| WARD     |       30 | SALES      |
| JONES    |       20 | RESEARCH   |
| MARTIN   |       30 | SALES      |
| BLAKE    |       30 | SALES      |
| CLARK    |       10 | ACCOUNTING |
| SCOTT    |       20 | RESEARCH   |
| KING     |       10 | ACCOUNTING |
| TURNER   |       30 | SALES      |
| ADAMS    |       20 | RESEARCH   |
| JAMES    |       30 | SALES      |
| FORD     |       20 | RESEARCH   |
| MILLER   |       10 | ACCOUNTING |
+----------+----------+------------+
14 rows in set (0.00 sec)  

最终的查询结果条数是14条,但是在匹配的过程中,匹配的次数还是没有减少,依然是56次,但是4选1。

就算加上条件也无法避免底层的匹配次数,底层还是会把表中的每一条行数据都给匹配一遍,只是显示的结果是有效数据

降低表的连接次数也是性能优化的重要手段

5.4 给表起别名

select e.ename as '员工姓名',e.deptno as '部门编号',d.dname as '部门名称'
from emp as e,dept as d
where e.deptno=d.deptno;//指定相同字段 SQL92语法

给表名起别名的话,使用as的话,好像会报错(未验证过

为什么select上可以直接使用from 的时候才定义的表别名呢?

因为执行顺序的不同(见上一章),首先执行的是from,后执行的select。

使用表名前缀可以提高执行效率。

如果使用了表的别名,则不能再使用表的真名。

性能影响可以忽略不计,但是您可以更好地阅读查询。这只是为了你的方便。

性能影响是在SQL Server程序本身中分配几kb的内存来存储别名等。与执行查询所需的其余操作相比,这几乎是小菜一碟。

5.5 内连接 inner join

5.5.1 等值连接

例:

查询每个员工所在的部门名称,并显示员工名称和部门名称

#SQL92语法

select 
	e.ename as '员工名',d.dname as '部门名'
from
	emp e,dept d
where
	e.deptno=d.deptno;
#92语法:结构不清晰,where后面容易混杂

#SQL99语法
select 
	e.ename as '员工名',d.dname as '部门名'
from
	emp e
inner join
	dept d
on
	e.deptno=d.deptno <-连接条件
where
	e.ename='smith';
	
#inner 可以省略 表示内连接,inner 可读性更好
#表连接和筛选语法进行了分离
#99语法后 on后面依然可以添加where来进行筛选

5.5.2 非等值连接

例子:

找出每个员工的薪资等级,要求显示员工名,薪资和薪资等级。

select 
	e.ename as '员工名称',e.sal as '工资',s.grade as '工资等级'
from
	emp e
inner join
	salgrade s 
on
	e.sal between s.losal and hisal
order by
	e.sal asc;

5.5.3 自连接

例子:

查询员工的上级领导,要求显示员工名和对应的领导名称

select
	e.empno as '员工编号',e.ename as '员工名',e.mgr as '领导编号',b.ename as '领导名称'
from
	emp e
inner join
	emp b
on
	ifnull(e.mgr,7839) = b.empno;
	
#ifnull替换空值为king的员工编号(自己是自己的领导),否则查询出来只有12条记录
	
结果:
+----------+--------+----------+----------+
| 员工编号 | 员工名 | 领导编号 | 领导名称 |
+----------+--------+----------+----------+
|     7369 | SMITH  |     7902 | FORD     |
|     7499 | ALLEN  |     7698 | BLAKE    |
|     7521 | WARD   |     7698 | BLAKE    |
|     7566 | JONES  |     7839 | KING     |
|     7654 | MARTIN |     7698 | BLAKE    |
|     7698 | BLAKE  |     7839 | KING     |
|     7782 | CLARK  |     7839 | KING     |
|     7788 | SCOTT  |     7566 | JONES    |
|     7839 | KING   |     NULL | KING     |
|     7844 | TURNER |     7698 | BLAKE    |
|     7876 | ADAMS  |     7788 | SCOTT    |
|     7900 | JAMES  |     7698 | BLAKE    |
|     7902 | FORD   |     7566 | JONES    |
|     7934 | MILLER |     7782 | CLARK    |
+----------+--------+----------+----------+
14 rows in set (0.00 sec)

5.6 外连接

外连接的例子:

#内连接的特点:必须完全匹配的数据才能被查出来
select 
	e.ename as '员工名',d.dname as '部门名'
from
	emp e
inner join
	dept d
on
	e.deptno=d.deptno;
	
#外连接:把匹配不上的数据也依然显示出来
select 
	e.ename as '员工名',d.dname as '部门名'
from
	emp e
right join
	dept d
on
	e.deptno=d.deptno;

#外连接中,两张表是有主次关系的,需要指定主表,主表中的数据将会完全显示出来。
#内连接中两张表没有主次关系。

#任何一个左连接都有一个右连接的写法
#任何一个右连接都有一个左连接的写法

#外连接的查询结果条数 一定 大于等于 内连接的查询结果条数

#outer关键字可以省略

5.6.1 右外连接 right outer join

#外连接:把匹配不上的数据也依然显示出来
#主要是为了将这张表的所有数据全都查询出来,捎带着关联查询左边的表
#就是为了将符合两个表条件的数据 和 右边表剩余的,没有匹配上的数据的全部显示出来
select 
	e.ename as '员工名',d.dname as '部门名'
from
	emp e
right outer join
	dept d
on
	e.deptno=d.deptno;

5.6.2 左外连接 left outer join

select 
	e.ename as '员工名',d.dname as '部门名'
from
	emp e
left outer join
	dept d
on
	e.deptno=d.deptno;

案例:

查询每个员工的上级领导,要求显示所有员工的名字和领导名

select
	a.ename as '员工名称',b.ename as '领导名称'
from
	emp a
left outer join
	emp b
on
	a.mgr=b.empno;
	
结果:
+----------+----------+
| 员工名称 | 领导名称 |
+----------+----------+
| SMITH    | FORD     |
| ALLEN    | BLAKE    |
| WARD     | BLAKE    |
| JONES    | KING     |
| MARTIN   | BLAKE    |
| BLAKE    | KING     |
| CLARK    | KING     |
| SCOTT    | JONES    |
=>| KING     | NULL     |<= king也被显示出来了
| TURNER   | BLAKE    |
| ADAMS    | SCOTT    |
| JAMES    | BLAKE    |
| FORD     | JONES    |
| MILLER   | CLARK    |
+----------+----------+
14 rows in set (0.00 sec)

5.6.3 全连接

全连接显示两侧表中所有满足检索条件的行。

只做一个了解,实际需求用的很少

了解一下写法就行了

#oracle 的全连接
select * from a full join b on a.id = b.id

#mysql 的全连接
select * from a left join b on a.id = b.id
union
select * from a right join b on a.id = b.id

5.7 连接两张表以上

#若我们想要连接3、4、5张表怎么办?
select ...xxx
from ...a
(inner)join ...b
on ...a.x=b.y
left (outer) join ...c
on ...b.x=c.y
right (outer) join ...d
on ...c.x=d.y
group by ...
where ...
order by ...
#以此类推

案例:

找出每个员工的部门名称以及工资等级和上级领导,且要显示员工名、部门名、领导名、薪资、薪资等级

#各个表结构(只展示一部分):
mysql> select * from salgrade;
+-------+-------+-------+
| GRADE | LOSAL | HISAL |
+-------+-------+-------+
|     1 |   700 |  1200 |

mysql> select * from dept;
+--------+------------+----------+
| DEPTNO | DNAME      | LOC      |
+--------+------------+----------+
|     10 | ACCOUNTING | NEW YORK |

mysql> select * from emp;
+-------+--------+-----------+------+------------+---------+---------+--------+
| EMPNO | ENAME  | JOB       | MGR  | HIREDATE   | SAL     | COMM    | DEPTNO |
+-------+--------+-----------+------+------------+---------+---------+--------+
|  7369 | SMITH  | CLERK     | 7902 | 1980-12-17 |  800.00 |    NULL |     20 |

思路:三个表连接,然后显示对应的字段即可。

select
	e.ename as '员工名称',d.dname as '部门名称',e2.ename as '领导名称',e.sal as '薪资',s.grade as '薪资等级'
from
	emp e
inner join
	dept d
on 
	e.deptno=d.deptno
inner join 
	salgrade s
on
	e.sal between s.losal and s.hisal
left outer join
	emp e2
on
	e.mgr=e2.empno;
	
结果为:
+----------+------------+----------+---------+----------+
| 员工名称 | 部门名称   | 领导名称 | 薪资    | 薪资等级 |
+----------+------------+----------+---------+----------+
| SMITH    | RESEARCH   | FORD     |  800.00 |        1 |
| ALLEN    | SALES      | BLAKE    | 1600.00 |        3 |
| WARD     | SALES      | BLAKE    | 1250.00 |        2 |
| JONES    | RESEARCH   | KING     | 2975.00 |        4 |
| MARTIN   | SALES      | BLAKE    | 1250.00 |        2 |
| BLAKE    | SALES      | KING     | 2850.00 |        4 |
| CLARK    | ACCOUNTING | KING     | 2450.00 |        4 |
| SCOTT    | RESEARCH   | JONES    | 3000.00 |        4 |
| KING     | ACCOUNTING | NULL     | 5000.00 |        5 | <=左外连接显示了领导编号为null| TURNER   | SALES      | BLAKE    | 1500.00 |        3 |
| ADAMS    | RESEARCH   | SCOTT    | 1100.00 |        1 |
| JAMES    | SALES      | BLAKE    |  950.00 |        1 |
| FORD     | RESEARCH   | JONES    | 3000.00 |        4 |
| MILLER   | ACCOUNTING | CLARK    | 1300.00 |        2 |
+----------+------------+----------+---------+----------+
14 rows in set (0.00 sec)

6 子查询

6.1 什么是子查询?

select 中嵌套 select 查询语句,被嵌套的 select 语句称为子查询。

6.2 子查询可以出现在哪些位置?

select 
	..(select)
from
	..(select)
where
	..(select)

#综上,select可以出现在select\from\where语句的后面

6.3 where子句中的子查询

案例:找出比最低工资高的员工姓名和工资

select ename,sal
from emp
where sal > (select min(sal) from emp);

#程序会首先执行子查询 select min(sal) from emp; 得到最低的工资
#然后就根据查询到的最低工资与sal对比,然后返回所有满足条件的记录

6.4 from子句中的子查询

from后面的子查询,可以将子查询的查询结果当做一张临时表(技巧)

案例:

​ 找出每个岗位的平均工资的工资等级(按照岗位分组,求平均值,再进行分等级)

#首先按照岗位来分组,算出平均工资
select job,round(avg(sal),0) from emp group by job;
#然后算出来的表如下:
+-----------+-------------------+
| job       | round(avg(sal),0) |
+-----------+-------------------+
| CLERK     |              1038 |
| SALESMAN  |              1400 |
| MANAGER   |              2758 |
| ANALYST   |              3000 |
| PRESIDENT |              5000 |
+-----------+-------------------+
5 rows in set (0.00 sec)

#然后把这个表跟salgrade进行连接
select 
	temp.job as '工作岗位',temp.avgsal as '岗位平均工资',s.grade as '薪资等级'
from
	(select job,round(avg(sal),0) as avgsal from emp group by job) temp
inner join
	salgrade s
on
	temp.avgsal between s.losal and s.hisal;

一定要有嵌套的思想,和明确的思路,每个表查出来是什么要有一个大概的认识。

6.5 select 子句中的子查询

找出每个员工的部门名称,要求显示员工名和部门名

select e.ename,d.dname,d.dname
from emp e
join dept d
on e.deptno=d.deptno
order by e.ename;
#select 子句 有且只能有返回一条数据
select e.ename as '员工名',(select d.dname from dept d where e.deptno=d.deptno) as '部门名称' from emp e order by e.ename;

select子查询在select之后时,子查询只能每次查询返回一条结果。

7 union合并查询结果集

7.1 什么是union?

“UNION表示“并”,当用的时候,系统会自动将重复的元组去掉,如果要保留重复元组则就用UNION ALL UNION 会合并重复数据,(由于要合并重复,该操所 隐藏着一个 排序的操作.)UNION ALL 简单合并,不会合并重复的. SQL中union运算操作的理解 在SQL中,对于并运算,可以使用union关键字.”

#union 的效率会高一些 对于表连接来说
#每连接一次表,匹配次数满足笛卡尔积
#但是union可以减少匹配的次数,在减少匹配次数的情况下,可以完成两个结果集的合并、拼接
# a表连接b表
# a、b、c三个表各有10条数据,则连接需要(笛卡尔积)10*10*10=1000次
# 使用union连接三个表:a连接b:10*10=100  a连接c:10*10=100 总需:100+100=200次
# 1000>200次

7.2 union如何使用?

select ename,job from emp where job="manager"
union
select ename,job from emp where job="salesman"

7.3 union使用时的注意事项

select ename,job from emp where job="manager"
union
select ename from emp where job="salesman"
==>
ERROR 1222 (21000): The used SELECT statements have a different number of columns
#以上语句是错误的,因为union在合并结果集的时候,要求两个查询的结果必须列数相同

select ename,job from emp where job="manager"
union
select ename,sal from emp where job="salesman"
==>
+--------+---------+
| ename  | job     |
+--------+---------+
| JONES  | MANAGER |
| BLAKE  | MANAGER |
| CLARK  | MANAGER |
| ALLEN  | 1600    |
| WARD   | 1250    |
| MARTIN | 1250    |
| TURNER | 1500    |
+--------+---------+
7 rows in set (0.01 sec)
#以上语句在mysql中可以执行,但是在oracle中会报错,因为oracle语法更加严格。

8 limit (非常重要)

8.1 limit是什么?

limit 是将查询结果集的一部分显示出来,通常使用在分页查询之中。

分页是为了提高用户的体验。

8.2 limit的用法

limit 跟在语句最后面

limit 有两个参数,一个是其实的下标,第二个是要取的长度

当limit 后面只跟一个参数时,默认是将要取的长度。

注意:起始下标从0开始,且在mysql中,limit在order by之后执行

select
	ename,sal
from
	emp
order by
	sal desc
limit 0,5;

+-------+---------+
| ename | sal     |
+-------+---------+
| KING  | 5000.00 |
| SCOTT | 3000.00 |
| FORD  | 3000.00 |
| JONES | 2975.00 |
| BLAKE | 2850.00 |
+-------+---------+
5 rows in set (0.00 sec)

#在mysql中,limit在order by之后执行

在mysql中,limit在order by之后执行

8.3 limit 的起始位置

# limit的起始位置跟数组的起始位置是一样的
如:
arr[]={1,2,3,4,5,6,7,8,9}
limit 5,2 取出的将会是{6,7}
下标默认是从0开始的
limit 0,2 取出的是 {1,2}
limit 1,2 取出的是 {2,3}

8.4 limit 的第二个参数

案例:

取出工资排名是在【5-9】名的员工

#
select ename as '员工名称',sal as '工资'
from emp 
order by sal desc
limit 4,5;
#第一个参数是从第5名开始的,由于下标是从0开始,所以第一个参数是5-1=4
#注意:是第5到9名员工,实际上是 [5,6,7,8,9]  结果中是包含了5的
#所以第二个参数是 (9-5)+1=5条记录

8.5 分页的原理

#需求:需要我们每页显示3条数据
#先确定第二条数据,第二条数据是页面大小,也就是返回的数据条数,所以是3 固定不变

第一页: limit 0,3  [0,1,2]
第二页: limit 3,3	[3,4,5]
第三页: limit 6,3	[6,7,8]
第四页: limit 9,3	[9,10,11]
....

指定页面大小为pageSize

指定当前页码为pageNum

那么可以得出

第pageNum页: limit pageSize(pageNum-1),pageSize*

//模拟后台管理的分页   
@Controller
public static void main(String[] args){
    int pageNo=5;
    int pageSize=10;
    int startIndex = (pageN0 -1 ) * pageSize;
    
    String sql="select ename from emp order by ename desc limit"+startIndex+','+pageSize+";"
    //...然后使用SQL
}

9 DQL的总结

select
	...
from
	...
where
	...
group by
	...
having
	...
order by
	...
limit
	...

你可能感兴趣的:(SQL,mysql,学习,数据库)