写在前面,课程来自李玉婷老师。

1.初始MySQL

1.1 基本概念和操作

保存数据的容器

1.数组——内存

2.集合——内存

3.文件

……

数据库的好处

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

数据库的相关概念

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

数据库存储数据的特点

1.将数据放到表中,表再放到库中

2.一个数据库中可以有多个表,每个表都有一个名字,用来标识自己。表名具有唯一性。

3.表具有一些特性,此些特性定义了数据库在表中如何存储,类似Java中“类”的设计。

4.表由列组成,即字段。所有表都是由一个或多个列组成的,每一个类似java中的“属性”。

5.表中是数据时按行存储的,每一行类似于java中的“对象”。

数据库服务端的登录和退出

登录:

方式1:

​ 通过Windows自带的客户端

​ 打开命令提示符,输入

mysql (-h主机名 -P端口号) -u用户名 -p密码

​ mysql -h localhost -P 3306 -u root -p

​ 按回车以后输入密码

方式2:

​ mysql自带的客户端

​ 只限于root用户

退出:

​ exit回车即可

mysql的常见命令

显示数据库:

show database;

ps:Database是系统自带的数据库,里面有4个库

information_shema——-存储源数据

mysql—————————存储用户数据

performance_schama—-存储姓名

test——————————-测试数据,暂时为空

进入仓库查看数据:

1.use 仓库名;

2.show tables;

例如 :use sys;

再如:

查看其他库的所有表

show tablas from mysql;

查看现在在哪个库里:

select database();

创建表:

语法:

1
2
3
4
create table 表名(
列名,列类型;
列名,列类型;
)

查看表结构:

desc 表名;

查看服务器版本:

方法1:先退出mysql,再输入:

1
2
3
mysql -V;
//或者
mysql -version

方法2:登录mysql,语法:

select version();

语法规范:

  1. 不区分大小写,但建议关键字大写,表名、列名小写

  2. 每条命令最好用分号结尾

  3. 每条命令可以根据需要进行缩进或换行

  4. 注释

    ​ 单行注释:#注释文字

    · 单行注释:– 注释文字(注意有空格)

    ​ 多行注释:/* 注释文字 */#

2. DQL语言

2.1 基础查询

快速复制表:查询创建工具,勾选即可。(针对navicat用户)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

/*
语法:
SELECT 查询列表 from 表名;

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


#1.查询表中的单个字段
SELECT last_name from employees;

#2.查询表中的多个字段
select last_name,salary,email from employees;

#3.查询表中的所有字段
#方式1:
SELECT
employees.employee_id,
employees.first_name,
employees.last_name,
employees.phone_number,
employees.job_id,
employees.salary,
employees.commission_pct,
employees.manager_id,
employees.department_id,
employees.hiredate,
employees.email,
jobs.job_id,
jobs.job_title,
jobs.min_salary,
jobs.max_salary
FROM
employees
INNER JOIN jobs ON employees.job_id = jobs.job_id;

#方式2:
#*代表全部字段(不足:查询到表的顺序和原始表一模一样)
select * from employees;

执行:快捷键—-F9

查询时注意的细节:

  1. 需要打开某个库,再查询

    语法:USE 库名;

    1
    UES myemployee;

    2.着重号:用于区分字段和关键字 ``其上档符号是“~ ”,使用Shift键可以换挡输入。

    3.查询常量

    1
    2
    3
    4
    USE myemployees;

    SELECT 'John'; #注意这里是单引号不是着重号!!!
    SELECT 100;

    注意:字符型和日期型的常量值必须用单引号括起来

    4.查询表达式

    1
    SELECT 100%98;

    5.查询函数

    1
    SELECT VERSION();

    6.起别名

    1
    2
    3
    4
    5
    6
    7
    #方式一:使用as
    SELECT 100%98 AS 结果;
    SELECT last_name AS 姓,first_name AS 名 FROM employees;

    #方式二:使用空格
    SELECT last_name 姓,first_name 名 FROM employees;

    起别名的意义:

  • 便于理解。

    • 如果要查询的字段有重名的情况,使用别名可以区分开来。
    1
    2
    #如果别名中含有关键字或者空格或者"#",建议使用"括起来,如:
    SELECT salary AS "OUT PUT" FROM employees;

    7.去重(即去除重复的字段)

    不去重写法:

    1
    SELECT department_id FROM employees;

    去重:加上关键字 DISTINCT

    1
    2
    #查询员工表中涉及到的所有的部门编号
    SELECT DISTINCT department_id FROM employees;

    8.加号的作用:mysql中的加号只有一个功能:运算符

    • 如果两个操作符都为整数值,则做加法运算

    • 其一字符型,试图将字符型数值转换为数值型,若成功,就继续做加法运算,如果失败,则将字符型数值转换成0

    • 只要其中一方为NULL,则结果为NULL

    案例:查询员工名和姓连接成一个字段,并显示为姓名。

    解决:引入关键字 CONCAT

    1
    2
    3
    4
    5
    6
    SELECT CONCAT('a','b','c') AS 结果;

    SELECT
    CONCAT(last_name,first_name) AS 姓名
    FROM
    employees;

    题目:显示出表employee的全部列,各个列之间用逗号隔连接,列头显示成out_put.

    如果我们直接这样写:

    1
    2
    3
    4
    5
    6
    SELECT
    CONCAT(employees.first_name,',',employees.last_name,',',employees.job_id,',',employees.commission_pct) AS OUT_PUT

    FROM
    employees

    结果如下:

    可以看到,即使有些地方commission_pct不等于NULL,结果也全为NULL了。

    解决办法:引入IFNULL函数

    函数原型:ifnull(expr1,expr2)

    expr1是可能为NULL的值,expr2是假如expr1真的为NULL了,你所期待函数返回的值。

    正确写法:

    1
    2
    3
    4
    5
    6
    SELECT
    CONCAT(employees.first_name,',',employees.last_name,',',employees.job_id,',',IFNULL(employees.commission_pct,0)) AS OUT_PUT

    FROM
    employees

    查询后可以从结果看出

    即使有时利息为0,但奖金不为0,都可以正常显示

2.2 条件查询

语法:

1
2
3
4
5
6
SELECT
查询列表
FROM
表名
WHERE
筛选条件;

分类:

  1. ​ 按条件表达式筛选

    条件运算符:> < = != <> >= <=

  2. 按逻辑表达式筛选

    && || !

    and or not

  3. 模糊查询

    like,between and in is null

    特点:一般和通配符搭配使用。

    通配符:

    ​ % 任意多个字符,包含0个字符

    ​ _ 任意单个字符

1.按条件表达式式筛选

案例1:查询工资>12000的员工信息

1
2
3
4
5
6
SELECT
*
FROM
employees
WHERE
salary>12000;

案例2:查询部门编号不等于90号的员工和部门编号

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

2.按逻辑表达式筛选

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

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

案例2:查询部门编号不是在90到110之间,或者工资高于15000的员工信息

1
2
3
4
5
6
7
8
SELECT 
*
FROM
employees
WHERE
department_id<90 OR department_id>110 OR salary>15000;

#或者这样写NOT(department_id>=90 AND department_id<=110) OR salary>15000;

3.模糊查询

like

案例1:查询员工名中包含字符a的员工信息

1
2
3
4
5
6
SELECT 
*
FROM
employees
WHERE
last_name LIKE '%a%';

案例2:查询员工名中第三个字符为n,第五个字符为l的员工名和工资

1
2
3
4
5
6
SELECT 
salary,last_name
FROM
employees
WHERE
last_name LIKE '__n_l%';

案例3:查询员工中第二个字符为_的员工名

1
2
3
4
5
6
7
8
9
10
11
12
13
14
SELECT 
salary,last_name
FROM
employees
WHERE
last_name LIKE '_\_%'; #\是转义字符

#转义字符也可以引用ESCAPE,其中$可以替换为任何符号
SELECT
salary,last_name
FROM
employees
WHERE
last_name LIKE '_$_%' ESCAPE '$';

between and—–提高语句简洁度,包含临界值,临界值不能交换位置

案例1:查询员工编号在100到120之间的员工信息

1
2
3
4
5
6
SELECT 
*
FROM
employees
WHERE
employee_id BETWEEN 100 AND 120;

in——–判断某字段的值是否属于in列表中的某一项

特点:

  • 使用in提高语句简洁度
  • in列表的值类型必须是一致或兼容(不支持通配符)

案例:查询员工的工种编号是 IT_PROG、AD_VP、AD_FRES中的一个员工名和工种编号

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#普通写法
SELECT
last_name,
job_id
FROM
employees
WHERE
job_id = 'IT_PROG' OR job_id = 'AD_VP' OR job_id = 'AD_PRES';

#in写法
SELECT
last_name,
job_id
FROM
employees
WHERE
job_id IN ('IT_PROG','AD_VP','AD_PRES');

is null

注意:

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

案例1:查询没有奖金的员工名和奖金率

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

案例2:查询有奖金的员工名和奖金率

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

补充:

安全等于 <=> ——–用来判断是否相等

案例1:查询没有奖金的员工名和奖金率

1
2
3
4
5
6
7
SELECT 
last_name,
commission_pct
FROM
employees
WHERE
commission_pct <=> NULL;

案例2:查询工资为12000的员工信息

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

is null 和 <=> 的区别

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

案例:查询员工号为176的员工的姓名和部门号和年薪

1
2
3
4
5
6
7
8
SELECT 
last_name,
department_id,
salary*12*(1+IFNULL(commission_pct,0)) AS 年薪
FROM
employees
WHERE
department_id <=> 176;

2.3 排序查询

语法:

1
2
3
4
select 查询列表
from 表
【where 筛选条件】
order by 排序列表 【asc|desc】

ps:

  • asc代表升序,desc代表降序。如果不写,默认是升序。
  • oder by子句中可以支持单个字段,多个字段,表达式,函数,别名
  • oder by一般放在查询语句的最后面,但limit子句除外

案例1:查询员工信息,要求工资从高到低排序

1
SELECT * FROM employees ORDER BY salary DESC;

案例2:查询员工信息,要求工资从低到高排序

1
SELECT * FROM employees ORDER BY salary ASC; #(或省略ASC)

案例3:查询部门编号>=90的员工信息,按入职时间的先后进行排序。

1
SELECT * FROM employees WHERE department_id>=90 ORDER BY ASC;

案例4:按年薪的高低显示员工的信息和和年薪———-按表达式排序

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

案例5:按年薪的高低显示员工的信息和和年薪———-按别名排序

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

案例6:按姓名的长度显示员工的姓名和工资————-按函数排序

1
2
3
SELECT LENGTH(last_name) as 字节长度,last_name,salary
FROM employees
ORDER BY LENGTH(last_name) DESC;

案例7:查询员工信息,要求先按工资排序,再按员工编号降序————按多个字段排序

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

2.4 常见函数

概念:类似于java中的方法,将一组逻辑语句封装在方法体中,对外暴露方法名

好处:

  • 隐藏了实现细节
  • 提高代码的重用性

调用:select 函数名() [from 表];

特点:

  • 叫什么(函数名)
  • 干什么(函数功能)

分类:

  • 单行函数,eg:concat,length,ifnull等
  • 分组函数
    • 功能:做统计使用,又称为统计函数,聚合函数,组函数

2.4.1 字符函数

  1. length——-获取参数值的字节个数
1
2
SELECT LENGTH('john');       #返回值4个字节
SELECT LENGTH('张三丰hahaha');#返回值15个字节,因为一个汉字占3个字节
  1. concat——拼接字符串
1
SELECT CONCAT(last_name,'_',first_name) FROM employees;

​ 3.upper、lower

1
2
SELECT UPPER('john');
SELECT LOWER('joHn');

示例:将姓变大写,名变小写,然后拼接

1
SELECT CONCAT(UPPER(last_name),LOWER(first_name)) 姓名 FROM employees;

​ 4.substr、substring——-截取字符

1
2
SELECT SUBSTR('花花的世界',4) out_put; #截取从索引处后面所有字符
SELECT SUBSTR('花花的世界',1,2) out_put;#截取从指定索引处指定字符长度的字符

ps: sql语言索引从1开始

输出:

结果1 结果2

out_put out_put

世界 花花

案例:姓名中首字符大写,其他字符小写然后用_拼接,显示出来

1
SELECT CONCAT(UPPER(SUBSTR(last_name,1,1)),'_',LOWER(SUBSTR(last_name,2))) out_put FROM employees;

​ 5.instr——-返回子串第一次出现的索引,提供找不到返回0

1
SELECT INSTR('我爱米切氏凤头鹦鹉','鹦鹉') AS out_put; #返回8

​ 6.trim

1
2
3
4
SELECT LENGTH(TRIM('     花花     ')) as out_put;  #返回6,说明去掉了空格
SELECT TRIM('a' FROM 'aaaaaa花aaaa花aaaaaaa') as out_put;#输出:花aaaa花--只去掉前后的a

SELECT TRIM('aa' FROM 'aaaaaaa花aaaa花aaaaaaa') as out_put;# 输出:a花aaaa花a 成对删除,单一的剩下

​ 7.lpad——–用指定的字符实现左填充指定长度

1
SELECT LPAD('花花',10,'*') as out_put;  #输出:********花花(总共10个字符)

​ 8.rpad——-用指定的字符实现右填充指定长度

1
SELECT RPAD('花花',12,'ab') as out_put; #输出:花花ababababab

​ 9.replace——替换

1
SELECT REPLACE('我爱惨了鹦鹉鹦鹉鹦鹉','鹦鹉','二哈') as out_put; #输出:我爱惨了二哈二哈二哈

2.4.2 数学函数

​ 1.round—–四舍五入

1
2
SELECT ROUND(-1.55); #输出: -2
SELECT ROUND(1.576,2); #输出:1.58

​ 2.ceil——向上取整,返回>=该参数的最小整数

1
SELECT CEIL(-1.02);  #输出结果:-1

​ 3.floor—向下取整,返回<=该参数的最大整数

1
SELECT FLOOR(9.99);  #输出结果:9

​ 4.truncate—–截断

1
SELECT TRUNCATE(1.699999999,1);  #输出结果:1.6

参数表示保留几位小数

​ 5.mod———–取余

1
2
SELECT MOD(10,-3); #结果:1
SELECT 10%-3; #相同运算,符号和被除数相同

ps:mod(a,b)的运算: a-a/b*b

2.4.3 日期函数

  • now 返回当前系统日期+时间
1
SELECT NOW();
  • curdate 返回当前系统日期,不包含时间
1
SELECT CURDATE();
  • curtime 返回当前时间,不包含日期
1
SELECT CURTIME();
  • 可以获取指定的部分,年,月,日,小时,分钟,秒
1
2
3
SELECT YEAR(now()) 年;
SELECT YEAR('1998-1-1') 年;
SELECT YEAR(hiredate) 年 FROM employees;
  • 获取英文的月份
1
SELECT MONTHNAME(NOW()) 月;

out_put:

February

  • 日期转换为特定格式
序号 格式符 功能
1 %Y 四位的年份
2 %y 二位的年份
3 %m 月份(01,02,…11,12)
4 %c 月份(1,2,…11,12)
5 %d 日(01,02…)
6 %H 小时(24小时制)
7 %h 小时(12小时制)
8 %i 分钟(00,01…59)
9 %s 秒(00,01,…59)

1.str_to_date 将日期格式的字符转换成指定格式的日期

2021-02-18

1
2
3
SELECT STR_TO_DATE('2021-2-18','%Y-%m-%d') AS out_put;
SELECT * FROM employees WHERE hiredate = '1992-4-3';
SELECT * from employees WHERE hiredate = STR_TO_DATE('4-3 1992','%c-%d %Y');

2.date_format 将日期转换成字符————-2021年02月18日

1
SELECT DATE_FORMAT(NOW(),'%y年%m月%d日') AS out_put;

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

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

2.4.4 其他函数

1
2
3
4
5
6
7
8
9
SELECT VERSION();#当前数据库服务器的版本

SELECT DATABASE();#当前打开的数据库

SELECT USER();#当前用户

PASSWORD('字符');#返回该字符的密码形式

md5('字符'):返回该字符的md5加密形式

2.4.5 流程控制函数

  • if函数:if else 效果
1
2
3
4
SELECT IF(10<5,'大','小');

SELECT last_name,commission_pct,IF(commission_pct is null,'没奖金,呵呵','有奖金,嘻嘻') 备注
FROM employees;
  • case 函数的使用一:switch case 效果

语法:case 要判断的字段或表达式

​ when 常量1 then 要显示的值1或语句1

​ when 常量2 then 要显示的值2或语句2

​ …

​ else 要显示的值n或语句n;

​ end

案例:查询员工工资,要求

部门号=30,显示的工资为1.1倍

部门号=40,显示的工资为1.2倍

部门号=50,显示的工资为1.3倍

其他部门,显示的工资为原工资

1
2
3
4
5
6
7
8
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;
  • case函数的使用二:类似于 多重if

语法:case

​ when 条件1 then 要显示的值1或语句1

​ when 条件2 then 要显示的值2或语句2

​ …

​ eles 要显示的值n或语句n;

​ end

案例:查询员工的工资情况

如果工资>20000,显示A级别

如果工资>15000,显示B级别

如果工资>10000,显示C级别

否则,显示D级别

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

2.5 分组函数

分类:

sum 求和

avg 平均值

max 最大值

min 最小值

count 计算个数

1.简单使用

1
2
3
4
5
6
7
8
SELECT SUM(salary) FROM employees;
SELECT AVG(salary) FROM employees;
SELECT MAX(salary) FROM employees;
SELECT MIN(salary) FROM employees;
SELECT COUNT(salary) FROM employees;

SELECT SUM(salary) 和,ROUND(AVG(salary),2) 平均,MAX(salary) 最高,MIN(salary) 最低,COUNT(salary) 个数
FROM employees;

2.参数类型

特点:

  • sum,avg一般用于处理数值型

  • MAX,MIN、COUNT可以处理任何类型

3.是否忽略null值

  • 以上分组函数均忽略null值

4.和distinct搭配

1
2
3
4
5
SELECT SUM(DISTINCT salary),SUM(salary) FROM employees;
SELECT COUNT(DISTINCT salary),COUNT(salary) FROM employees;

#或者
SELECT count(1) FROM employees;

5.count函数的详细介绍

统计行数

1
SELECT count(*) FROM employees;

效率:

MYISAM存储引擎下,count(*)效率高

INNODB存储引擎下,count(*)和count(1)效率差不多,比count(字段)要高

6.和分组函数一同查询的字段有限制

和分组函数一同使用的字段要求是group by后的字段

2.6 分组查询

语法:

select 分组函数,列(要求出现在group by后面)

from 表

【where 筛选条件】

group by 分组的列表

【order by 字句】

PS:要求查询列表必须特殊,要求是分组函数和group by后面出现的字段

特点:

  • 分组查询中的筛选条件分为两类
数据源 位置 关键字
分组前筛选 原始表 group by子句前面 where
分组后筛选 分组后的结果表 group by子句后面 having
  • 分组函数做条件一定要放在having子句中
  • 能用分组前筛选优先分组前筛选
  • group by子句支持单个字段分组,多个字段分组(多个字段之间用逗号隔开没有顺序要求)
  • 也可以添加排序(排序放在整个分组查询的最后)

1.简单查询

案例:查询每个工种的最高工资

1
2
3
SELECT MAX(salary),job_id
FROM employees
GROUP BY job_id;

案例2:查询每个位置上的部门个数

1
2
3
SELECT COUNT(*),location_id
FROM departments
group by location_id;

2.添加筛选条件

案例:查询邮箱中包含a字符的,每个部门的平均工资

1
2
3
4
SELECT AVG(salary),department_id
FROM employees
WHERE email Like '%a%'
GROUP BY department_id;

案例2:查询有奖金的每个领导手下员工的最高工资

1
2
3
4
SELECT MAX(salary),manager_id
FROM employees
WHERE commission_pct is not null
GROUP BY manager_id;

3.添加复杂的筛选条件

案例:查询哪个部门的员工个数>2

1
2
3
4
5
6
#先查询每个部门的员工个数
#根据上述结果进行筛选,查询哪个部门的员工个数>2
SELECT COUNT(*),department_id
FROM employees
GROUP BY department_id
HAVING count(*)>2;

案例2:查询每个工种有奖金的员工的最高工资>12000的工种编号和最高工资

1
2
3
4
5
SELECT MAX(salary),job_id
FROM employees
WHERE commission_pct is not null
GROUP BY job_id
having MAX(salary)>12000;

案例3:查询领导编号>102的每个领导手下的最低工资>5000的领导编号,以及最低工资

1
2
3
4
5
SELECT MIN(salary),manager_id
FROM employees
WHERE manager_id>102
GROUP BY manager_id
HAVING MIN(salary)>5000;

4.按表达式或函数分组

案例:按员工姓名的长度分组,查询每一个员工个数,筛选员工个数>5的有哪些

1
2
3
4
SELECT COUNT(*),LENGTH(last_name) len_name
FROM employees
GROUP BY LENGTH(last_name)
HAVING COUNT(*)>5;

5.按多个字段进行分组

案例:查询每个部门每个工种的员工平均工资

1
2
3
SELECT AVG(salary),department_id,job_id
FROM employees
GROUP BY department_id,job_id;

6.添加排序

案例:查询每个部门每个工种的员工平均工资,并按平均工资的高低显示

1
2
3
4
5
6
SELECT AVG(salary),department_id,job_id
FROM employees
WHERE department_id IS NOT NULL
GROUP BY job_id,department_id
HAVING AVG(salary)>10000
ORDER BY AVG(salary) DESC

2.7 连接查询

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

笛卡尔乘积现象:表1 有m行,表2有n行,结果=m*n行

发生原因:没有有效的连接条件

避免措施:添加有效的连接条件

1
2
SELECT NAME,boyName FROM boys,beauty
WHERE beauty.boyfriend_id=boys.id;

分类:

按年代分类:

sq192标准:仅仅支持内连接

sq199标准:[推荐]支持内连接,外连接(左外和右外)+交叉连接

按功能分类:

内连接 外连接 交叉连接
等值连接 左外连接 交叉连接
非等值连接 右外链接
自连接 全外连接

sq192标准

1.等值连接

特点:

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

案例:查询女孩名和对应的男孩名

1
2
3
SELECT NAME,boyName
FROM boys,beauty
WHERE beauty.boyfriend_id=boys.id;

2.为表起别名

优点:

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

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

案例2:查询员工名和对应的部门名

1
2
3
SELECT e.last_name,e.job_id,job_title
FROM employees e,jobs j
WHERE e.job_id=j.job_id;

3.两个表的顺序可以调换

案例:上述案例

1
2
3
SELECT e.last_name,e.job_id,job_title
FROM jobs j,employees e
WHERE e.job_id=j.job_id;

3.可以加筛选

案例:查询有奖金的员工名,部门名

1
2
3
4
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;

案例2:查询城市名中第二个字符为o的部门名和城市名

1
2
3
4
SELECT city,department_name
FROM departments d,locations l
WHERE d.location_id=l.location_id
AND city LIKE '_o%';

5.可以分组

案例1:查询每个城市的部门个数

1
2
3
4
SELECT COUNT(*) 个数,city
FROM departments d,locations l
WHERE d.`location_id`=l.`location_id`
GROUP BY city;

案例2:查询有奖金的每个部门的部门名和部门的领导编号和该部门的最低工资

1
2
3
4
5
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;

6.可以加排序

案例:查询每个工种名和员工的个数,并且按员工个数降序

1
2
3
4
5
SELECT job_title,COUNT(*)
FROM employees e,jobs j
WHERE e.job_id=j.job_id
GROUP BY job_title
ORDER BY COUNT(*) DESC;

7.可以实现三表连接

查询:查询员工名、部门名和所在城市

1
2
3
4
SELECT last_name,department_name,city
FROM employees e,departments d,locations l
WHERE e.employee_id=d.department_id
AND d.location_id=l.location_id;

在此基础上可以继续追加筛选和排序

1
2
AND city like 's%';
ORDER BY department_name DESC;

非等值连接

案例:查询员工的工资和工资级别

1
2
3
4
SELECT salary,grade_level
FROM employees e,job_grades g
WHERE salary BETWEEN g.lowest_sal AND g.highest_sal
AND g.grade_level='A';

3.自连接

案例:查询员工名和上级的名称

1
2
3
SELECT e.employee_id,e.last_name,m.employee_id,m.last_name
FROM employees e,employees m
WHERE e.department_id=m.employee_id;

sq199语法

语法:

select 查询列表

from 表1 别名 【连接类型】

join 表2 别名

on 连接条件

【where 筛选条件】

【group by 分组】

【having 筛选条件】

【order by 排序条件】

分类:

内连接 inner
外连接 左外 left[outer]
右外 right[outer]
全外 full[outer]
交叉连接 cross

(1)内连接

语法:

select 查询列表

from 表名1 别名

inner join 表2 别名

on 连接条件

1.等值连接

特点:

  • 添加排序、分组、筛选
  • inner可以省略
  • 筛选条件放在where后面,连接条件放在on后面,提高分离性,便于阅读
  • inner join连接和sq192语法中的等值连接效果一样,都是查询多表的交集

案例1:查询员工名,部门名

1
2
3
4
SELECT last_name,department_name
FROM employees e
INNER JOIN departments d
on e.department_id = d.department_id;

案例2:查询名字中包含e的员工名和工种名(添加筛选)

1
2
3
4
5
SELECT last_name,job_title
FROM employees e
INNER JOIN jobs j
ON e.job_id=j.job_id
WHERE e.last_name LIKE '%e%';

案例3:查询部门个数>3的城市和部门个数(添加+筛选)

1
2
3
4
5
6
SELECT city,COUNT(*) 部门个数
FROM locations l
INNER JOIN departments d
ON l.location_id=d.location_id
GROUP BY city
having COUNT(*)>3;

案例4:查询哪个部门的 员工个数>3的部门名和员工个数,并按个数降序

1
2
3
4
5
6
7
SELECT department_name,count(*) 员工个数
FROM departments d
INNER JOIN employees e
WHERE d.department_id=e.department_id
GROUP BY d.department_name
having COUNT(*)>3
ORDER BY count(*) DESC;

案例5:查询员工名、部门名、工种名、并按部门名降序()—-多表连接

1
2
3
4
5
SELECT last_name,department_name,job_title
FROM employees e
INNER JOIN departments d on e.department_id=d.department_id
INNER JOIN jobs j on e.job_id=j.job_id
ORDER BY d.department_name DESC;

2. 非等值连接

#查询员工的工资级别

1
2
3
4
SELECT salary,grade_level
FROM employees e
INNER JOIN job_grades g
ON e.salary BETWEEN g.lowest_sal AND g.highest_sal;

#查询员工的工资级别个数>20的个数,并且按工资级别降序

1
2
3
4
5
6
7
SELECT count(*),grade_level
FROM employees e
INNER JOIN job_grades g
ON e.salary BETWEEN g.lowest_sal AND g.highest_sal
GROUP BY grade_level
HAVING count(*)>20
ORDER BY grade_level DESC;
  1. #查询姓名中包含字符k的员工姓名、上级的名字
1
2
3
4
5
SELECT e.last_name,m.last_name
FROM employees e
JOIN employees m #inner可以省略
ON e.manager_id=m.employee_id;
WHERE e.last_name LIKE '%k%';

(2)外连接

应用场景:用于查询一个表中有,另一个表没有的记录

特点:

  • 外连接的查询结果为主表中的所有记录,如果从表中有和它匹配的,则显示匹配的值;否则,显示null

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

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

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

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

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

案例:查询男朋友不在男神表的女神名字

左外连接:

1
2
3
4
5
SELECT b.`name`
FROM beauty b
LEFT OUTER JOIN boys bo
ON b.boyfriend_id=bo.id
WHERE bo.id is null;

右外连接:

1
2
3
4
5
SELECT b.`name`
FROM boys bo
RIGHT OUTER JOIN beauty b
ON b.boyfriend_id=bo.id
WHERE bo.id is null;

案例:查询哪个部门没有员工

1
2
3
4
5
SELECT d.*,e.employee_id
FROM departments d
LEFT OUTER JOIN employees e
ON d.department_id=e.department_id
WHERE e.employee_id is null;

全外连接:

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

交叉连接:

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

总结之sq192和sql99异同:

功能:sql99支持的较多

可读性:sql99实现连接条件和筛选条件的分离,可读性较高


补充案例:

查询编号>3的女神的男朋友信息,如果有则列出,没有用null填充

1
2
3
4
5
SELECT b.id,b.`name`,bo.*
FROM beauty b
LEFT OUTER JOIN boys bo
ON b.boyfriend_id=bo.id
WHERE b.id>3;

2.7 子查询

概念:

出现在其他语句中的select语句,称为子查询或内查询

外部的查询语句,称为主查询或外查询

分类:

按子查询出现的位置

select后面 from后面 where或having(☆) exists后面(相关子查询)
仅仅支持标量子查询 支持表子查询 标量子查询(单行) 表子查询
列子查询(多行)
行子查询

按结果的行列不同:

  • 标量子查询(结果集只有一行一列)
  • 列子查询(结果集只有一行多列)
  • 行子查询(结果集只有一行多列)
  • 表子查询(结果集一般为多行多列)

where或having后面

1.标量子查询(单行子查询)

2.列子查询(多行子查询)

3.行子查询(多行多列)

特点:

  • 子查询放在小括号内
  • 子查询一般放在条件右侧
  • 标量子查询,一般搭配着单行操作符使用(> < >= <= = <> )
  • 列子查询,一般搭配着多行操作符使用(in,any/some,all)
2.7.1.标量子查询

案例1:谁的工资比Abel高?

第一步:查询Abel的工资

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

第二步:查询满足的员工信息,满足salary>第一步结果

1
2
3
4
5
6
7
8
9
SELECT *
FROM employees
WHERE salary>(

SELECT salary
FROM employees
WHERE last_name = 'Abel'

);

案例2:返回job_id与141号员工相同,salary比143号员工多的员工姓名,job_id和工资

第一步:查询141号员工的job_id

1
2
3
SELECT job_id
FROM employees
WHERE employee_id = 141;

第二步:查询143号员工的工资

1
2
3
SELECT salary
FROM employees
WHERE employee_id = 143;

第三步:查询员工的姓名,job_id和工资,要求……

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
SELECT last_name,job_id,salary
FROM employees
WHERE job_id = (

SELECT job_id
FROM employees
WHERE employee_id = 141

) AND (

SELECT salary
FROM employees
WHERE employee_id = 143

)
2.7.2.列子查询(多行)

案例:返回location_id是1400或1700的部门中的所有员工姓名

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

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
SELECT last_name
FROM employees
WHERE department_id IN(

SELECT department_id
FROM departments
WHERE location_id IN(1400,1700)

);

#或

SELECT last_name
FROM employees
WHERE department_id =ANY(

SELECT department_id
FROM departments
WHERE location_id IN(1400,1700)

);

#PS:NOT IN == <>ALL

案例2:返回其他工种中比job_id为IT_PROG工种任一工资低的员工的员工号,姓名,job_id以及salary

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

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
SELECT last_name,employee_id,salary
FROM employees
WHERE salary<ANY(

SELECT DISTINCT salary
FROM employees
WHERE job_id = 'IT_PROG'

) AND job_id<>'IT_PROG';

#或

SELECT last_name,employee_id,salary
FROM employees
WHERE salary<(

SELECT MAX(salary)
FROM employees
WHERE job_id = 'IT_PROG'

) AND job_id<>'IT_PROG';

案例3:返回其他工种中比job_id为IT_PROG工种所有工资低的员工的员工号,姓名,job_id以及salary

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
SELECT last_name,employee_id,salary
FROM employees
WHERE salary<ALL(

SELECT DISTINCT salary
FROM employees
WHERE job_id = 'IT_PROG'

) AND job_id<>'IT_PROG';

#或

SELECT last_name,employee_id,salary
FROM employees
WHERE salary<(

SELECT MIN(salary)
FROM employees
WHERE job_id = 'IT_PROG'

) AND job_id<>'IT_PROG';
2.7.3.行子查询

ps:用得少,但是面试会问

案例:查询员工编号最小并且工资最高的员工信息

之前的方式:

①查询最小的员工编号

1
2
SELECT MIN(employee_id)
FROM employees;

②查询最高工资

1
2
SELECT MAX(salary)
FROM employees;

③查询员工信息

1
2
3
4
5
6
7
8
9
10
11
12
13
SELECT *
FROM employee
WHERE employee_id=(

SELECT MIN(employee_id)
FROM employees

) AND salary=(

SELECT MAX(salary)
FROM employees

);

新的方式:

1
2
3
4
5
6
7
8
SELECT *
FROM employees
WHERE (employee_id,salary)=(

SELECT MIN(employee_id),MAX(salary)
FROM employees

);

select后面

案例:查询每个部门的员工个数

1
2
3
4
5
6
7
SELECT d.*,(

SELECT COUNT(*)
FROM employees e
WHERE e.department_id = d.department_id
) 个数
FROM departments d;

案例:查询员工号=102的部门名

1
2
3
4
5
6
7
8
SELECT (

SELECT d.department_name
FROM employees e
INNER JOIN departments d
on d.department_id=e.department_id
WHERE e.employee_id=102
) 部门名;

可以用连接查询写

from后面

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

案例:查询每个部门的平均工资的工资等级

①查询每个部门的平均工资

②连接①的结果和hob_grades表,筛选条件平均工资

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

exists后面

—–(相关子查询)

简单语法:

exitsts(完整的查询语句)

结果:1 or 0

案例1:查询有员工的部门名

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
SELECT department_name
FROM departments d
WHERE EXISTS(
SELECT *
FROM employees e
WHERE d.department_id=e.department_id
)

#用in

SELECT department_name
FROM departments d
WHERE d.department_id IN(

SELECT department_id
FROM employees
)

案例2:查询没有女朋友的男神信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#in
SELECT bo.*
FROM boys bo
WHERE bo.id not in (

SELECT boyfriend_id
FROM beauty
)

#exists

SELECT bo.*
FROM boys bo
WHERE NOT EXISTS(

SELECT boyfriend_id
FROM beauty b
WHERE bo.id=b.boyfriend_id
);

2.7.1 分页查询

应用场景:当要显示的数据,一页显示不全,需要分页提交sql请求

语法:

select 查询列表

from 表

【join type join 表2

where 筛选条件

group by 分组字段

order by 排序的字段】

limit offset,size;

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

特点:

  • limit语句放在查询语句的最后

  • 公式:

    要显示的页数page,每页的条目数size

select 查询列表

from 表

limit (Page-1)*size,size;

案例1:查询前五条员工信息

1
2
3
SELECT * FROM employees LIMIT 0,5;
#或
SELECT * FROM employees LIMIT 5;#(从0开始可以省略)

案例2:查询第11条—-第25条

1
SELECT * FROM employees LIMIT 10,15;

案例3:查询有奖金的员工信息,并且工资较高的前10名显示出来

1
2
3
4
SELECT * 
FROM employees
WHERE commission_pct IS NOT NULL
ORDER BY salary;

案例4:查询平均工资最低的部门信息

①查询各部门的平均工资

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

②求出最低平均工资的部门编号

1
2
3
4
5
SELECT department_id
FROM employees
GROUP BY department_id
ORDER BY AVG(salary)
LIMIT 1;

③查询部门信息

1
2
3
4
5
6
7
8
9
10
11
SELECT *
FROM departments d
WHERE d.department_id=(

SELECT department_id
FROM employees
GROUP BY department_id
ORDER BY AVG(salary)
LIMIT 1

);

案例:查询平均工资最低的部门信息和该部门的平均工资

①查询各部门的平均工资

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

②求出最低平均工资的部门编号

1
2
3
4
5
SELECT AVG(salary),department_id
FROM employees
GROUP BY department_id
ORDER BY AVG(salary)
LIMIT 1;

③查询部门信息

1
2
3
4
5
6
7
8
9
10
11
12
SELECT d.*,ag
FROM departments d
JOIN (

SELECT AVG(salary) ag,department_id
FROM employees
GROUP BY department_id
ORDER BY AVG(salary)
LIMIT 1

) ag_dep
ON d.department_id=ag_dep.department_id;

2.7.2 union联合查询

联合:将多条查询语句的结果合并成一个结果

语法:

查询语句1

union

查询语句2

union

应用场景:

要查询的结果来自多个表,且多个表没有直接的连接关系,但查询的信息一致时

特点:

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

案例:查询中国用户中男性的信息以及外国用户中年男性的用户信息

1
2
3
SELECT id,cname,csex FROM t_ca WHERE csex='男'
union
SELECT t_id,tName,tGender FROM t_ua WHERE tGender='male';

ps:两表没有关联

3. DML语言

简介:数据库操作语言

包含:

  • 插入:insert

  • 修改:update

  • 删除:delete

3.1 插入语句

方式一:

语法:

insert into 表名(列名,……)value(值1,……);

1.插入的值的类型要与列的类型一致或兼容

例如:

1
2
INSERT INTO beauty(id,NAME,sex,borndate,phone,photo,boyfriend_id)
VALUES (13,'王冰冰','女','1994-4-23','1898888888',NULL,2);

2.不可以为null的列必须插入值

3.可以为null的列如何插入值?

way one:直接写null,如上例

way two:列不写,值也不写,如下

1
2
beauty(id,NAME,sex,borndate,phone,boyfriend_id)
VALUES (13,'王冰冰','女','1994-4-23','1898888888',2);

4.列的顺序可以调换

5.列数和值的个数必须一致

6.可以省略列名,默认所有列,而且列的顺序和表中的顺序一致

1

方式2:

语法:

insert into 表名

set 列名=值,…

eg:

1
2
INSERT INTO beauty
set id=19,NAME='刘涛',phone='999';

两种方式对比:

1.方式一支持插入多行,方式二不支持

eg:

1
2
3
4
INSERT INTO beauty
VALUES (23,'王冰冰1','女','1994-4-23','1898888888',NULL,2)
,(24,'王冰冰2','女','1994-4-23','1898888888',NULL,2)
,(25,'王冰冰3','女','1994-4-23','1898888888',NULL,2)

2.方式一支持子查询,方式二不支持

eg:

1
2
INSERT INTO beauty(id,NAME,phone)
SELECT 26,'宋茜','11809866';

eg:

1
2
3
INSERT INTO beauty(id,NAME,phone)
SELECT id,boyName,'1234567'
FROM boys WHERE id<3

3.2 修改语句

1.修改单表的记录

语法:

update 表名

set 列=新值,列=新值,……

where 筛选条件;

2.修改多表的记录

示例:

1.修改单表的记录

案例1:修改beauty表中姓王的女神的电话为13899888899

1
2
UPDATE beauty SET phone='13899888899'
WHERE NAME LIKE '王%';

案例2:

1
2
UPDATE boys SET boyname='张飞',usercp=1000
WHERE id=2;

2.修改多表记录

sql92语法:

update 表1 别名,表2 别名

set 列=值,……

where 连接条件

and 筛选条件;

sql99语法:

update 表1 别名

inner|left|right join 表2 别名

on 连接条件

set 列=值,……

where 筛选条件;

案例1:修改张无忌的女朋友的手机号为114

1
2
3
4
UPDATE boys bo
INNER JOIN beauty b ON bo.id=b.boyfriend_id
SET b.phone='114'
WHERE bo.boyName='张无忌'

案例2:修改没有男朋友的女神的男朋友编号都为2号

1
2
3
4
UPDATA boys bo
RIGHT JOIN beauty b on bo.id=b.boyfriend_id
SET b.boyfriend_id=2
WHERE bo.id IS NULL;

3.3 删除语句

方式一:delete

语法:

1.单表的删除★

delete from 表名 where 筛选条件

案例:删除手机号以9结尾的女神信息

1
DELETE FROM beauty WHERE phone LIKE '%9';

2.多表的删除

sql92语法:

delete 表1的别名,表2的别名

from 表1 别名,表2 别名

where 连接条件

and 筛选条件;

sql99语法:
where 表1的别名,表2的别名

inner|left|right join 表2 别名 on 连接条件

where 筛选条件;

案例:删除张无忌的女朋友信息

1
2
3
4
DELETE b
FROM beauty b
INNER JOIN boys bo ON b.boyfriend_id=bo.id
WHERE bo.boyName='张无忌';

案例2:删除黄晓明的信息以及他女朋友的信息

1
2
3
4
5
DELETE bo,b
FROM boys bo
INNER JOIN beauty b
ON bo.id=b.boyfriend_id
WHERE bo.boyName='黄晓明';

方式二:truncate

语法:truncate table 表名;

案例:将魅力值打与100的男神信息删除

1
TRUNCATE TABLE boys;#一删全删,用来清空表的

总结:delete和truncate区别

  • delete可以加where条件,truncate不能加
  • truncate删除,效率高一点点
  • 假如要删除的表中有自增长列,如果用delete删除后,再插入数据,自增长列的值从断点开始,而truncate删除后,再插入数据,自增长列的值从1开始
  • truncate删除没有返回值,delete删除有返回值
  • truncate删除不能回滚,delete删除可以回滚

ps:自增长列在后面会详细讲解

4. TCL语言

Transation Control Language 事务控制语言

事务:

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

事务的ACID(acid)属性

1.原子性(Atomicity)

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

2.一致性(Consistency)

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

3.隔离线(isolation)

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

4.持久性(Durability)

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

4.1 事务的创建

  • 隐式事务:事务没有明显的开启和结束的标记

比如:insert、update、delete语句

  • 显示事务:事务具有明显的开启和结束的标志

前提:必须先设置自动提交功能为禁用

设置禁用:

1
2
SET autocommit=0;
SHOW VARIABLES LIKE 'autocommit'

显示事务书写:

步骤1:开启事务:

set autocommit=0;

start stransaction;(非必须)

步骤2:编写事务中的sql语句(select insert update delete)

语句1;

语句2;

步骤3:结束事务

commit;提交事务

rollback;回滚事务

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#准备工作:
CREATE DATABASE test;
use test;
CREATE TABLE account(
id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(20),
balance DOUBLE
);
INSERT INTO account(username,balance)
VALUES('张无忌',1000),('赵敏',1000);
SELECT * FROM account;

#开启事务
SET autocommit=0;
START TRANSACTION;
#编写一组事务的语句
UPDATE account SET balance = 1000 WHERE username='张无忌';
UPDATE account SET balance = 1000 WHERE username='赵敏';
#结束事务
ROLLBACK;
COMMIT;

SELECT * FROM account;

delete和truncate在事务使用时的区别

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#演示delete
SET autocommit=0;
START TRANSACTION;
DELETE FROM account;
ROLLBACK;
#演示truncate
SET autocommit=0;
START TRANSACTION;
TRUNCATE TABLE account;
ROLLBACK;

#演示savepoint的使用

SET autocommit=0;
START TRANSACTION;
DELETE FROM account WHERE id=1;
SAVEPOINT a;#设置保存点
DELETE FROM account WHERE id=2;
ROLLBACK TO a;#回滚到保存点
SELECT * FROM account;#结果是2号没删1号删了

总结:delete支持回滚,而truncate不支持

savepoint 结点名;设置保存点

事务的隔离级别:

脏读 不可重复读 幻读
read uncommitted
read committed ×
repeatable read × ×
serializable × × ×

mysql中默认第三个隔离级别 repeatable read

Oracle默认第二个隔离级别 read committed

设置隔离级别

set session|global transaction isolation level 隔离级别;

展示当前隔离机制:

select @@transaction_isolation;

设置当前的mySQL连续的隔离级别:

set transaction isolation level read committed;

设置数据库系统的全局的隔离级别:

set global transaction isolation level read committed;

4.2 视图

含义:虚拟的表,和普通表一样使用,是通过表动态生成的数据,只保存sql逻辑,不保存查询结果

比如:舞蹈班和普通班的对比

视图vs表

创建语法的关键字 是否实际占用物理空间 使用
视图 create view 只保存了sql逻辑 增删改查,一般不能增删改
create table 保存了数据 增删改查

应用场景:

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

案例:查询姓张的学生名和专业名

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#以前
SELECT stuname,majorname
FROM stuinfo s
INNER JOIN major m ON s.majorid=m.id
WHERE s.stuname LIKE '张%';

CREATE VIEW v1
AS
SELECT stuname,majorname
FROM stuinfo s
INNER JOIN major m ON s.majorid=m.id;

#现在
SELECT * FROM v1 WHERE stuname LIKE '张%';

创建视图

语法:

create view 视图名

as

查询语句;

特点:

  • 重用sql语句
  • 简化复杂的sql语句,不必 知道它的查询细节
  • 保护数据,提高安全性

案例1:查询员工名包含a字符的员工名、部门名和工种信息

1
2
3
4
5
6
7
8
9
10
#①创建
CREATE VIEW myv1
AS
SELECT last_name,department_name,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;

#②使用
SELECT * FROM myv1 WHERE last_name LIKE '%a%';

案例2:查询每个部门平均工资对应的工资等级

1
2
3
4
5
6
7
8
9
10
11
12
13
#创建视图查看每个部门的平均工资
CREATE VIEW myv2
AS
SELECT AVG(salary) ag,department_id
FROM employees
GROUP BY department_id;

SELECT * FROM myv2;
#使用
SELECT jg.grade_level,department_id,myv2.ag
FROM myv2
JOIN job_grades jg
on myv2.ag BETWEEN jg.lowest_sal and jg.highest_sal;

案例3:查询平均工资最低的部门信息

1
SELECT * FROM myv2 ORDER BY ag LIMIT 1;

案例4:查询平均工资最低的部门名和工资

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#myway:
SELECT d.*,salary
FROM myv2
JOIN employees e
ON e.department_id=myv2.department_id
JOIN departments d
ON d.department_id = e.department_id
ORDER BY ag LIMIT 1;

#teachar's way:

CREATE VIEW myv3
AS
SELECT * FROM myv2 ORDER BY ag LIMIT 1;

SELECT d.*,m.ag
FROM myv3 m
JOIN departments d
ON m.department_id=d.department_id;

视图的修改

方式一:

create or replace view 视图名

as

查询语句;

示例:

1
2
3
4
5
CREATE OR REPLACE VIEW myv3
AS
SELECT AVG(salary),job_id
FROM employees
GROUP BY job_id;

方式二:

语法:

alter view 视图名

as

查询语句;

示例:

1
2
3
ALTER VIEW myv3
AS
SELECT * FROM employees;

删除视图

语法:

drop view 视图名、视图名,…

示例:

1
DROP VIEW myv1,myv2,myv3;

查看视图

1
2
3
DESC myv3;
#或
SHOW create view myv3;

视图的更新

1.插入

1
INSERT INTO myv1 VALUES('张飞','zf@qq.com');

ps:连原表employees的数据一起更新了

2.修改

1
INSERT INTO myv1 VALUES('张飞','zf@qq.com');

ps:也是连原始表一起更新了

3.删除

1
DELETE FROM myv1 WHERE last_name='张无忌';

具备以下特点的视图不允许更新:

1.包含关键字的sql语句:分组函数,distinct、group by、having、union或者union all

2.常量视图

3.select中包含子查询

4.join

5.from一个不能更新的视图

6.where字句中的子查询引用了from字句中的表

5. 变量

分类:

系统变量 自定义变量
全局变量、会话变量 用户变量、局部变量

5.1 系统变量

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

使用语法:

1.查看所有的系统变量

SHOW GLOBAL|[SESSION] VARIABLES;

2.查看满足条件的部分变量

SHOW GLOBAL|[SESSION] VARIABLES LIKE '%CHAR%';

3.查看指定的某个系统变量的值

SELECT @@GLOBAL | [SESSION].系统变量名;

4.为某个系统变量赋值

方式一:

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

方式二:

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

总结:

如果是全局级别,则需要加global;

如果是会话级别,则需要加session

全局变量

说明:

作用域:服务器每次启动将为所有的全局变量赋初始值,针对于所有的会话有效,但不能跨重启

查看所有的全局变量

1
SHOW GLOBAL VARIABLES;

查看部分的全局变量

1
SHOW GLOBAL VARIABLES LIKE '%char%';

查看指定的全局变量的值

1
2
3
SELECT @@global.autocommit;
#查看隔离级别
SELECT @@transaction_isolation;

为某个全局变量赋值

1
SET @@global.autocommit=0;#跨连接有效

会话连接

作用域:仅仅针对于当前会话(连接)有效

查看所有的会话变量

1
SHOW (SESSION) VARIABLES;

查看指定的某个会话变量

1
SHOW SESSION VARIABLES LIKE '%char%';

查看指定的某个会话变量

1
2
SELECT @@transaction_isolation;
SELECT @@SESSION.transaction_isolation;

为某个会话变量赋值

1
2
3
4
#方式一
SET @@session.transaction_isolation='read-uncommitted';
#方式二
SET session transaction_isolation='read-committed';

自定义变量

说明:变量是用户自定义的,不是由系统定义的

使用步骤:

1.声明2.赋值3.使用(查看、比较、运算等)

用户变量

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

应用场合:应用在任何地方,在begin end内也可以在begin end外

赋值的操作符:=或:=

①声明并初始化

SET @用户变量名=值;或

SET @用户变量名:=值;或

SELECT @用户变量名:=值;

②赋值(更新用户变量的值)

方式一:通过SET或SELECT

SET @用户变量名=值;或

SET @用户变量名:=值;或

SELECT @用户变量名:=值;

方式二:通过SELECT INTO

SELECT 字段 INTO 变量名

FROM 表;

③使用(查看用户变量的值)

SELECT @用户变量名;

案例:

1
2
3
4
5
6
7
8
9
#声明并初始化
SET @name='john';
SET @name=100;
SET @count=1;
#赋值
SELECT COUNT(*) INTO @count
FROM employees;
#使用
SELECT @count;

局部变量

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

应用场合:应用在begin end中的第一句话

①声明

DECLARE 变量名 类型;

declare 变量名 类型 DEFAULT 值;

②赋值

方式一:通过SET或SELECT

SET 局部变量名=值;或

SET 局部变量名:=值;或

SELECT @局部变量名:=值;

方式二:通过SELECT INTO

SELECT 字段 INTO 局部变量名

FROM 表;

③使用

SELECT 局部变量名;

对比用户变量和局部变量

作用域 定义和使用的位置 语法
用户变量 当前会话 会话中的任何地方 必须加@符号,不用限定类型
局部变量 BEGIN END中 只能在BEGIN END中,且为第一句话 一般不用加@符号,需要限定类型

案例:声明两个变量并赋初值,求和,并打印

1
2
3
4
5
6
7
8
9
10
11
#1.用户变量
SET @m=1;
SET @n=2;
SET @sum=@m+@n;
SELECT @sum;
#2.局部变量(未放在begin end中报错)
DECLARE m INT DEFAULT 1;
DECLARE n INT DEFAULT 2;
DECLARE SUM INT;
SET SUM=m+n;
SELECT @SUM;

6. 存储过程和函数

存储过程和函数

含义:类似于java中的方法

好处:

1.提高代码的重用性

2.简化操作

存储过程

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

好处:

1.提高代码的重用性

2.简化操作

3.减少了编译次数并且减少了和数据库服务器的连接次数,提高了效率

一、创建语法

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

BEGIN

存储过程体(一组合法的SQL语句)

END

ps:

1.参数列表包含三部分(参数模式 参数名 参数类型)

eg:

1
IN stuname VARCHAR(20);

2.参数模式:

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

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

INOUT:该参数既可以作为输入又可以作为输出

3.如果存储过程体仅仅只有一句话,BEGIN END 可以省略

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

存储过程的结尾可以使用DELIMITER 重新设置

语法:

DELIMITER 结束标记

案例:

1
DELIMITER $

二、调用语法

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

1.空参列表

案例:插入到admin表中五条记录

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


END $
#调用
CALL myp1()$

2.创建一个带in模式参数的存储过程

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

1
2
3
4
5
6
7
8
9
10
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
10
11
12
13
14
DELIMITER W
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 W
#调用
CALL myq3('张飞','8888')W

案例:根据女神名返回对应的男神名

1
2
3
4
5
6
7
8
9
10
11
CREATE PROCEDURE myp5(IN beautyName VARCHAR(20),OUT boyName VARCHAR(20))
BEGIN
SELECT bo.boyName INTO boyName
FROM boys bo
INNER JOIN beauty b ON bo.id = b.boyfriend_id
WHERE b.name=beautyName;
END ;

#调用
CALL myp5('小昭',@bName);
SELECT @bName;

案例:根据女神名,返回对应的男生名和男神魅力值

1
2
3
4
5
6
7
8
9
10
CREATE PROCEDURE myp6(IN beautyName VARCHAR(20),OUT boyName VARCHAR(20),OUT userCP INT)
BEGIN
SELECT bo.boyName,bo.userCP INTO boyName,userCP
FROM boys bo
INNER JOIN beauty b ON bo.id = b.boyfriend_id
WHERE b.name=beautyName;
END ;

CALL myp6('小昭',@bName,@userCP);
SELECT @bName,@userCP;5

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

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

1
2
3
4
5
6
7
8
9
10
11
CREATE PROCEDURE myp8(INOUT a INT,INOUT b INT)
BEGIN
SET a=a*2;
SET b=b*2;
END ;

SET @m=10;
set @n=20;
CALL myp8(@m,@n);

select @m,@n;

删除存储过程

语法:drop procedure 存储过程名

1
DROP PROCEDURE p1;

查看存储过程的信息

1
SHOW CREATE PROCEDURE myp2;

案例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#创建存储过程或函数实现传入一个日期,格式化为××年××月××日
DELIMITER $
CREATE PROCEDURE test_pro4(IN mydata DATETIME,OUT strDate VARCHAR(50))
BEGIN
SELECT DATE_FORMAT(mydata,'%y年%m月%d日') INTO strDate;
END $
call test_pro4(now(),@str);
SELECT @str;

#创建存储过程或函数实现传入女神名称,返回:女神 and 男神 格式的字符串
DELIMITER $
CREATE PROCEDURE test_pro5(IN name varchar(20),OUT str VARCHAR(50))
BEGIN
SELECT concat(name,'and',IFNULL(boyName,'NULL')) into str
from boys bo RIGHT JOIN beauty b on b.boyfriend_id=bo.id
WHERE b.name=name;
END $

call test_pro5('王冰冰',@str);
select @str;

#创建存储过程或函数,根据传入的条目和起始索引,查询beauty表的记录
DELIMITER $
create PROCEDURE test_pro7(IN size INT,IN STARTINDEX INT)
begin
SELECT * from beauty limit STARTINDEX,size;
end$
call test_pro7(3,5);

函数

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

好处:

1.提高代码的重用性

2.简化操作

3.减少了编译次数并且减少了和数据库服务器的连接次数,提高了效率

区别:

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

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

创建语法:

CREATER FUNCTION 函数(参数列表) RETURN<u>S</u> 返回类型

BEGIN

函数体

END

ps:

1.参数列表包含:函数名、函数类型

2.函数体:一定要有return语句,如果没有会报错。

3.函数体中仅有一句话,则可以省略begin end

4.使用delimiter语句设置结束标记

调用语法:

select 函数名(参数列表)

1.无参有返回值

案例:返回公司的员工个数

1
2
3
4
5
6
7
8
9
10
11
12
set global log_bin_trust_function_creators=1;

CREATE FUNCTION myf1() RETURNS INT
BEGIN
DECLARE c INT DEFAULT 0;#定义变量
SELECT COUNT(*) INTO c
FROM employees;
RETURN c;
END ;

SELECT myf1();

2.有参有返回

案例:根据员工名,返回它的工资

1
2
3
4
5
6
7
8
9
10
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');

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

1
2
3
4
5
6
7
8
9
10
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
SHOW CREATE FUNCTION myf3;

删除函数

1
DROP FUNCTION myf3;

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

1
2
3
4
5
6
7
CREATE FUNCTION test_fun1(num1 FLOAT,num2 FLOAT) RETURNS FLOAT
BEGIN
DECLARE SUM FLOAT DEFAULT 0;
SET SUM=num1+num2;
RETURN SUM;
END ;
SELECT test_fun1(1,2);

7. 流程控制结构

分类:

顺序结构:程序从上往下依次 执行

分支结构:程序从两条或多条路径中选择一条去执行

循环结构:程序在满足一定条件的基础上,重复执行一段代码

7.1 分支结构

1.if函数

功能:实现简单的双分支

语法:

IF(表达式1,表达式2,表达式3)

执行顺序:

如果表达式1成立,则if函数返回表示式2的值,否则返回表达式3的值

应用:任何地方

2.case结构

情况1:类似于java中的switch语句,一般用于实现等值判断

语法:

CASE 变量|表达式|字段

WHEN 要判断的值 THEN 返回的值1或语句2

WHEN 要判断的值 THEN 返回的值2或语句2;

……

ELSE 要返回的值n或语句n;

END CASE;

情况2:类似于java中的多重IF语句,一般用于实现区间判断

语法:

CASE

WHEN 要判断的条件1 THEN 返回的值1或语句1;

WHEN 要判断的条件2 THEN 返回的值2或语句2;

……

ELSE 要返回的值n

END CASE;

特点:

  • 可以作为表达式,嵌套在其他语句中使用,可以 放在任何地方,BEGIN END中或BEGIN END的外面

  • 可以作为独立的语句去使用,只能放在BEGIN END中

  • 如果WHEN中的值满足或条件成立,则执行对应的THEN后面的语句,并且结束CASE

如果都不满足,则执行ELSE中的语句或值

  • ELSE语句可以省略,如果省略,并且所有WHEN条件都不满足,则返回NULL

案例:

创建存储过程,根据传入的成绩,来显示等级,比如传入的成绩:90-100,显示B,60-80显示C

1
2
3
4
5
6
7
8
9
10
11
CREATE PROCEDURE test_case(IN score INT)
BEGIN
CASE
WHEN score>=90 AND score<100 THEN SELECT 'A';
WHEN score>=80 THEN SELECT 'B';
WHEN score>=60 THEN SELECT 'C';
ELSE SELECT 'D';
END CASE;
END ;

CALL test_case(95);

3.if结构

功能:实现多重分支

语法:

if 条件1 then 语句1;

elseif 条件2 then 语句2;

……

【else 语句n;】

end if;

应用场景:应用在begin end中

案例:

创建存储过程,根据传入的成绩,来返回等级,比如传入的成绩:90-100,显示A,80-90返回B,60-80返回C

1
2
3
4
5
6
7
8
9
10
11
CREATE FUNCTION test_if(score INT) RETURNS CHAR
BEGIN

IF score>=90 AND score<100 THEN RETURN 'A';
ELSEIF score>=80 THEN RETURN 'B';
ELSEIF score>=60 THEN RETURN 'C';
ELSE RETURN 'D';
END IF;
END ;

select test_if(80);

7.2 循环结构

分类:

while、loop、repeat

循环控制:

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

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

1.while

语法:

【标签:】while 循环条件 do

循环体;

end while 【标签:】

联想:

while(循环条件){

循环体;

}

2.loop

语法:

【标签:】loop

循环体;

end loop【标签】;

可以用来模拟简单的死循环

3.repeat

语法:

【标签:】repeat

循环体;

until 结束循环的条件

end repeat 【标签】;

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

1
2
3
4
5
6
7
8
9
10
11
12
CREATE PROCEDURE pro_while1(IN insertCount INT)
BEGIN
DECLARE I INT DEFAULT 1;
WHILE I<=insertCount DO
INSERT INTO admin(username,password) VALUES(CONCAT('Rose',I),'666');
SET I=I+1;
END WHILE;
END ;

CALL pro_while1(100);

SELECT * FROM admin;

2.添加leave语句

案例:批量插入,根据次数插入到admin表中多条记录,如果次数>20就停止

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
DROP PROCEDURE pro_while1;
CREATE PROCEDURE pro_while1(IN insertCount INT)
BEGIN
DECLARE I INT DEFAULT 1;
a:WHILE I<=insertCount DO
INSERT INTO admin(username,password) VALUES(CONCAT('Huahua',I),'1111');
IF I>=20 THEN LEAVE a;
END IF;
SET I=I+1;
END WHILE a;
END ;

CALL pro_while1(100);

SELECT * FROM admin;

3.添加iterate语句

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
TRUNCATE TABLE admin;
DROP PROCEDURE pro_while1;
CREATE PROCEDURE pro_while1(IN insertCount INT)
BEGIN
DECLARE I INT DEFAULT 0;
a:WHILE I<=insertCount DO
SET I=I+1;
IF MOD(I,2)!=0 THEN ITERATE a;
END IF;
INSERT INTO admin(username,password) VALUES(CONCAT('Huahua',I),'1111');


END WHILE a;
END ;

CALL pro_while1(100);

SELECT * FROM admin;

总结:

名称 特点 位置
while 先判断后执行 begin end中
repeat 先执行后判断
loop 没有条件的死循环

8.触发器

image-20211119210351255