blog

vuePress-theme-reco blog    2019 - 2020
blog blog

Choose mode

  • dark
  • auto
  • light
Home
Category
  • Linux
  • java
  • java 后端
  • typora
  • vue
  • java 基础
  • 编程方法
  • Mysql
Tag
TimeLine
在线工具
  • PDF 转换器
  • JSON 编辑器
  • MD 表格生成器
  • CRON 表达式
  • 代码格式化
  • 公式编辑器
  • 二维码生成器
  • 在线编码转换
  • YAML <-> Properties
  • 在线 Web 练习
Contact
  • GitHub
  • 简书
  • CSDN
  • 博客圆
  • WeChat
author-avatar

blog

23

文章

16

标签

Home
Category
  • Linux
  • java
  • java 后端
  • typora
  • vue
  • java 基础
  • 编程方法
  • Mysql
Tag
TimeLine
在线工具
  • PDF 转换器
  • JSON 编辑器
  • MD 表格生成器
  • CRON 表达式
  • 代码格式化
  • 公式编辑器
  • 二维码生成器
  • 在线编码转换
  • YAML <-> Properties
  • 在线 Web 练习
Contact
  • GitHub
  • 简书
  • CSDN
  • 博客圆
  • WeChat
  • Mysql

    • 数据库和SQL概述
      • 数据库的好处
      • 数据库的概念
      • SQL语言
    • mysql的安装与使用
      • mysql的安装
      • 启动和停止MySQL服务
      • MySQL服务端的登录和退出
      • MySql数据库的使用
      • 基本命令
    • 基本查询
      • SELECT 语句
      • 查询时给列的别名
      • 查询去重
      • +号的作用
      • concat函数
      • IFNULL函数
      • ISNULL函数
      • NULLIF函数
    • 条件查询
      • 案例
    • 排序查询
      • 语法
      • 特点
      • 案例
    • 单行函数
      • 字符函数
      • 数学函数
      • 日期函数
      • 其他函数
      • 流程控制函数
    • 分组函数
      • SUM(expression)
      • AVG(expression)
      • MAX(expression)
      • MIN(expression)
      • COUNT(expression)
      • 参数支持哪些类型
      • 特点
      • 案例
    • 分组查询
      • 语法
      • 特点
      • 问题
      • 案例
    • 连接查询
      • 含义
      • 笛卡尔乘积现象
      • 分类
      • 等值连接
      • 非等值连接
      • 自连接
      • SQL99语法-内连接
      • SQL99语法-外连接
    • 子查询
      • 含义
      • 分类
      • 示例
    • 分页查询
      • 应用场景
      • 语法
      • 分页公式
      • 案例
      • 语法
      • 意义
      • 特点
      • 案例
    • DML数据处理之增删改
      • INSERT
      • UPDATE
      • DELETE
      • TRUNCATE
      • DELETE和TRUNCATE的区别
    • DDL数据库定义语言
      • 库的管理
      • 表的管理
      • MYSQL数据类型
      • 常见的约束
      • 自增长列
    • MySQL事物
      • 转账案例
      • 事务的特性(ACID)
      • 事务的创建
      • 并发事务
      • 事物隔离级别
      • 案例
    • MySQL视图
      • 应用场景
      • 优点
      • 创建视图
      • 修改视图
      • 删除视图
      • 查看视图
      • 视图的更新
      • 视图和表的对比
    • 变量
      • 系统变量
      • 自定义变量
      • 局部变量
      • 案例
    • 存储过程
      • 创建语法
      • 调用语法
      • 创建带in模式参数的存储过程
      • 创建out模式参数的存储过程
      • 创建带inout模式参数的存储过程
      • 查看存储过程的信息
    • 存储函数
      • 存储过程与存储函数的区别
      • 创建语法
      • 调用语法
      • 无参有返回
      • 有参有返回
      • 查看函数
    • 流程控制结构
      • 分支结构
      • 循环结构

Mysql

vuePress-theme-reco blog    2019 - 2020

Mysql


blog 2019-11-22 Mysql

# MySql教程

# 数据库和SQL概述

# 数据库的好处

  • 实现数据持久化
  • 使用完整的管理系统统一管理,易于查询

# 数据库的概念

DB DBMS SQL 数据库(database):存储数据的“仓库”。它保存了一系列有组织的数据。 数据库管理系统(Database Management System)。数据库是通过DBMS创 建和操作的容器 结构化查询语言(Structure Query Language):专门用来与数据库通信的语 言

常见的数据库管理系统:MySQL、Oracle、DB2、SqlServe

# SQL语言

数据库是不认识JAVA语言的,但是我们同样要与数据库交互,这时需要使用到数据库认识的语言SQL语句,它是数据库的代码。

结构化查询语言(Structured Query Language)简称SQL,是一种数据库查询和程序设计语言 ,用于存取数据以及查询、更新和管理关系数据库系统 。

创建数据库、创建数据表、向数据表中添加一条条数据信息均需要使用SQL语句。

# DML

DML(Data Manipulation Language):数据操纵语句,用于添加、删除、修改、查询数据库记录,并检查数据完整性。包括如下SQL语句等:

  • INSERT:添加数据到数据库中
  • UPDATE:修改数据库中的数据
  • DELETE:删除数据库中的数据
  • SELECT:选择(查询)数据
    • SELECT是SQL语言的基础,最为重要。

# DDL

DDL(Data Definition Language):数据定义语句,用于库和表的创建、修改、删除。包括如下SQL语句等:

  • CREATE TABLE:创建数据库表
  • ALTER TABLE:更改表结构、添加、删除、修改列长度
  • DROP TABLE:删除表
  • CREATE INDEX:在表上建立索引
  • DROP INDEX:删除索引

# DCL

DCL(Data Control Language):数据控制语句,用于定义用户的访问权限和安全级别包括如下SQL语句等:

  • GRANT:授予访问权限
  • REVOKE:撤销访问权限
  • COMMIT:提交事务处理
  • ROLLBACK:事务处理回退
  • SAVEPOINT:设置保存点
  • LOCK:对数据库的特定部分进行锁定

# DQL

数据查询语言:简称DQL(Data Query Language),用来查询数据库中表的记录。关键字:select,from,where等

# mysql的安装与使用

# mysql的安装

  • Windows
  • Linux

# 启动和停止MySQL服务

  • Windows

    方式一:通过计算机管理方式 右击计算机—管理—服务—启动或停止MySQL服务 方式二:通过命令行方式 启动:net start mysql服务名 停止:net stop mysql服务名

  • Linux

    启动:service mysql(mysqld) start

    停止:service mysql(mysqld) stop

    状态:service mysql(mysqld) status

# MySQL服务端的登录和退出

  • 登录

    mysql –u用户名 –p密码

  • 远程登录

    mysql -h 主机名 -P 端口号 -u root -proot

  • 退出

    exit(quit)

# MySql数据库的使用

  • 不区分大小写(Linux上区分,可以通过配置文件修改)
  • 每句话用;或\g结尾
  • 各子句一般分行写
  • 关键字不能缩写也不能分行
  • 用缩进提高语句的可读性

# 基本命令

查看 mysql 中有哪些个数据库:

show databases;
1

使用一个数据库:

use 数据库名称;
1

查看当前使用数据库中的表:

show tables;
1

查看指定数据库中的表:

show tables from 库名;
1

新建一个数据库:

#创建数据库 数据库中数据的编码采用的是安装数据库时指定的默认编码 utf8
CREATE DATABASE 数据库名;

#创建数据库 并指定数据库中数据的编码
CREATE DATABASE 数据库名 CHARACTER SET utf8;
1
2
3
4
5

查看当前选择的数据库:

select database();
1

创建表:

create table stuinfo(
    id int,
    name varchar(20));
1
2
3

查看表结构:

desc 表名;
1

查看表中的所有记录:

select * from 表名;
1

向表中插入记录:

insert into 表名(列名1,列名,...,列名n) values(列1值,列2值,...,列n值);
1

注意:插入 varchar 或 date 型的数据要用 单引号 引起来

# 基本查询

SQL文件

# SELECT 语句

SELECT *****|{[DISTINCT] column|expression [alias],...}FROM table

  • SELECT 标识选择哪些列。
  • FROM 标识从哪个表中选

语法:

select 查询列表 from 表名;
1

特点:

  1. 查询列表可以是:表中的字段、常量值、表达式、函数
  2. 查询的结果是一个虚拟的表格

查询全部列:

SELECT *FROM 表名;
1

查询特定的列:

SELECT id, name FROM student;
1

查询表达式:

 SELECT 100%98;
1

查询函数:

select now();
1

# 查询时给列的别名

别名使用双引号,以便在别名中包含空格或特殊的字符

①便于理解 ②如果要查询的字段有重名的情况,使用别名可以区分开来

  1. 使用as

    SELECT 列名1 AS "列1别名",列名2 AS "列2别名" FROM 表名;
    
    1

    案例:

    SELECT 100%98 AS "结果";
    SELECT last_name AS "姓",first_name AS "名" FROM employees;
    
    1
    2
  2. 使用空格

    SELECT 列名1 "列1别名",列名2 "列2别名" FROM 表名;
    
    1

    案例:

    # 查询salary,显示结果为 out put
    SELECT salary AS "out put" FROM employees;
    
    1
    2

# 查询去重

select distinct 字段名 from 表名;
# 案例:查询员工表中涉及到的所有的部门编号
SELECT DISTINCT department_id FROM employees;
1
2
3

# +号的作用

java中的+号: ①运算符,两个操作数都为数值型 ②连接符,只要有一个操作数为字符串

mysql中的+号: 仅仅只有一个功能:运算符

# 直接运算
select 数值+数值;

# 先试图将字符转换成数值,如果转换成功,则继续运算;否则转换成0,再做运算
select 字符+数值;

# 结果都为null
select null+值;
1
2
3
4
5
6
7
8

# concat函数

功能:拼接字符,相当于java中的 "1"+"23"="123"

select concat(字符1,字符2,字符3,...);
1

# IFNULL函数

功能:判断某字段或表达式是否为null,如果为null 返回指定的值,否则返回原本的值

# 如果commission_pct列为空则返回0
select ifnull(1,0) 返回1
select ifnull(null,0) 返回0
1
2
3

# ISNULL函数

功能:判断某字段或表达式是否为null,如果是,则返回1,否则返回0

select isnull(1) 返回0
select isnull(1/0) 返回1
1
2

# NULLIF函数

NULLIF(expr1,expr2)  的用法: 
如果expr1 == expr2 则 NULL
否则 为 expr1
SELECT NULLIF(1,1) 返回NULL
SELECT NULLIF(2,1) 返回 2
1
2
3
4
5

# 条件查询

过滤:使用WHERE 子句,将不满足条件的行过滤掉。

语法:

select 查询列表 from 表名 where 筛选条件;
1
比较运算符 >、< 、>=、<=、= 、<> 大于、小于、大于等于、小于等于、等于、不等于
BETWEEN ...AND... 显示在某一区间的值(含头含尾)
IN(set) 显示在in列表中的值,例:in(100,200)
LIKE 通配符 模糊查询,Like语句中有两个通配符:% 用来匹配多个字符;例first_name like ‘a%’;_ 用来匹配一个字符。例first_name like ‘a_’;
IS NULL 判断是否为空is null; 判断为空;is not null; 判断不为空
逻辑运算符 and (&&) 多个条件同时成立
or (||) 多个条件任一成立
not (!) 不成立,例:where not(salary>100);

# 案例

# 按条件表达式筛选

  1. 查询工资>12000的员工信息

    SELECT 
        *
    FROM
        employees
    WHERE
        salary>12000;
    
    1
    2
    3
    4
    5
    6
  2. 查询部门编号不等于90号的员工名和部门编号

    SELECT 
        last_name,
        department_id
    FROM
        employees
    WHERE
        department_id<>90;
    
    1
    2
    3
    4
    5
    6
    7

# 按逻辑表达式筛选

  1. 查询工资在10000到20000之间的员工名、工资以及奖金

    SELECT
        last_name,
        salary,
        commission_pct
    FROM
        employees
    WHERE
        salary>=10000 AND salary<=20000;
    
    1
    2
    3
    4
    5
    6
    7
    8

    或者

    SELECT
        last_name,
        salary,
        commission_pct 
    FROM
        employees 
    WHERE
        salary BETWEEN 10000 
        AND 20000;
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
  2. 查询部门编号不是在90到110之间,或者工资高于15000的员工信息

    SELECT
        * 
    FROM
        employees 
    WHERE
        NOT ( department_id >= 90 AND department_id <= 110 ) 
        OR salary > 15000;
    
    1
    2
    3
    4
    5
    6
    7

    或者

    SELECT
        * 
    FROM
        employees 
    WHERE
        NOT ( department_id BETWEEN 90 AND 110 ) 
        OR salary > 15000;
    
    1
    2
    3
    4
    5
    6
    7

# 模糊查询

  1. 查询员工名中包含字符a的员工信息

    select 
        *
    from
        employees
    where
        last_name like '%a%';
    
    1
    2
    3
    4
    5
    6
  2. 查询员工名中第三个字符为e,第五个字符为a的员工名和工资

    select
        last_name,
        salary
    FROM
        employees
    WHERE
        last_name LIKE '__e_a%';
    
    1
    2
    3
    4
    5
    6
    7
  3. 查询员工名中第二个字符为_的员工名

    ESCAPE:定义转义标识

    SELECT
        last_name
    FROM
        employees
    WHERE
        last_name LIKE '_$_%' ESCAPE '$';
    
    1
    2
    3
    4
    5
    6

# IN

含义:判断某字段的值是否属于in列表中的某一项 特点:

  1. 使用in提高语句简洁度
  2. 列表的值类型必须一致或兼容
  3. 列表中不支持通配符

查询员工的工种编号是 IT_PROG、AD_VP、AD_PRES中的一个员工名和工种编号

SELECT
    last_name,
    job_id
FROM
    employees
WHERE
    job_id IN( 'IT_PROT' ,'AD_VP','AD_PRES');
1
2
3
4
5
6
7

或者

SELECT
    last_name,
    job_id
FROM
    employees
WHERE
    job_id = 'IT_PROT' OR job_id = 'AD_VP' OR JOB_ID ='AD_PRES';
1
2
3
4
5
6
7

# IS NULL

=或<>不能用于判断null值 is null或is not null 可以判断null值

  1. 查询没有奖金的员工名和奖金率

    SELECT
        last_name,
        commission_pct
    FROM
        employees
    WHERE
        commission_pct IS NULL;
    
    1
    2
    3
    4
    5
    6
    7
  2. 查询有奖金的员工名和奖金率

    SELECT
        last_name,
        commission_pct
    FROM
        employees
    WHERE
        commission_pct IS NOT NULL;
    
    1
    2
    3
    4
    5
    6
    7

# 安全等于 <=>

  1. 查询没有奖金的员工名和奖金率

    SELECT
        last_name,
        commission_pct
    FROM
        employees
    WHERE
        commission_pct <=>NULL;
    
    1
    2
    3
    4
    5
    6
    7
  2. 查询工资为12000的员工信息

    SELECT
        last_name,
        salary
    FROM
        employees
    
    WHERE 
        salary <=> 12000;
    
    1
    2
    3
    4
    5
    6
    7
    8

IS NULL:仅仅可以判断NULL值,可读性较高,建议使用 <=> :既可以判断NULL值,又可以判断普通的数值,可读性较低

错误的写法:

SELECT
    last_name,
    commission_pct
FROM
    employees

WHERE 
    salary IS 12000;
1
2
3
4
5
6
7
8

# 排序查询

ORDER BY

  • 使用 ORDER BY 子句排序
    • ASC(ascend): 升序
    • DESC(descend): 降序

# 语法

select 查询列表

from 表名

【where  筛选条件】

order by 排序的字段或表达式;

1
2
3
4
5
6
7
8

# 特点

  1. asc代表的是升序,可以省略
  2. order by子句可以支持 单个字段、别名、表达式、函数、多个字段
  3. order by子句在查询语句的最后面,除了limit子句

# 案例

# 单个字段排序

  1. 查询员工表按薪水降序

    SELECT * FROM employees ORDER BY salary DESC;
    
    1
  2. 查询部门编号>=90的员工信息,并按员工编号降序

    SELECT *
    FROM employees
    WHERE department_id>=90
    ORDER BY employee_id DESC;
    
    1
    2
    3
    4

# 按表达式排序

  1. 查询员工信息 按年薪降序

    SELECT *,salary*12*(1+IFNULL(commission_pct,0))
    FROM employees
    ORDER BY salary*12*(1+IFNULL(commission_pct,0)) DESC;
    
    1
    2
    3

# 按别名排序

  1. 查询员工信息 按年薪升序

    SELECT *,salary*12*(1+IFNULL(commission_pct,0)) 年薪
    FROM employees
    ORDER BY 年薪 ASC;
    
    1
    2
    3

# 按函数排序

  1. 查询员工名,并且按名字的长度降序

    SELECT LENGTH(last_name),last_name 
    FROM employees
    ORDER BY LENGTH(last_name) DESC;
    
    1
    2
    3

# 多个字段排序

  1. 查询员工信息,要求先按工资降序,再按employee_id升序

    SELECT *
    FROM employees
    ORDER BY salary DESC,employee_id ASC;
    
    1
    2
    3

# 单行函数

# 字符函数

# length(s)

获取参数值的字符个数

SELECT LENGTH('john');
SELECT LENGTH('张三丰hahaha');
1
2

# CONCAT(s1,s2...sn)

拼接字符串

SELECT CONCAT(last_name,'_',first_name) 姓名 FROM employees;
1

# UPPER(s)

将字符串转换为大写

SELECT UPPER('john');
1

# LOWER(s)

将字符串转换为小写

SELECT LOWER('joHn');
# 示例:将姓变大写,名变小写,然后拼接
SELECT CONCAT(UPPER(last_name),LOWER(first_name))  姓名 FROM employees;
1
2
3

# SUBSTR(s, start, length)、SUBSTRING()

SUBSTR(s, start, length):从字符串 s 的 start 位置截取长度为 length 的子字符串

截取字符串(注意:索引从1开始)

SELECT SUBSTR('李莫愁爱上了陆展元',7)  out_put;
SELECT SUBSTRING('李莫愁爱上了陆展元',7)  out_put;
SELECT SUBSTR('李莫愁爱上了陆展元',1,3)  out_put;
# 案例:姓名中首字符大写,其他字符小写然后用_拼接,显示出来

SELECT CONCAT(UPPER(SUBSTR(last_name,1,1)),'_',LOWER(SUBSTR(last_name,2)))  out_put
FROM employees;
1
2
3
4
5
6
7

# INSTR(s,s)

返回子串第一次出现的索引,如果找不到返回0

SELECT INSTR('杨不殷六侠悔爱上了殷六侠','殷八侠') AS out_put;
1

# TRIM(s)

LTRIM(s):去掉字符串 s 开始处的空格

RTRIM(s):去掉字符串 s 结尾处的空格

去掉字符串开始和结尾处的空格

SELECT LENGTH(TRIM('    张翠山    ')) AS out_put;
1

# LPAD(s1,len,s2)

在字符串 s1 的开始处填充字符串 s2,使字符串长度达到 len

如果长度小于原字符串,则只取前len个字符

SELECT LPAD('殷素素',5,'*') AS out_put;    --> **殷素素
SELECT LPAD('殷素素',2,'*') AS out_put;    --> 殷素
1
2

# RPAD(s1,len,s2)

在字符串 s1 的结尾处添加字符串 s2,使字符串的长度达到 len

如果长度小于原字符串,则只取前len个字符

SELECT RPAD('殷素素',5,'*') AS out_put;    --> 殷素素**
SELECT RPAD('殷素素',2,'*') AS out_put;    --> 殷素
1
2

# REPLACE(s,s1,s2)

用字符串 s2 替代字符串 s 中的字符串 s1

SELECT REPLACE('张无忌爱上了周芷若','周芷若','赵敏') AS out_put;    -->张无忌爱上了赵敏
1

# 数学函数

# ROUND(x)

ROUND(x,d):保留d位小数

四舍五入

SELECT ROUND(-1.55);    --> -2
SELECT ROUND(1.567,2);    --> 1.57
1
2

# CEIL(x)

向上取整

SELECT CEIL(-1.02);    --> -1
SELECT CEIL(1.02);    --> 2
1
2

# FLOOR(x)

向下取整

SELECT FLOOR(-1.58);    --> -2
SELECT FLOOR(1.58);    --> 1
1
2

# TRUNCATE(x,y)

返回数值 x 保留到小数点后 y 位的值(与 ROUND 最大的区别是不会进行四舍五入)

SELECT TRUNCATE(1.567,2);    --> 1.56
1

# MOD(x,y)

返回 x 除以 y 以后的余数

SELECT MOD(10,-3);    --> 1
1

等价于

SELECT 10%-3;    --> 1
1

余数的符号取决于被除数的符号

SELECT MOD(-10,3);    --> -1
SELECT MOD(-10,-3);    --> -1
1
2

# 日期函数

# NOW()

返回当前系统时间(注:日期+时间)

SELECT NOW();    --> 2019-10-05 09:56:57
1

# CURDATE()

返回当前系统日期,不包含时间

SELECT CURDATE();    --> 2019-10-05
1

# CURTIME()

SELECT CURTIME();    -->09:56:57
1

# YEAR(d)

返回年份

SELECT YEAR(NOW());    -->2019
SELECT YEAR('1998-1-1');    -->1998
SELECT  YEAR(hiredate) 入职时间 FROM employees;
1
2
3

# MONTH(d)

返回日期d中的月份值,1 到 12

SELECT MONTH(NOW());    --> 10
1

# MONTHNAME(d)

返回日期当中的月份名称,如 November

SELECT MONTHNAME(NOW());    --> October
1

# STR_TO_DATE(s, f)

将字符通过指定的格式转换成日期

SELECT STR_TO_DATE('1998-3-2','%Y-%c-%d') AS out_put;    --> 1998-03-02
1

查询入职日期为1992-4-3的员工信息

SELECT * FROM employees WHERE hiredate = '1992-4-3';

# 或者

SELECT * FROM employees WHERE hiredate = STR_TO_DATE('4-3 1992','%c-%d %Y');
1
2
3
4
5

# DATE_FORMAT(d,f)

将日期通过指定的格式转换成字符

SELECT DATE_FORMAT(NOW(),'%Y年%m月%d日') AS out_put;    --> 2019年10月05日
1

查询有奖金的员工名和入职日期(xx月/xx日 xx年)

SELECT last_name,DATE_FORMAT(hiredate,'%m月/%d日 %y年') 入职日期
FROM employees
WHERE commission_pct IS NOT NULL;
1
2
3

# 其他函数

# VERSION()

返回数据库的版本号

SELECT VERSION();
1

# DATABASE()

返回当前数据库名

SELECT DATABASE();
1

# USER()

返回当前用户

SELECT USER();
1

# 流程控制函数

# IF(expr,v1,v2)

如果表达式 expr 成立,返回结果 v1;否则,返回结果 v2。

SELECT IF(1 > 0,'正确','错误');    --> 正确
SELECT
    last_name,
    commission_pct,
IF
    ( commission_pct IS NULL, '没奖金,呵呵', '有奖金,嘻嘻' ) 备注 
FROM
    employees;
1
2
3
4
5
6
7
8

# CASE

CASE expression
    WHEN condition1 THEN result1
    WHEN condition2 THEN result2
   ...
    WHEN conditionN THEN resultN
    ELSE result
END
1
2
3
4
5
6
7

CASE 表示函数开始,END 表示函数结束。如果 condition1 成立,则返回 result1, 如果 condition2 成立,则返回 result2,当全部不成立则返回 result,而当有一个成立之后,后面的就不执行了。

case 要判断的字段或表达式
when 常量1 then 要显示的值1或语句1;
when 常量2 then 要显示的值2或语句2;
...
else 要显示的值n或语句n;
end
SELECT CASE 
  WHEN 1 > 0
  THEN '1 > 0'
  WHEN 2 > 0
  THEN '2 > 0'
  ELSE '3 > 0'
  END
  
--> 1 > 0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

查询员工的工资,要求

  1. 部门号=30,显示的工资为1.1倍
  2. 部门号=40,显示的工资为1.2倍
  3. 部门号=50,显示的工资为1.3倍
  4. 其他部门,显示的工资为原工资
SELECT salary 原始工资,department_id,
CASE department_id
WHEN 30 THEN salary*1.1
WHEN 40 THEN salary*1.2
WHEN 50 THEN salary*1.3
ELSE salary
END AS 新工资
FROM employees;
1
2
3
4
5
6
7
8

查询员工的工资的情况

  1. 如果工资>20000,显示A级别
  2. 如果工资>15000,显示B级别
  3. 如果工资>10000,显示C级别
  4. 否则,显示D级别
SELECT salary,
CASE 
WHEN salary>20000 THEN 'A'
WHEN salary>15000 THEN 'B'
WHEN salary>10000 THEN 'C'
ELSE 'D'
END AS 工资级别
FROM employees;
1
2
3
4
5
6
7
8

# 分组函数

用作统计使用,又称为聚合函数或统计函数或组函数

# SUM(expression)

求和

SELECT SUM(salary) FROM employees;
1

# AVG(expression)

平均值

SELECT AVG(salary) FROM employees;
1

# MAX(expression)

最大值

SELECT MAX(salary) FROM employees;
1

# MIN(expression)

最小值

SELECT MIN(salary) FROM employees;
1

# COUNT(expression)

计算个数

SELECT COUNT(salary) FROM employees;
SELECT COUNT(*) FROM employees;
SELECT COUNT(1) FROM employees;
1
2
3

效率: MYISAM存储引擎下 ,COUNT()的效率高 INNODB存储引擎下,COUNT()和COUNT(1)的效率差不多,比COUNT(字段)要高一些


SELECT
    SUM( salary ) 和,
    AVG( salary ) 平均,
    MAX( salary ) 最高,
    MIN( salary ) 最低,
    COUNT( salary ) 个数 
FROM
    employees;
1
2
3
4
5
6
7
8

平均工资保留两位小数(四舍五入)

SELECT
    SUM( salary ) 和,
    ROUND( AVG( salary ), 2 ) 平均,
    MAX( salary ) 最高,
    MIN( salary ) 最低,
    COUNT( salary ) 个数 
FROM
    employees;
1
2
3
4
5
6
7
8

# 参数支持哪些类型

# 字符

先试图将字符转换成数值,如果转换成功,则继续运算;否则转换成0,再做运算

SELECT
    SUM( last_name ),
    AVG( last_name ) 
FROM
    employees;

--> 0,0
1
2
3
4
5
6
7

与排序差不多

SELECT
    MAX( last_name ),
    MIN( last_name ) 
FROM
    employees;
1
2
3
4
5

# 日期

SELECT SUM(hiredate) ,AVG(hiredate) FROM employees;
SELECT MAX(hiredate),MIN(hiredate) FROM employees;
1
2

# 特点

  1. sum、avg一般用于处理数值型
  2. max、min、count可以处理任何类型
  3. 以上分组函数都忽略null值
  4. 可以和distinct搭配实现去重的运算
  5. 一般使用count(*)用作统计行数
  6. 和分组函数一同查询的字段要求是group by后的字段

# 案例

  1. 查询员工表中的最大入职时间和最小入职时间的相差天数 (DIFFRENCE)

    SELECT
        MAX( hiredate ) 最大,
        MIN( hiredate ) 最小,
        (MAX( hiredate )- MIN( hiredate ))/ 1000 / 3600 / 24 DIFFRENCE 
    FROM
        employees;
    
    1
    2
    3
    4
    5
    6

    使用DATEDIFF(d1,d2)函数,计算日期 d1~d2 之间相隔的天数

    SELECT DATEDIFF(MAX(hiredate),MIN(hiredate)) DIFFRENCE;
    
    1
  2. 查询部门编号为90的员工个数

    SELECT
        COUNT(*) 
    FROM
        employees 
    WHERE
        department_id = 90;
    
    1
    2
    3
    4
    5
    6

# 分组查询

# 语法

select 分组函数,分组后的字段
from 表
【where 筛选条件】
 group by 分组的字段
【having 分组后的筛选】
【order by 排序列表】
1
2
3
4
5
6

# 特点

  1. 和分组函数一同查询的字段必须是group by后出现的字段
  2. 筛选分为两类:分组前筛选和分组后筛选
筛选 针对的表 连接的关键字
分组前筛选 原始表 where
分组后筛选 group by后的结果集 having

# 问题

  1. 分组函数做筛选能不能放在where后面

    不能,原表中没有分组后的数据

# 案例

# 简单的分组

  1. 查询每个工种的员工平均工资

    SELECT AVG(salary),job_id
    FROM employees
    GROUP BY job_id;
    
    1
    2
    3
  2. 查询每个位置的部门个数

    SELECT COUNT(*),location_id
    FROM departments
    GROUP BY location_id;
    
    1
    2
    3

# 分组前筛选

  1. 查询邮箱中包含a字符的 每个部门的最高工资

    SELECT MAX(salary),department_id
    FROM employees
    WHERE email LIKE '%a%'
    GROUP BY department_id;
    
    1
    2
    3
    4
  2. 查询有奖金的每个领导手下员工的平均工资

    SELECT
        AVG( salary ),
        manager_id 
    FROM
        employees 
    WHERE
        commission_pct IS NOT NULL 
    GROUP BY
        manager_id;
    
    1
    2
    3
    4
    5
    6
    7
    8
    9

# 分组后筛选

  1. 查询哪个部门的员工个数>5

    ① 首先查询每个部门的员工个数

    SELECT COUNT(*),department_id
    FROM employees
    GROUP BY department_id;
    
    1
    2
    3

    ② 筛选刚才①结果

    SELECT COUNT(*),department_id
    FROM employees
    
    GROUP BY department_id
    
    HAVING COUNT(*)>5;
    
    1
    2
    3
    4
    5
    6
  2. 每个工种有奖金的员工的最高工资>12000的工种编号和最高工资

    SELECT
        MAX( salary ) 最高工资,
        job_id 工种编号 
    FROM
        employees 
    WHERE
        commission_pct IS NOT NULL 
    GROUP BY
        job_id 
    HAVING
        MAX( salary )> 12000;
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
  3. 领导编号>102的每个领导手下的最低工资大于5000的领导编号和最低工资

    SELECT
        manager_id 领导编号,
        MIN( salary ) 最低工资 
    FROM
        employees 
    WHERE
        manager_id > 102 
    GROUP BY
        manager_id 
    HAVING
        MIN( salary )> 5000
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11

# 添加排序

  1. 每个工种有奖金的员工的最高工资>6000的工种编号和最高工资,按最高工资升序

    SELECT
        job_id 工种编号,
        MAX( salary ) 最高工资 
    FROM
        employees 
    WHERE
        commission_pct IS NOT NULL 
    GROUP BY
        job_id 
    HAVING
        MAX( salary )> 6000 
    ORDER BY
        MAX( salary );
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13

# 按多个字段分组

  1. 查询每个工种每个部门的最低工资,并按最低工资降序

    SELECT
        MIN( salary ) 最低工资,
        department_id 部门,
        job_id 工种 
    FROM
        employees 
    GROUP BY
        department_id,
        job_id 
    ORDER BY
        MIN( salary ) DESC;
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11

# 连接查询

# 含义

又称多表查询,当查询的字段来自于多个表时,就会用到连接查询

select 字段1,字段2

from 表1,表2,...;
1
2
3

# 笛卡尔乘积现象

当查询多个表时,没有添加有效的连接条件,导致多个表所有行实现完全连接

表1 有m行,表2有n行,结果=m*n行

发生原因:没有有效的连接条件 如何避免:添加有效的连接条件

# 分类

按年代分类: sql92标准:仅仅支持内连接 sql99标准【推荐】:支持内连接+外连接(左外和右外)+交叉连接

按功能分类:
    内连接:
        等值连接
        非等值连接
        自连接
    外连接:
        左外连接
        右外连接
        全外连接(mysql不支持)

    交叉连接
1
2
3
4
5
6
7
8
9
10
11

# 等值连接

  1. 多表等值连接的结果为多表的交集部分
  2. n表连接,至少需要n-1个连接条件
  3. 表的顺序没有要求
  4. 一般需要为表起别名
  5. 可以搭配前面介绍的所有子句使用,比如排序、分组、筛选

# 案例1

  1. 查询女神名和对应的男神名

    SELECT NAME,boyName 
    FROM
        boys,
        beauty 
    WHERE
        beauty.boyfriend_id = boys.id;
    
    1
    2
    3
    4
    5
    6
  2. 查询员工名和对应的部门名

    SELECT
        last_name,
        department_name 
    FROM
        employees,
        departments 
    WHERE
        employees.`department_id` = departments.`department_id`;
    
    1
    2
    3
    4
    5
    6
    7
    8

# 为表起别名

  1. 提高语句的简洁度
  2. 区分多个重名的字段

注意:如果为表起了别名,则查询的字段就不能使用原来的表名去限定

  1. 查询员工名、工种号、工种名

    SELECT
        e.last_name,
        e.job_id,
        j.job_title 
    FROM
        employees e,
        jobs j 
    WHERE
        e.`job_id` = j.`job_id`;
    
    1
    2
    3
    4
    5
    6
    7
    8
    9

# 加筛选条件

  1. 查询有奖金的员工名、部门名

    SELECT
        last_name,
        department_name,
        commission_pct 
    FROM
        employees e,
        departments d 
    WHERE
        e.`department_id` = d.`department_id` 
        AND e.`commission_pct` IS NOT NULL;
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
  2. 查询城市名中第二个字符为o的部门名和城市名

    SELECT
        department_name,
        city 
    FROM
        departments d,
        locations l 
    WHERE
        d.`location_id` = l.`location_id` 
        AND city LIKE '_o%';
    
    1
    2
    3
    4
    5
    6
    7
    8
    9

# 加分组

  1. 查询每个城市的部门个数

    SELECT
        COUNT(*) 个数,
        city 
    FROM
        departments d,
        locations l 
    WHERE
        d.`location_id` = l.`location_id` 
    GROUP BY
        city;
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
  2. 查询有奖金的每个部门的部门名和部门的领导编号和该部门的最低工资

    SELECT
        department_name,
        d.`manager_id`,
        MIN( salary ) 
    FROM
        departments d,
        employees e 
    WHERE
        d.`department_id` = e.`department_id` 
        AND commission_pct IS NOT NULL 
    GROUP BY
        department_name,
        d.`manager_id`;
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
  3. 查询每个工种的工种名和员工的个数,并且按员工个数降序

    SELECT
        job_title,
        COUNT(*) 
    FROM
        employees e,
        jobs j 
    WHERE
        e.`job_id` = j.`job_id` 
    GROUP BY
        job_title 
    ORDER BY
        COUNT(*) DESC;
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
  4. 查询员工名、部门名和所在的城市,并且城市名以s开头,按部门名称降序

    SELECT
        e.last_name 员工名,
        d.department_name 部门名,
        l.city 城市 
    FROM
        employees e,
        departments d,
        locations l 
    WHERE
        e.department_id = d.department_id 
        AND d.location_id = l.location_id 
        AND city LIKE 's%' 
    ORDER BY
        department_name DESC;
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14

# 非等值连接

# 语法

       select 查询列表
       from 表1 别名,表2 别名
       where 非等值的连接条件
       【and 筛选条件】
       【group by 分组字段】
       【having 分组后的筛选】
       【order by 排序字段】
1
2
3
4
5
6
7

# 案例2

  1. 查询员工的工资和工资级别

    SELECT
        e.salary 工资,
        j.grade_level 工资级别 
    FROM
        employees e,
        job_grades j 
    WHERE
        e.salary BETWEEN j.lowest_sal 
        AND j.highest_sal;
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
  2. 查询员工的工资和工资级别并筛选出级别为A的

    SELECT
        e.salary 工资,
        j.grade_level 工资级别 
    FROM
        employees e,
        job_grades j 
    WHERE
        e.salary BETWEEN j.lowest_sal 
        AND j.highest_sal 
        AND j.grade_level = 'A';
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10

# 自连接

# 语法

    select 查询列表
    from 表 别名1,表 别名2
    where 等值的连接条件
    【and 筛选条件】
    【group by 分组字段】
    【having 分组后的筛选】
    【order by 排序字段】
1
2
3
4
5
6
7
  1. 查询员工名和上级的名称

    SELECT
        e.last_name 员工名,
        m.last_name 上级名称 
    FROM
        employees e,
        employees m 
    WHERE
        e.manager_id = m.employee_id;
    
    1
    2
    3
    4
    5
    6
    7
    8

# SQL99语法-内连接

select 查询列表
from 表1 别名
【inner】 join 表2 别名 on 连接条件
where 筛选条件
group by 分组列表
having 分组后的筛选
order by 排序列表
limit 子句;
1
2
3
4
5
6
7
8

# 特点

  1. 表的顺序可以调换
  2. 内连接的结果=多表的交集
  3. n表连接至少需要n-1个连接条件
  4. 添加排序、分组、筛选
  5. inner可以省略
  6. 筛选条件放在where后面,连接条件放在on后面,提高分离性,便于阅读
  7. inner join连接和sql92语法中的等值连接效果是一样的,都是查询多表的交集

# 分类

  • 等值连接
  • 非等值连接
  • 自连接

# 内连接-等值连接

  1. 查询员工名、部门名

    SELECT
        last_name,
        department_name 
    FROM
        departments d
        INNER JOIN employees e ON e.`department_id` = d.`department_id`;
    
    1
    2
    3
    4
    5
    6
  2. 查询名字中包含e的员工名和工种名(添加筛选)

    SELECT
        last_name,
        job_title 
    FROM
        employees e
        JOIN jobs j ON e.`job_id` = j.`job_id` 
    WHERE
        e.`last_name` LIKE '%e%';
    
    1
    2
    3
    4
    5
    6
    7
    8
  3. 查询部门个数>3的城市名和部门个数,(添加分组+筛选)

    SELECT
        COUNT( department_id ) 部门个数,
        city 城市名 
    FROM
        departments d
        JOIN locations l ON d.location_id = l.location_id 
    GROUP BY
        l.city 
    HAVING
        COUNT( department_id )> 3;
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
  4. 查询哪个部门的员工个数>3的部门名和员工个数,并按个数降序(添加排序)

    SELECT
        COUNT(*) 员工个数,
        d.department_name 部门名称 
    FROM
        departments d
        JOIN employees e ON d.department_id = e.department_id 
    GROUP BY
        d.department_id 
    HAVING
        COUNT(
        *)> 3 
    ORDER BY
        1 DESC
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
  5. 查询员工名、部门名、工种名,并按部门名降序(添加三表连接)

    SELECT
        e.last_name 员工名,
        d.department_name 部门号,
        j.job_title 工种名 
    FROM
        employees e
        JOIN departments d ON e.department_id = d.department_id
        JOIN jobs j ON e.job_id = j.job_id 
    ORDER BY
        d.department_name DESC;
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10

# 内连接-非等值连接

  1. 查询员工的工资级别

    SELECT
        e.last_name 员工名,
        e.salary 薪水,
        j.grade_level 工资级别 
    FROM
        employees e
        JOIN job_grades j ON e.salary BETWEEN j.lowest_sal 
        AND j.highest_sal;
    
    1
    2
    3
    4
    5
    6
    7
    8
  2. 查询工资级别的个数>20的个数,并且按工资级别降序

    SELECT
        j.grade_level 工资级别,
        COUNT( * ) 个数 
    FROM
        employees e
        JOIN job_grades j ON e.salary BETWEEN j.lowest_sal 
        AND j.highest_sal 
    GROUP BY
        j.grade_level 
    HAVING
        COUNT( * ) > 20 
    ORDER BY
        2 DESC;
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13

# 内连接-自连接

  1. 查询员工的名字、上级的名字

    SELECT
        e.last_name 员工名,
        m.last_name 上级名 
    FROM
        employees e
        JOIN employees m ON e.manager_id = m.employee_id;
    
    1
    2
    3
    4
    5
    6
  2. 查询姓名中包含字符k的员工的名字、上级的名字

    SELECT
        e.last_name 员工名,
        m.last_name 上级名 
    FROM
        employees e
        JOIN employees m ON e.manager_id = m.employee_id 
    WHERE
        e.last_name LIKE '%k%'
    
    1
    2
    3
    4
    5
    6
    7
    8

# SQL99语法-外连接

# 语法

select 查询列表
from 表1 别名
left|right|full【outer】 join 表2 别名 on 连接条件
where 筛选条件
group by 分组列表
having 分组后的筛选
order by 排序列表
limit 子句;
1
2
3
4
5
6
7
8

# 应用场景

用于查询一个表中有,另一个表没有的记录

# 特点

  1. 外连接的查询结果为主表中的所有记录

    如果从表中有和它匹配的,则显示匹配的值

    如果从表中没有和它匹配的,则显示null

    外连接查询结果=内连接结果+主表中有而从表没有的记录

  2. 左外连接,left join 左边的是主表

  3. 右外连接,right join 右边的是主表

  4. 左外和右外交换两个表的顺序,可以实现同样的效果

  5. 全外连接=内连接的结果+表1中有但表2没有的+表2中有但表1没有的

# 左外连接

  1. 查询哪个部门没有员工

    SELECT
        d.* 
    FROM
        departments d
        LEFT JOIN employees e ON d.department_id = e.department_id 
    WHERE
        e.department_id IS NULL
    
    1
    2
    3
    4
    5
    6
    7

# 右外连接

  1. 查询哪个部门没有员工(调换位置使用RIGHT JOIN)

    SELECT
        d.* 
    FROM
        employees e
        RIGHT JOIN departments d ON d.department_id = e.department_id 
    WHERE
        e.department_id IS NULL
    
    1
    2
    3
    4
    5
    6
    7

# 全外连接

mysql不支持全外连接

 USE girls;
 SELECT b.*,bo.*
 FROM beauty b
 FULL OUTER JOIN boys bo
 ON b.`boyfriend_id` = bo.id;
1
2
3
4
5

同样boys表有的beauty没有的也是显示null

# 交叉连接

交叉联接返回左表中的所有行,左表中的每一行与右表中的所有行组合。交叉联接也称作笛卡尔积。

 SELECT b.*,bo.*
 FROM beauty b
 CROSS JOIN boys bo;
1
2
3

# 子查询

# 含义

嵌套在其他语句内部的select语句称为子查询或内查询, 外面的语句可以是insert、update、delete、select等,一般select作为外面语句较多 外面如果为select语句,则此语句称为外查询或主查询

# 分类

# 按出现位置

select后面:
        仅仅支持标量子查询
from后面:
        表子查询
where或having后面:
        标量子查询
        列子查询
        行子查询
exists后面:
        标量子查询
        列子查询
        行子查询
        表子查询
1
2
3
4
5
6
7
8
9
10
11
12
13

# 按结果集的行列

标量子查询(单行子查询):结果集为一行一列 列子查询(多行子查询):结果集为多行一列 行子查询:结果集为多行多列 表子查询:结果集为多行多列

# 示例

# where或having后面

  1. 标量子查询(单行单列)
  2. 列子查询(多行单列)
  3. 行子查询(多行多列)
# 特点

① 子查询放在小括号内 ② 子查询一般放在条件的右侧 ③ 标量子查询,一般搭配着单行操作符使用

<、>、<=、>=、=、<>

④ 列子查询,一般搭配着多行操作符使用

in、any/some、all

⑤ 子查询的执行优先于主查询执行,主查询的条件用到了子查询的结果

# 标量子查询
  1. 谁的工资比 Abel 高?

    ① 查询Abel的工资

    SELECT salary
    FROM employees
    WHERE last_name = 'Abel'
    
    1
    2
    3

    ②查询员工的信息,满足 salary>①结果

    SELECT *
    FROM employees
    WHERE salary>(
    
        SELECT salary
        FROM employees
        WHERE last_name = 'Abel'
    
    );
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
  2. 返回job_id与141号员工相同,salary比143号员工多的员工 姓名,job_id 和工资

    ① 查询141号员工的job_id

    SELECT job_id
    FROM employees
    WHERE employee_id = 141
    
    1
    2
    3

    ② 查询143号员工的salary

    SELECT salary
    FROM employees
    WHERE employee_id = 143
    
    1
    2
    3

    ③ 查询员工的姓名,job_id 和工资,要求job_id=①并且salary>②

    SELECT last_name,job_id,salary
    FROM employees
    WHERE job_id = (
        SELECT job_id
        FROM employees
        WHERE employee_id = 141
    ) AND salary>(
        SELECT salary
        FROM employees
        WHERE employee_id = 143
    
    );
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
  3. 返回公司工资最少的员工的last_name,job_id和salary

    ① 查询公司的 最低工资

    SELECT MIN(salary)
    FROM employees
    
    1
    2

    ② 查询last_name,job_id和salary,要求salary=①

    SELECT last_name,job_id,salary
    FROM employees
    WHERE salary=(
        SELECT MIN(salary)
        FROM employees
    );
    
    1
    2
    3
    4
    5
    6
  4. 查询最低工资大于50号部门最低工资的部门id和其最低工资

    ① 查询50号部门的最低工资

    SELECT  MIN(salary)
    FROM employees
    WHERE department_id = 50
    
    1
    2
    3

    ② 查询每个部门的最低工资

    SELECT MIN(salary),department_id
    FROM employees
    GROUP BY department_id
    
    1
    2
    3

    ③ 在②基础上筛选,满足min(salary)>①

    SELECT MIN(salary),department_id
    FROM employees
    GROUP BY department_id
    HAVING MIN(salary)>(
        SELECT  MIN(salary)
        FROM employees
        WHERE department_id = 50
    );
    
    1
    2
    3
    4
    5
    6
    7
    8
  5. 非法使用标量子查询

    SELECT MIN(salary),department_id FROM employees GROUP BY department_id HAVING MIN(salary)>( SELECT salary FROM employees WHERE department_id = 250 );

    这里250号部门的员工可能不止一个人,查询的是多行单列,就是列子查询了

# 列子查询
操作符 含义
IN/NOT IN 等于列表中的任意一个
ANY|SOME 和子查询返回的某一个值比较
ALL 和子查询返回的所有值比较
  1. 返回location_id是1400或1700的部门中的所有员工姓名

    ①查询location_id是1400或1700的部门编号

    SELECT DISTINCT department_id
    FROM departments
    WHERE location_id IN(1400,1700)
    
    1
    2
    3

    ② 查询员工姓名,要求部门号是①列表中的某一个

    SELECT last_name
    FROM employees
    WHERE department_id IN(
        SELECT DISTINCT department_id
        FROM departments
        WHERE location_id IN(1400,1700)
    );
    
    1
    2
    3
    4
    5
    6
    7
  2. 返回其它工种中比job_id为‘IT_PROG’工种任一工资低的员工的员工号、姓名、job_id 以及salary

    ① 查询job_id为‘IT_PROG’部门任一工资

    SELECT DISTINCT salary
    FROM employees
    WHERE job_id = 'IT_PROG';
    
    1
    2
    3

    ② 查询员工号、姓名、job_id 以及salary,salary<(①)的任意一个

    SELECT last_name,employee_id,job_id,salary
    FROM employees
    WHERE salary<ANY(
        SELECT DISTINCT salary
        FROM employees
        WHERE job_id = 'IT_PROG'
    
    ) AND job_id<>'IT_PROG';
    
    1
    2
    3
    4
    5
    6
    7
    8

    或者(小于最大值,就肯定满足任意一个)

    SELECT last_name,employee_id,job_id,salary
    FROM employees
    WHERE salary<(
        SELECT MAX(salary)
        FROM employees
        WHERE job_id = 'IT_PROG'
    
    ) AND job_id<>'IT_PROG';
    
    1
    2
    3
    4
    5
    6
    7
    8
  3. 返回其它部门中比job_id为‘IT_PROG’部门所有工资都低的员工 的员工号、姓名、job_id 以及salary

    SELECT last_name,employee_id,job_id,salary
    FROM employees
    WHERE salary<ALL(
        SELECT DISTINCT salary
        FROM employees
        WHERE job_id = 'IT_PROG'
    
    ) AND job_id<>'IT_PROG';
    
    1
    2
    3
    4
    5
    6
    7
    8

    或者(小于最小的)

    SELECT last_name,employee_id,job_id,salary
    FROM employees
    WHERE salary<(
        SELECT MIN( salary)
        FROM employees
        WHERE job_id = 'IT_PROG'
    
    ) AND job_id<>'IT_PROG';
    
    1
    2
    3
    4
    5
    6
    7
    8
# 行子查询
  1. 查询员工编号最小并且工资最高的员工信息

    SELECT * 
    FROM employees
    WHERE (employee_id,salary)=(
        SELECT MIN(employee_id),MAX(salary)
        FROM employees
    );
    
    1
    2
    3
    4
    5
    6

    或者

    SELECT *
    FROM employees
    WHERE employee_id=(
        SELECT MIN(employee_id)
        FROM employees
    )AND salary=(
        SELECT MAX(salary)
        FROM employees
    );
    
    1
    2
    3
    4
    5
    6
    7
    8
    9

# select后面

仅仅支持标量子查询

  1. 查询每个部门的员工个数

    SELECT d.*,(
        SELECT COUNT(*)
        FROM employees e
        WHERE e.department_id = d.`department_id`
     ) 个数
     FROM departments d;
    
    1
    2
    3
    4
    5
    6

    或者(使用外连接)

    SELECT
        d.*,
        count( e.employee_id ) 个数 
    FROM
        employees e
        RIGHT JOIN departments d ON e.department_id = d.department_id 
    GROUP BY
        d.department_id;
    
    1
    2
    3
    4
    5
    6
    7
    8
  2. 查询员工号=102的部门名

    SELECT department_id,( 
        SELECT department_name 
        FROM departments d 
        WHERE department_id = e.department_id 
    ) 
    FROM
        employees e 
    WHERE
        e.employee_id = 102;
    
    1
    2
    3
    4
    5
    6
    7
    8
    9

    或者

        SELECT department_name,e.department_id
        FROM departments d
        INNER JOIN employees e
        ON d.department_id=e.department_id
        WHERE e.employee_id=102;
    
    1
    2
    3
    4
    5

    # from后面

    将子查询结果充当一张表,要求必须起别名

    1. 查询每个部门的平均工资的工资等级

      SELECT  ag_dep.*,g.`grade_level`
      FROM (
          SELECT AVG(salary) ag,department_id
          FROM employees
          GROUP BY department_id
      ) ag_dep
      INNER JOIN job_grades g
      ON ag_dep.ag BETWEEN lowest_sal AND highest_sal;
      
      1
      2
      3
      4
      5
      6
      7
      8

# exists后面(相关子查询)

判断子查询是否存在结果,存在返回1,不能存在返回0,可用于筛选判断

  1. 查询有员工的部门名

    SELECT department_name
    FROM departments d
    WHERE EXISTS(
        SELECT *
        FROM employees e
        WHERE d.`department_id`=e.`department_id`
    );
    
    1
    2
    3
    4
    5
    6
    7
  2. 查询没有女朋友的男神信息

    SELECT bo.*
    FROM boys bo
    WHERE NOT EXISTS(
        SELECT boyfriend_id
        FROM beauty b
        WHERE bo.`id`=b.`boyfriend_id`
    );
    
    
    1
    2
    3
    4
    5
    6
    7
    8
    SELECT bo.*
    FROM boys bo
    WHERE bo.id NOT IN(
        SELECT boyfriend_id
        FROM beauty
    )
    
    1
    2
    3
    4
    5
    6

能用exists的地方就都可以用In替代

# 分页查询

# 应用场景

当要显示的数据,一页显示不全,需要分页提交sql请求

# 语法

    select 查询列表
    from 表
    【join type】 join 表2
    on 连接条件
    where 筛选条件
    group by 分组字段
    having 分组后的筛选
    order by 排序的字段】
    limit 【offset,】size;
1
2
3
4
5
6
7
8
9

offset要显示条目的起始索引(起始索引从0开始)

size 要显示的条目个数

# 分页公式

(page-1)*size,size
1

# 案例

1.查询前五条员工信息

SELECT * FROM  employees LIMIT 0,5;
# 或者
SELECT * FROM  employees LIMIT 5;
1
2
3

2.查询第11条~第25条

SELECT * FROM  employees LIMIT 10,15;
1

3.有奖金的员工信息,并且工资较高的前10名显示出来

SELECT 
    * 
FROM
    employees 
WHERE commission_pct IS NOT NULL 
ORDER BY salary DESC 
LIMIT 10;
1
2
3
4
5
6
7

合并、联合,将多次查询结果合并成一个结果

# 语法

查询语句1
union 【all】
查询语句2
union 【all】
...
1
2
3
4
5

# 意义

  1. 将一条比较复杂的查询语句拆分成多条语句
  2. 适用于查询多个表的时候,查询的列基本是一致

# 特点

  1. 要求多条查询语句的查询列数是一致的!
  2. 要求多条查询语句的查询的每一列的类型和顺序最好一致
  3. union关键字默认去重,如果使用union all 可以包含重复项

# 案例

  1. 查询部门编号>90或邮箱包含a的员工信息

    SELECT * FROM employees WHERE email LIKE '%a%' OR department_id>90;
    
    1
    SELECT * FROM employees  WHERE email LIKE '%a%'
    UNION
    SELECT * FROM employees  WHERE department_id>90;
    
    1
    2
    3

# DML数据处理之增删改

DML(Data Manipulation Language –数据操纵语言) 可以在下列条件下执行:

  • 向表中插入数据
  • 修改现存数据
  • 删除现存数据

事务是由完成若干项工作的DML语句组成的

运行以下脚本创建表my_employees

USE myemployees;
CREATE TABLE my_employees(
    Id INT(10),
    First_name VARCHAR(10),
    Last_name VARCHAR(10),
    Userid VARCHAR(10),
    Salary DOUBLE(10,2)
);
CREATE TABLE users(
    id INT,
    userid VARCHAR(10),
    department_id INT

);
1
2
3
4
5
6
7
8
9
10
11
12
13
14

显示表my_employees的结构

DESC my_employees;
1

# INSERT

# 方式一

# 语法
insert into 表名(字段名,...) values(值,...);
1
# 特点
  1. 要求值的类型和字段的类型要一致或兼容

  2. 字段的个数和顺序不一定与原始表中的字段个数和顺序一致

    但必须保证值和字段一一对应

  3. 假如表中有可以为null的字段,注意可以通过以下两种方式插入null值

    ① 字段和值都省略 ② 字段写上,值使用null

  4. 字段和值的个数必须一致

  5. 字段名可以省略,默认所有列

# 方式二

语法

insert into 表名 set 字段=值,字段=值,...;
1

两种方式的区别:

  1. 方式一支持一次插入多行,语法如下:

    insert into 表名【(字段名,..)】 values(值,..),(值,...),...;
    
    1
  2. 方式一支持子查询,语法如下:

    insert into 表名
    查询语句;
    
    1
    2

# 向表中插入数据

向my_employees表中插入下列数据

ID FIRST_NAME LAST_NAME USERID SALARY
1 patel Ralph Rpatel 895
2 Dancs Betty Bdancs 860
3 Biri Ben Bbiri 1100
4 Newman Chad Cnewman 750
5 Ropeburn Audrey Aropebur 155
INSERT INTO my_employees
VALUES
    ( 1, 'patel', 'Ralph', 'Rpatel', 895 ),
    ( 2, 'Dancs', 'Betty', 'Bdancs', 860 ),
    ( 3, 'Biri', 'Ben', 'Bbiri', 1100 ),
    ( 4, 'Newman', 'Chad', 'Cnewman', 750 ),
    ( 5, 'Ropeburn', 'Audrey', 'Aropebur', 1550 );
1
2
3
4
5
6
7

或者

# 先删除表数据
DELETE FROM my_employees;
# 再插入
INSERT INTO my_employees
SELECT 1,'patel','Ralph','Rpatel',895 UNION
SELECT 2,'Dancs','Betty','Bdancs',860 UNION
SELECT 3,'Biri','Ben','Bbiri',1100 UNION
SELECT 4,'Newman','Chad','Cnewman',750 UNION
SELECT 5,'Ropeburn','Audrey','Aropebur',1550;
1
2
3
4
5
6
7
8
9

向users表中插入数据

INSERT INTO users
VALUES
    ( 1, 'Rpatel', 10 ),
    ( 2, 'Bdancs', 10 ),
    ( 3, 'Bbiri', 20 );
1
2
3
4
5

# UPDATE

# 修改单表的记录

# 语法
update 表名 set 字段=值,字段=值 【where 筛选条件】;
1
  1. 将3号员工的last_name修改为“drelxer”

    UPDATE my_employees SET last_name='drelxer' WHERE id = 3;
    
    1
  2. 将所有工资少于900的员工的工资修改为1000

    UPDATE my_employees SET salary=1000 WHERE salary<900;
    
    1

# 修改多表的记录

# 语法
update 表1 别名 
left|right|inner join 表2 别名 
on 连接条件  
set 字段=值,字段=值 
【where 筛选条件】;
1
2
3
4
5

# DELETE

# 删除单表的记录

# 语法
delete from 表名 【where 筛选条件】【limit 条目数】
1

# 级联删除

# 语法
delete 别名1,别名2 from 表1 别名 
inner|left|right join 表2 别名 
on 连接条件
 【where 筛选条件】
1
2
3
4
  1. 将userid 为Bbiri的user表和my_employees表的记录全部删除

    DELETE u,e 
    FROM
        users u
        JOIN my_employees e 
    ON u.`userid` = e.`Userid` 
    WHERE
        u.`userid` = 'Bbiri';
    
    1
    2
    3
    4
    5
    6
    7
  2. 删除所有数据

    DELETE FROM my_employees;
    DELETE FROM users;
    
    1
    2

# TRUNCATE

清空表

# 语法

truncate table 表名
1

# DELETE和TRUNCATE的区别

  1. truncate删除后,如果再插入,标识列从1开始。delete删除后,如果再插入,标识列从断点开始
  2. delete可以添加筛选条件,truncate不可以添加筛选条件
  3. truncate效率较高
  4. truncate没有返回值,delete可以返回受影响的行数
  5. truncate不可以回滚,delete可以回滚

# DDL数据库定义语言

数据定义语言:简称DDL(Data Definition Language),用来定义数据库对象:数据库,表,列等。关键字:create,alter,drop等

  • 创建: create
  • 修改: alter
  • 删除: drop

# 库的管理

# 创建库

create database 【if not exists】 库名【 character set 字符集名】;
CREATE DATABASE IF NOT EXISTS books;
1
2

# 修改库

alter database 库名 character set 字符集名;
1
# 修改库名
已废弃:RENAME DATABASE books TO 新库名;
1
# 修改库的字符集
ALTER DATABASE books CHARACTER SET gbk;
1

# 删除库

drop database 【if exists】 库名;
1

# 表的管理

# 创建表

create table 表名(
    列名 列的类型【(长度) 约束】,
    列名 列的类型【(长度) 约束】,
    列名 列的类型【(长度) 约束】,
    ...
    列名 列的类型【(长度) 约束】
)
1
2
3
4
5
6
7

创建表Book表

CREATE TABLE book ( 
    id INT AUTO_INCREMENT PRIMARY KEY, #编号
    bName VARCHAR ( 20 ), #图书名
    price DOUBLE, #价格
    authorId INT, #作者编号
    publishDate DATETIME #出版日期
);


CREATE TABLE college_professional(
	cp_id INT AUTO_INCREMENT PRIMARY KEY,
	c_id INT ,
	p_id INT ,
	CONSTRAINT fk_college_id FOREIGN KEY(c_id) REFERENCES college(c_id) 
)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

查看book表结构

DESC book;
1

创建表author

CREATE TABLE IF NOT EXISTS author(
    id INT,
    au_name VARCHAR(20),
    nation VARCHAR(10)
)
1
2
3
4
5

数据类型

# 修改表

# 添加列
alter table 表名 add column 列名 类型 【first|after 字段名】;
1
# 修改列的类型或约束
alter table 表名 modify column 列名 新类型 【新约束】;

ALTER TABLE college_professional ADD CONSTRAINT fk_professional_id FOREIGN KEY(p_id) REFERENCES professional(p_id)
1
2
3
# 修改列名
alter table 表名 change column 旧列名 新列名 类型;
1
# 删除列
alter table 表名 drop column 列名;
1
# 修改表名
alter table 表名 rename 【to】 新表名;
1

# 删除表

drop table【if exists】 表名;
1

# 复制表

# 复制表的结构
create table 表名 like 旧表;
1
# 复制表的结构+数据
create table 表名 
select 查询列表 from 旧表【where 筛选】;
1
2

案例

  1. 向author表插入数据

    INSERT INTO author
    VALUES
        ( 1, '村上春树', '日本' ),
        ( 2, '莫言', '中国' ),
        ( 3, '冯唐', '中国' ),
        ( 4, '金庸', '中国' );
    
    1
    2
    3
    4
    5
    6
  2. 创建表copy,复制author表的结构

    CREATE TABLE copy LIKE author;
    
    1
  3. 创建表copy2,复制author表的结构和数据

    CREATE TABLE copy2 
    SELECT * FROM author;
    
    1
    2
  4. 创建表copy3,复制author表的部分数据

    CREATE TABLE copy3
    SELECT id,au_name
    FROM author 
    WHERE nation='中国';
    
    1
    2
    3
    4
  5. 创建表copy4,复制author表的部分字段

    CREATE TABLE copy4 
    SELECT id,au_name
    FROM author
    WHERE 0;
    
    1
    2
    3
    4

# MYSQL数据类型

分类 类型名称 说明
整数类型 tinyInt 很小的整数
smallint 小的整数
mediumint 中等大小的整数
int(integer) 普通大小的整数
小数类型 float 单精度浮点数
double 双精度浮点数
decimal(m,d) 压缩严格的定点数
日期类型 year YYYY 1901~2155
time HH : MM : SS -838:59 : 59~838 : 59 : 59
date YYYY-MM-DD 1000-01-01~9999-12-3
datetime YYYY-MM-DD HH : MM : SS 1000-01-01 00 : 00 : 00~ 9999-12-31 23 : 59 : 59
timestamp YYYY-MM-DD HH : MM : SS 19700101 00 : 00 : 01 UTC~2038-01-19 03 : 14 : 07UTC
文本、二进制类型 CHAR(M) M为0~255之间的整数
VARCHAR(M) M为0~65535之间的整数
TINYBLOB 允许长度0~255字节
BLOB 允许长度0~65535字节
MEDIUMBLOB 允许长度0~167772150字节
LONGBLOB 允许长度0~4294967295字节
TINYTEXT 允许长度0~255字节
TEXT 允许长度0~65535字节
MEDIUMTEXT 允许长度0~167772150字节
LONGTEXT 允许长度0~4294967295字节
VARBINARY(M) 允许长度0~M个字节的变长字节字符串
BINARY(M) 允许长度0~M个字节的定长字节字符串

# 数值型

# 整型

tinyint、smallint、mediumint、int/integer、bigint

特点:

  1. 都可以设置无符号和有符号,默认有符号,通过unsigned设置无符号
  2. 如果超出了范围,会报out or range异常,插入临界值
  3. 长度可以不指定,默认会有一个长度,长度代表显示的最大宽度,如果不够则左边用0填充,但需要搭配zerofill,并且默认变为无符号整型

如何设置无符号

CREATE TABLE tab_int(
    t1 INT,
    t2 INT unsigned 
);
1
2
3
4

显示长度

CREATE TABLE tab_int(
    t1 INT(7) ZEROFILL,
    t2 INT(7) ZEROFILL 
);
1
2
3
4
# 浮点型

定点数:decimal(M,D) 浮点数: float(M,D) double(M,D)

特点:

  1. M代表整数部位+小数部位的个数,D代表小数部位
  2. 如果超出范围,则报out or range异常,并且插入临界值
  3. M和D都可以省略,但对于定点数,M默认为10,D默认为0
  4. 如果精度要求较高,则优先考虑使用定点数

# 字符型

char、varchar、binary、varbinary、enum(用于保存枚举)、set(用于保存集合)、text、blob

char:固定长度的字符,写法为char(M),最大长度不能超过M,其中M可以省略,默认为1

varchar:可变长度的字符,写法为varchar(M),最大长度不能超过M,其中M不可以省略

Enum:又称为枚举类型哦,要求插入的值必须属于列表中指定的值之一。

Set:和Enum类型类似,里面可以保存0~64个成员。和Enum类型最大的区别是:SET类型一次可以选取多个成员,而Enum只能选一个

CREATE TABLE tab_char(
    c1 ENUM('a','b','c')
);

INSERT INTO tab_char VALUES('a');
INSERT INTO tab_char VALUES('b');
INSERT INTO tab_char VALUES('c');
INSERT INTO tab_char VALUES('m');    --> 会插入空字符 ''
INSERT INTO tab_char VALUES('A');    -->  会插入 'a'
CREATE TABLE tab_set(
    s1 SET('a','b','c','d')
);

INSERT INTO tab_set VALUES('a');
INSERT INTO tab_set VALUES('A,B');
INSERT INTO tab_set VALUES('a,c,d');
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 日期型

year年 date日期 time时间 datetime 日期+时间 timestamp 日期+时间 ,比较容易受时区、语法模式、版本的影响,更能反映当前时区的真实时间

# 常见的约束

  • NOT NULL:非空,该字段的值必填
  • UNIQUE:唯一,该字段的值不可重复
  • DEFAULT:默认,该字段的值不用手动插入有默认值
  • CHECK:检查,mysql不支持(但是设置并不会报错,只是没有效果)
  • PRIMARY KEY:主键,该字段的值不可重复并且非空 unique+not null
  • FOREIGN KEY:外键,该字段的值引用了另外的表的字段

添加约束的时机:

  1. 创建表时
  2. 修改表时

约束的添加分类:

    列级约束:

        六大约束语法上都支持,但外键约束没有效果

    表级约束:

        除了非空、默认,其他的都支持
1
2
3
4
5
6
7

# 主键和唯一

  1. 一个表至多有一个主键,但可以有多个唯一
  2. 主键不允许为空,唯一可以为空
保证唯一性 是否允许为空 一个表中可以有多少个 是否允许组合
主键 √ × 至多有1个 √,但不推荐
唯一 √ √ 可以有多个 √,但不推荐

外键:

  1. 要求在从表设置外键关系
  2. 从表的外键列的类型和主表的关联列的类型要求一致或兼容,名称无要求
  3. 主表的关联列必须是一个key(一般是主键或唯一)
  4. 插入数据时,先插入主表,再插入从表
  5. 删除数据时,先删除从表,再删除主表

# 创建表时添加约束

CREATE TABLE 表名(
    字段名 字段类型 列级约束,
    字段名 字段类型,
    表级约束
)
1
2
3
4
5
# 添加列级约束

直接在字段名和类型后面追加 约束类型即可。

只支持:默认、非空、主键、唯一

# 创建一个数据库
CREATE DATABASE students;
USE students;
CREATE TABLE stuinfo (
    id INT PRIMARY KEY,#主键
    stuName VARCHAR ( 20 ) NOT NULL UNIQUE,#非空并且唯一
    gender CHAR ( 1 ) CHECK ( gender = '男' OR gender = '女' ),#检查
    seat INT UNIQUE,#唯一
    age INT DEFAULT 18,#默认约束
    majorId INT REFERENCES major ( id ) #外键,(没有效果)
);

CREATE TABLE major(
    id INT PRIMARY KEY,
    majorName VARCHAR(20)
);

# 查看stuinfo中的所有索引,包括主键、外键、唯一
SHOW INDEX FROM stuinfo;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 添加表级约束

在各个字段的最下面

【constraint 约束名】 约束类型(字段名)

DROP TABLE IF EXISTS stuinfo;
CREATE TABLE stuinfo (
    id INT,
    stuname VARCHAR ( 20 ),
    gender CHAR ( 1 ),
    seat INT,
    age INT,
    majorid INT,
    CONSTRAINT pk PRIMARY KEY ( id ),#主键
    CONSTRAINT uq UNIQUE ( seat ),#唯一键
    CONSTRAINT ck CHECK ( gender = '男' OR gender = '女' ),#检查
    CONSTRAINT fk_stuinfo_major FOREIGN KEY ( majorid ) REFERENCES major ( id ) #外键

);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 通用写法
CREATE TABLE IF NOT EXISTS stuinfo (
        id INT PRIMARY KEY,
        stuname VARCHAR ( 20 ),
        sex CHAR ( 1 ),
        age INT DEFAULT 18,
        seat INT UNIQUE,
        majorid INT,
    CONSTRAINT fk_stuinfo_major FOREIGN KEY ( majorid ) REFERENCES major ( id ) 
);
1
2
3
4
5
6
7
8
9

# 修改表时添加约束

  • 添加列级约束
alter table 表名 modify column 字段名 字段类型 新约束;
1
  • 添加表级约束
alter table 表名 add 【constraint 约束名】 约束类型(字段名) 【外键的引用】;
DROP TABLE IF EXISTS stuinfo;
CREATE TABLE stuinfo(
    id INT,
    stuname VARCHAR(20),
    gender CHAR(1),
    seat INT,
    age INT,
    majorid INT
)
1
2
3
4
5
6
7
8
9
10
  1. 添加非空约束

    ALTER TABLE stuinfo MODIFY COLUMN stuname VARCHAR(20)  NOT NULL;
    
    1
  2. 添加默认约束

    ALTER TABLE stuinfo MODIFY COLUMN age INT DEFAULT 18;
    
    1
  3. 添加主键

    ① 列级约束

    ALTER TABLE stuinfo MODIFY COLUMN id INT PRIMARY KEY;
    
    1

    ② 表级约束

    ALTER TABLE stuinfo ADD PRIMARY KEY(id);
    
    1
  4. 添加唯一

    ① 列级约束

    ALTER TABLE stuinfo MODIFY COLUMN seat INT UNIQUE;
    
    1

    ② 表级约束

    ALTER TABLE stuinfo ADD UNIQUE(seat);
    
    1
  5. 添加外键

    ALTER TABLE stuinfo 
    ADD CONSTRAINT fk_stuinfo_major FOREIGN KEY(majorid) REFERENCES major(id); 
    
    1
    2

# 修改表时删除约束

  1. 删除非空约束

    ALTER TABLE stuinfo MODIFY COLUMN stuname VARCHAR(20) NULL;
    
    1
  2. 删除默认约束

    ALTER TABLE stuinfo MODIFY COLUMN age INT ;
    
    1
  3. 删除主键

    ALTER TABLE stuinfo DROP PRIMARY KEY;
    
    1
  4. 删除唯一

    ALTER TABLE stuinfo DROP INDEX seat;
    
    1
  5. 删除外键

    ALTER TABLE stuinfo DROP FOREIGN KEY fk_stuinfo_major;
    
    1

# 自增长列

不用手动插入值,可以自动提供序列值,默认从1开始,步长为1

auto_increment_increment
1

如果要更改起始值:手动插入值

如果要更改步长:更改系统变量:set auto_increment_increment=值;

  1. 一个表至多有一个自增长列
  2. 自增长列只能支持数值型
  3. 自增长列必须为一个key

# 创建表时设置自增长列

create table 表(
    字段名 字段类型 约束 auto_increment
)
1
2
3

# 修改表时设置自增长列

alter table 表 modify column 字段名 字段类型 约束 auto_increment
1

# 删除自增长列

alter table 表 modify column 字段名 字段类型 约束 
1

# MySQL事物

TCL:Transaction Control Language 事务控制语言

事务由单独单元的一个或多个SQL语句组成,在这个单元中,每个MySQL语句是相互依赖的。而整个单独单元作为一个不可分割的整体,如果单元中某条SQL语句一旦执行失败或产生错误,整个单元将会回滚。所有受到影响的数据将返回到事物开始以前的状态;如果单元中的所有SQL语句均执行成功,则事物被顺利执行。

一个或一组sql语句组成一个执行单元,这个执行单元要么全部执行,要么全部不执行。

  • 概念:在mysql中的数据用各种不同的技术存储在文件(或内存)中。
  • 通过show engines;来查看mysql支持的存储引擎。
  • 在mysql中用的最多的存储引擎有:innodb,myisam ,memory 等。其中innodb支持事务,而myisam、memory等不支持

# 转账案例

张三丰转给郭襄500元

# 张三丰  1000
# 郭襄    1000

update 表 set 张三丰的余额=张三丰的余额-500 where name='张三丰'

# 中间发生意外,张三丰的余额少了500,而郭襄的余额并没有增加

update 表 set 郭襄的余额=郭襄的余额+500 where name='郭襄'
1
2
3
4
5
6
7
8

# 事务的特性(ACID)

  1. 原子性(Atomicity)

    原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。

  2. 一致性(Consistency)

    事务必须使数据库从一个一致性状态变换到另外一个一致性状态。

  3. 隔离性(Isolation)

    事务的隔离性是指一个事务的执行不能被其他事务干扰,即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。

  4. 持久性(Durability)

    持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来的其他操作和数据库故障不应该对其有任何影响

# 事务的创建

# 隐式事务

事务没有明显的开启和结束的标记,比如insert、update、delete语句

# 执行完表中id列为1的那一行数据就直接被删除了
delete from 表 where id =1;
1
2

# 显式事务

事务具有明显的开启和结束的标记

前提:必须先设置自动提交功能为禁用 set autocommit=0;

# 查看自动提交是否开启
SHOW VARIABLES LIKE 'autocommit';

# 查看数据库引擎
SHOW ENGINES;
1
2
3
4
5
# 开启事物步骤
  1. 开启事务

    # 关闭自动提交
    set autocommit=0;
    
    # 开启事物(可选)
    start transaction;
    
    1
    2
    3
    4
    5
  2. 编写事务中的sql语句(select insert update delete)

  3. 可选:savepoint 节点名;设置回滚点

  4. 结束事务

    commit;提交事务
    # 或者
    rollback;回滚事务
    # 或者
    rollback to 回滚点名;回滚到指定的地方
    
    1
    2
    3
    4
    5

# 并发事务

  1. 事务的并发问题是如何发生的?

    多个事务 同时 操作 同一个数据库的相同数据时

  2. 并发问题都有哪些?

    脏读: 对于两个事务 T1, T2, T1 读取了已经被 T2 更新但还没有被提交的字段. 之后, 若 T2 回滚, T1读取的内容就是临时且无效的

    不可重复读: 对于两个事务T1, T2, T1 读取了一个字段, 然后 T2 更新了该字段. 之后, T1再次读取同一个字段, 值就不同了(在一个事物中不管读多少次,读取的数据应该都一样)

    幻读: 对于两个事务T1, T2, T1 从一个表中读取了一个字段, 然后 T2 在该表中插入了一些新的行. 之后, 如果 T1 再次读取同一个表, 就会多出几行

  3. 如何解决并发问题

    通过设置隔离级别来解决并发问题

# 事物隔离级别

√:已解决

隔离级别 脏读 不可重复读 幻读
read uncommitted:读未提交 × × ×
read committed:读已提交 √ × ×
repeatable read:可重复读 √ √ ×
serializable:串行化 √ √ √
mysql`中默认 第三个隔离级别 `repeatable read
oracle`中默认第二个隔离级别 `read committed
1
2

查看隔离级别

select @@tx_isolation;
1

设置隔离级别

# 设置当前 mySQL 连接的隔离级别: 
set transaction isolation level read committed;

# 设置数据库系统的全局的隔离级别:
set global transaction isolation level read committed;
1
2
3
4
5

# 案例

  1. 事务的使用步骤

    # 开启事务
    SET autocommit=0;
    START TRANSACTION;
    # 编写一组事务的语句
    UPDATE account SET balance = 1000 WHERE username='张无忌';
    UPDATE account SET balance = 1000 WHERE username='赵敏';
    
    # 结束事务
    ROLLBACK;
    # COMMIT;
    
    SELECT * FROM account;
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
  2. 事务对于delete和truncate的处理的区别

    SET autocommit=0;
    START TRANSACTION;
    
    DELETE FROM account;
    ROLLBACK;
    
    1
    2
    3
    4
    5

    TRUNCATE 无法回滚

    SET autocommit=0;
    START TRANSACTION;
    
    TRUNCATE TABLE account;
    ROLLBACK;
    
    1
    2
    3
    4
    5
  3. savepoint 的使用

    SET autocommit=0;
    START TRANSACTION;
    DELETE FROM account WHERE id=25;
    SAVEPOINT a;#设置保存点
    DELETE FROM account WHERE id=28;
    ROLLBACK TO a;#回滚到保存点
    
    SELECT * FROM account;
    
    1
    2
    3
    4
    5
    6
    7
    8

# MySQL视图

MySQL从5.0.1版本开始提供视图功能。一种虚拟存在的表,行和列的数据来自定义视图的查询中 使用的表,并且是在使用视图时动态生成的,只保存了sql逻辑,不保存查询结果

# 应用场景

  • 多个地方用到同样的查询结果
  • 该查询结果使用的sql语句较复杂

# 优点

  1. 简化sql语句
  2. 提高了sql的重用性
  3. 保护基表的数据,提高了安全性

# 创建视图

create view 视图名
as
查询语句;
1
2
3
  1. 创建视图emp_v1,要求查询电话号码以‘011’开头的员工姓名和工资、邮箱

    CREATE VIEW emp_v1 AS SELECT
        last_name,
        salary,
        email 
    FROM
        employees 
    WHERE
        phone_number LIKE '011%';
    
    1
    2
    3
    4
    5
    6
    7
    8
  2. 创建视图emp_v2,要求查询部门的最高工资高于12000的部门信息

    # 创建视图,查询每个部门的最高工资,筛选出高于12000的
    DROP VIEW emp_v2;
    CREATE VIEW emp_v2 AS SELECT
    department_id,
    MAX( salary ) max_salary 
    FROM
        employees 
    GROUP BY
        department_id 
    HAVING
        max_salary > 12000;
    
    # 根据创建的视图连接departments表查询部门信息
    SELECT
        d.*,
        ev2.max_salary 
    FROM
        departments d
        JOIN emp_v2 ev2 
        ON d.department_id = ev2.department_id;
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
  3. 查询姓名中包含a字符的员工名、部门名和工种信息

    # 查询员工名、部门名和工种信息
    CREATE VIEW emp_v3 AS SELECT
    e.last_name,
    d.department_name,
    j.job_title 
    FROM
        employees e
        LEFT JOIN departments d ON e.department_id = d.department_id
        LEFT JOIN jobs j ON e.job_id = j.job_id;
    
    # 筛选出姓名中包含a字符的员工
    SELECT * FROM emp_v3 WHERE last_name LIKE '%a%';
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
  4. 查询各部门的平均工资级别

    # 查询各部门的平均工资
    CREATE VIEW emp_v4 AS SELECT
    AVG( salary ) avg_salary,
    department_id 
    FROM
        employees 
    GROUP BY
        department_id;
    
    # 查询各部门的平均工资级别
    SELECT
        department_id,
        j.grade_level 
    FROM
        emp_v4 ev4
        LEFT JOIN job_grades j 
        ON ev4.avg_salary BETWEEN j.lowest_sal 
        AND j.highest_sal
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
  5. 查询平均工资最低的部门信息

    SELECT
        d.*,
        ev4.avg_salary 
    FROM
        departments d
        JOIN ( SELECT * FROM emp_v4 ORDER BY avg_salary LIMIT 1 ) ev4 
        ON d.department_id = ev4.department_id
    
    1
    2
    3
    4
    5
    6
    7

# 修改视图

# 方式一

create or replace view  视图名
as
查询语句;
1
2
3

# 方式二

alter view 视图名
as 
查询语句;
1
2
3

# 删除视图

drop view 视图名,视图名,...
DROP VIEW emp_v1,emp_v2,emp_v3,emp_v4;
1
2

# 查看视图

DESC 视图名;

SHOW CREATE VIEW 视图名;
CREATE VIEW emp_v1 AS SELECT
* 
FROM
    employees;

DESC emp_v1;

SHOW CREATE VIEW emp_v1;
1
2
3
4
5
6
7
8
9
10
11

# 视图的更新

视图的可更新性和视图中查询的定义有关系,以下类型的视图是不能更新的。

  • 包含以下关键字的sql语句:分组函数、distinct、group by
  • having、union或者union all
  • 常量视图
  • Select中包含子查询
  • join
  • from一个不能更新的视图
  • where子句的子查询引用了from子句中的表

# 案例

  1. 张飞能否成功插入?
   # 创建视图,查询员工的姓名,邮箱和年薪
   
   CREATE 
       OR REPLACE VIEW myv1 AS SELECT
       last_name,
       email,
       salary * 12 *(1+IFNULL ( commission_pct, 0 )) "annual salary" 
   FROM
       employees;
   
   # 插入一条数据
   INSERT INTO myv1 VALUES('张飞','zf@qq.com',94862.00);
1
2
3
4
5
6
7
8
9
10
11
12
  1. 张飞能否成功插入?
   # 创建视图,查询员工的姓名和邮箱
   
   CREATE 
       OR REPLACE VIEW myv1 AS SELECT
       last_name,
       email
   FROM
       employees;
   
   # 插入一条数据
   INSERT INTO myv1 VALUES('张飞','zf@qq.com');
1
2
3
4
5
6
7
8
9
10
11
  1. 能否将张飞修改为张无忌?
   UPDATE myv1 SET last_name = '张无忌' WHERE last_name='张飞';
1
  1. 能否干掉张无忌?
   DELETE FROM myv1 WHERE last_name = '张无忌';
1
  1. 能否将10号部门的最高薪水改为9000?
   # 创建视图,查询每个部门的最高工资
   CREATE 
       OR REPLACE VIEW myv1 AS SELECT
       MAX( salary ) m,
       department_id 
   FROM
       employees 
   GROUP BY
       department_id;
   
   # 将10号部门的最高薪水改为9000
   UPDATE myv1 SET m=9000 WHERE department_id=10;
1
2
3
4
5
6
7
8
9
10
11
12
  1. 能否更改?
   CREATE OR REPLACE VIEW myv2
   AS
   SELECT 'john' NAME;
   
   #更新
   UPDATE myv2 SET NAME='lucy';
1
2
3
4
5
6
  1. 能够将最高工资列修改为100000?
   CREATE OR REPLACE VIEW myv3 AS 
   SELECT department_id,( SELECT MAX( salary ) FROM employees ) 最高工资 
   FROM
       departments;
   
   # 修改
   
   UPDATE myv3 SET 最高工资=100000;
1
2
3
4
5
6
7
8
  1. 修改和插入能成功吗?
   # 创建视图,查询员工名与部门名
   CREATE 
       OR REPLACE VIEW myv4 AS SELECT
       last_name,
       department_name 
   FROM
       employees e
       JOIN departments d ON e.department_id = d.department_id;
   
   # 修改
   UPDATE myv4 SET last_name  = '张飞' WHERE last_name='Whalen';
   # 插入
   INSERT INTO myv4 VALUES('陈真','Acc');
1
2
3
4
5
6
7
8
9
10
11
12
13
  1. 能修改吗?
   CREATE 
       OR REPLACE VIEW myv5 AS SELECT
       * 
   FROM
       myv3;
   
   # 修改
   UPDATE myv5 SET 最高工资=10000 WHERE department_id=60;
1
2
3
4
5
6
7
8
  1. 能修改吗?
   # 查询所有的领导信息
   CREATE OR REPLACE VIEW myv6
   AS
   
   SELECT last_name,email,salary
   FROM employees
   WHERE employee_id IN(
       SELECT  manager_id
       FROM employees
       WHERE manager_id IS NOT NULL
   );
   
   # 修改
   UPDATE myv6 SET salary=10000 WHERE last_name = 'k_ing';
1
2
3
4
5
6
7
8
9
10
11
12
13
14

查询领导Id(将所有员工的上级Id查出来,这些Id就是领导Id):

SELECT DISTINCT manager_id FROM employees WHERE manager_id IS NOT NULL

# 视图和表的对比

关键字 是否占用物理空间 使用
视图 view 占用较小,只保存sql逻辑 一般用于查询
表 table 保存实际的数据 增删改查

# 变量

# 系统变量

系统变量:

  • 全局变量
  • 会话变量

说明:变量由系统定义,不是用户定义,属于服务器层面

注意:全局变量需要添加global关键字,会话变量需要添加session关键字,如果不写,默认会话级别

  1. 查看所有系统变量

    # 全局
    show global variables;
    
    #会话
    show【session】variables;
    
    1
    2
    3
    4
    5
  2. 查看满足条件的部分系统变量

    show global |【session】 variables like '%char%';
    
    1
  3. 查看指定的系统变量的值(带.)

    select @@global |【@@session】.系统变量名;
    
    1
  4. 为某个系统变量赋值

    • 方式一

      set global|【session】系统变量名=值;
      
      1
    • 方式二(带.)

      set @@global|【@@session】.系统变量名=值;
      
      1

例如:

# 查看所有全局变量
SHOW GLOBAL VARIABLES;
# 查看满足条件的部分系统变量
SHOW GLOBAL VARIABLES LIKE '%char%';
# 查看指定的系统变量的值
SELECT @@global.autocommit;
# 为某个系统变量赋值
SET @@global.autocommit=0;
SET GLOBAL autocommit=0;
# 查看所有会话变量
SHOW SESSION VARIABLES;
# 查看满足条件的部分会话变量
SHOW SESSION VARIABLES LIKE '%char%';
# 查看指定的会话变量的值
SELECT @@autocommit;
SELECT @@session.tx_isolation;
# 为某个会话变量赋值
SET @@session.tx_isolation='read-uncommitted';
SET SESSION tx_isolation='read-committed';
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 自定义变量

变量由用户自定义,而不是系统提供的

作用域:针对于当前会话(连接)有效,作用域同于会话变量

声明并初始化

赋值操作符:=或:=

SET @变量名=值;
SET @变量名:=值;
SELECT @变量名:=值;
1
2
3

更新变量的值

  • 方式一

    SET @变量名=值;
    SET @变量名:=值;
    SELECT @变量名:=值;
    
    1
    2
    3
  • 方式二

    SELECT 字段 INTO @变量名
    FROM 表;
    
    1
    2

查看变量的值

SELECT @变量名;
1

# 局部变量

作用域:仅仅在定义它的begin end块中有效

应在 begin end中的第一句话声明

声明

DECLARE
    变量名 类型;
    ...
END

## 

DECLARE 
    变量名 类型 【DEFAULT 值】;
    ...
END
1
2
3
4
5
6
7
8
9
10
11

赋值(更新变量的值)

  • 方式一

    SET 局部变量名=值;
    SET 局部变量名:=值;
    SELECT 局部变量名:=值;
    
    1
    2
    3
  • 方式二

    SELECT 字段 INTO 具备变量名
    FROM 表;
    
    1
    2

查看变量的值

SELECT 局部变量名;
1

# 案例

  1. 声明两个用户变量,求和并打印

    SET @m=1;
    SET @n=1;
    SET @sum=@m+@n;
    SELECT @sum;
    
    1
    2
    3
    4

# 存储过程

什么是存储过程:

事先经过编译并存储在数据库中的一段sql语句的集合。

类似于java中的方法

使用好处

  1. 简化应用开发人员的很多工作
  2. 减少数据在数据库和应用服务器之间的传输
  3. 提高了数据处理的效率

一组预先编译好的SQL语句的集合,理解成批处理语句

# 创建语法

CREATE PROCEDURE 存储过程名(参数列表)
BEGIN

    存储过程体(一组合法的SQL语句)
END
1
2
3
4
5

参数列表包含三部分

  • 参数模式
  • 参数名
  • 参数类型

例:in stuname varchar(20)

参数模式:

in:该参数可以作为输入,也就是该参数需要调用方传入值

out:该参数可以作为输出,也就是该参数可以作为返回值

inout:该参数既可以作为输入又可以作为输出,也就是该参数既需要传入值,又可以返回值

  • 如果存储过程体仅仅只有一句话,begin end可以省略

  • 存储过程体中的每条sql语句的结尾要求必须加分号。

  • 存储过程的结尾可以使用 delimiter 重新设置(避免与存储过程体的分号混淆)

    delimiter 结束标记
    # 例
    delimiter $
    
    1
    2
    3

例:

插入到admin表中五条记录

USE girls;

DELIMITER $
CREATE PROCEDURE myp1()
BEGIN
    INSERT INTO admin(username,`password`) 
    VALUES
    ('john1','0000'),('lily','0000'),('rose','0000'),('jack','0000'),('tom','0000');
END $
1
2
3
4
5
6
7
8
9

# 调用语法

CALL 存储过程名(实参列表);
CALL myp1();
1
2

# 创建带in模式参数的存储过程

创建存储过程实现 根据女神名,查询对应的男神信息

DELIMITER $
CREATE PROCEDURE myp2(IN beautyName VARCHAR(20))
BEGIN
    SELECT bo.*
    FROM boys bo
    RIGHT JOIN beauty b ON bo.id = b.boyfriend_id
    WHERE b.name=beautyName;
END $
CALL myp2('赵敏');
1
2
3
4
5
6
7
8
9

创建存储过程实现,判断用户是否登录成功

DELIMITER $
CREATE PROCEDURE myp3(IN username VARCHAR(20),IN PASSWORD VARCHAR(20))
BEGIN
    DECLARE result INT DEFAULT 0;    #声明并初始化

    SELECT COUNT(*) INTO result    #赋值
    FROM admin
    WHERE admin.username = username
    AND admin.password = PASSWORD;

    SELECT IF(result>0,'成功','失败');    #使用
END $
CALL myp3('john','8888');
CALL myp3('john','1234');
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 创建out模式参数的存储过程

根据输入的女神名,返回对应的男神名

DELIMITER $
CREATE PROCEDURE myp4(IN beautyName VARCHAR(20),OUT boyName VARCHAR(20))
BEGIN
    SELECT bo.boyname INTO boyname
    FROM boys bo
    RIGHT JOIN
    beauty b ON b.boyfriend_id = bo.id
    WHERE b.name=beautyName ;
END $
# 调用 使用自定义变量接收
CALL myp4('赵敏',@name);
SELECT @name;
1
2
3
4
5
6
7
8
9
10
11
12

根据输入的女神名,返回对应的男神名和魅力值

DELIMITER $
CREATE PROCEDURE myp5(IN beautyName VARCHAR(20),OUT boyName VARCHAR(20),OUT usercp INT) 
BEGIN
    SELECT boys.boyname ,boys.usercp INTO boyname,usercp
    FROM boys 
    RIGHT JOIN
    beauty b ON b.boyfriend_id = boys.id
    WHERE b.name=beautyName ;

END $
CALL myp5('小昭',@name,@cp);
SELECT @name,@cp;
1
2
3
4
5
6
7
8
9
10
11
12

# 创建带inout模式参数的存储过程

传入a和b两个值,最终a和b都翻倍并返回

DELIMITER $
CREATE OR REPLACE PROCEDURE myp5(INOUT a INT,INOUT b INT)
BEGIN

    SET a:=a*2;
    SET b:=b*2;

END $
SET @m=10;
SET @n=20;
CALL myp5(@m,@n);
SELECT @m,@n;
1
2
3
4
5
6
7
8
9
10
11
12

# 查看存储过程的信息

SHOW CREATE PROCEDURE myp5;
1

# 存储函数

# 存储过程与存储函数的区别

存储过程:可以有0个返回,也可以有多个返回,适合做批量插入、批量更新

存储函数:有且仅有1 个返回,适合做处理数据后返回一个结果

# 创建语法

CREATE FUNCTION 函数名(参数列表) RETURNS 返回类型
BEGIN
    函数体
END
1
2
3
4
  • 参数列表 包含两部分:参数名,参数类型
  • 函数体:
    • 肯定会有return语句,如果没有会报错 如果return语句没有放在函数体的最后也不报错,但不建议
    • 函数体中仅有一句话,则可以省略begin end
  • 使用 delimiter语句设置结束标记

# 调用语法

SELECT 函数名(参数列表);
1

# 无参有返回

返回公司的员工个数

USE myemployees;
DELIMITER //
CREATE FUNCTION myf1() RETURNS INT
BEGIN

    DECLARE c INT DEFAULT 0;    #定义局部变量
    SELECT COUNT(*) INTO c     #赋值
    FROM employees;
    RETURN c;

END //
SELECT myf1();
1
2
3
4
5
6
7
8
9
10
11
12

# 有参有返回

根据员工名,返回它的工资

DELIMITER //
CREATE FUNCTION myf2(empName VARCHAR(20)) RETURNS DOUBLE
BEGIN
    SET @sal=0;    #定义用户变量 
    SELECT salary INTO @sal    #赋值
    FROM employees
    WHERE last_name = empName;

    RETURN @sal;

END //
SELECT myf2('Kochhar');

SELECT @sal;
1
2
3
4
5
6
7
8
9
10
11
12
13
14

根据部门名,返回该部门的平均工资

DELIMITER //
CREATE FUNCTION myf3(deptName VARCHAR(20)) RETURNS DOUBLE
BEGIN
    DECLARE sal DOUBLE ;
    SELECT AVG(salary) INTO sal
    FROM employees e
    JOIN departments d ON e.department_id = d.department_id
    WHERE d.department_name=deptName;
    RETURN sal;
END //
SELECT myf3('IT');
1
2
3
4
5
6
7
8
9
10
11

# 查看函数

SHOW CREATE FUNCTION myf3;
1

# 删除函数

DROP FUNCTION myf3;
1

创建函数,实现传入两个float,返回二者之和

DELIMITER //
CREATE OR REPLACE FUNCTION myf3(num1 FLOAT,num2 FLOAT) RETURNS FLOAT(3,1)
BEGIN
    DECLARE SUM FLOAT(3,1) DEFAULT 0;
    SET SUM=num1+num2;
    RETURN SUM;
END //
SELECT myf3(15.5,15.3);    --> 30.8
1
2
3
4
5
6
7
8

# 流程控制结构

# 分支结构

# if函数

语法:if(条件,值1,值2)

功能:实现双分支

应用在begin end中或外面

# case结构

类似java中的switch

功能:实现多分支

应用在begin end 中或外面

语法一:

case 表达式或字段
when 值1 then 语句1;
when 值2 then 语句2;
...
else 语句n;
end [case];
1
2
3
4
5
6

语法二:

case 
when 条件1 then 语句1;
when 条件2 then 语句2;
...
else 语句n;
end [case];
1
2
3
4
5
6

创建函数,实现传入成绩,如果成绩>90,返回A,如果成绩>80,返回B,如果成绩>60,返回C,否则返回D

DELIMITER //
CREATE OR REPLACE FUNCTION test_case(score DOUBLE) RETURNS CHAR
BEGIN
-- DECLARE result CHAR;
CASE  
    WHEN score>90 THEN
        RETURN 'A';
    WHEN score>80 THEN 
        RETURN 'B';
    WHEN score>60 THEN
        RETURN 'C';
    ELSE
        RETURN 'D';
END CASE;

END //
SELECT test_case(95);

SELECT test_case(85);

SELECT test_case(75);

SELECT test_case(55);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# if结构

功能:实现多分支

只能放在begin end中

语法:

if 条件1 then 语句1;
elseif 条件2 then 语句2;
...
else 语句n;
end if;
1
2
3
4
5

创建函数,实现传入成绩,如果成绩>90,返回A,如果成绩>80,返回B,如果成绩>60,返回C,否则返回D

DELIMITER //
CREATE OR REPLACE FUNCTION test_if(score DOUBLE) RETURNS CHAR 
BEGIN 
    DECLARE result CHAR;
    IF score>90 THEN
        SET result:='A';
    ELSEIF score>80 THEN
        SET result:='B';
    ELSEIF score>60 THEN
        SET result:='C';
    ELSE
        SET result:='D';
    END IF;
    RETURN result;
END //
SELECT test_if(95);

SELECT test_if(85);

SELECT test_if(75);

SELECT test_if(55);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

创建存储过程,如果工资<2000,则删除,如果5000>工资>2000,则涨工资1000,否则涨工资500

DELIMITER //
CREATE OR REPLACE PROCEDURE test_if2(IN salary DOUBLE)
BEGIN

    IF salary<2000 THEN
            DELETE FROM employees WHERE employees.salary=salary;
    ELSEIF 2000<salary<5000 THEN 
            UPDATE employees SET employees.salary=employees.salary+1000 WHERE employees.salary=salary; 
    ELSE
            UPDATE employees SET employees.salary=employees.salary+500 WHERE employees.salary=salary; 
    END IF;
END //
CALL test_if2(3300.00);
1
2
3
4
5
6
7
8
9
10
11
12
13

# 循环结构

分类:while、loop、repeat

循环控制:

iterate类似于 continue,继续,结束本次循环,继续下一次

leave 类似于 break,跳出,结束当前所在的循环

只能放在begin end中

标签:用上循环控制就必须要加

# while

[标签:] while 循环条件 do
    循环体;
end while [标签];
1
2
3

批量插入,根据次数插入到admin表中多条记录

USE girls;

DELIMITER //
CREATE OR REPLACE PROCEDURE test_while(IN total INT)
BEGIN

DECLARE i INT DEFAULT 0;

WHILE i<total DO
    INSERT INTO admin VALUES(NULL,CONCAT('员工',i),'8888');
    SET i:=i+1;
END WHILE;

END //
CALL test_while(10);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

批量插入,根据次数插入到admin表中多条记录,如果次数>20则停止 (需要加标签)

DELIMITER //
CREATE OR REPLACE PROCEDURE test_while(IN total INT)
BEGIN

DECLARE i INT DEFAULT 1;

a:WHILE i<=total DO
    IF i>20 THEN LEAVE a;
    END IF;
    INSERT INTO admin VALUES(NULL,CONCAT('员工',i),'8888');
    SET i:=i+1;
END WHILE a;

END //
TRUNCATE TABLE admin;
CALL test_while(100);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

批量插入,根据次数插入到admin表中多条记录,只插入偶数次

DELIMITER //
CREATE OR REPLACE PROCEDURE test_while(IN total INT)
BEGIN

DECLARE i INT DEFAULT 0;

a:WHILE i<=total DO
    SET i:=i+1;
    IF MOD(i,2)!=0 THEN ITERATE a;
    END IF;
    INSERT INTO admin VALUES(NULL,CONCAT('员工',i),'8888');

END WHILE a;

END //

TRUNCATE TABLE admin;
CALL test_while(20);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# loop

[标签:] loop

    循环体;

end loop [标签];

1
2
3
4
5
6

# repeat

[标签] repeat
    循环体;
until 结束循环的条件
end repeat [标签];
1
2
3
4