MySQL
MySQL
一,为什么要学习数据库
数据库的好处
- 持久化数据到本地
- 可以实现结构化查询,方便管理
二,数据库相关的概念
DBMS
- 数据库管理系统(Database Management System)。数据库是通过DBMS创建和操作的容器。
DB
- 数据库(database):存储数据的“仓库”。保存了一系列有组织的数据。
SQL
- 结构化查询语言(Structure Query Language):专门用来于数据库通信的语言。
SQL的优点
- 不是某个特定的数据库供应商专有的语言,几乎所有的DBMS都支持SQL
- 简单易学
- 虽然简单,但是实际上是一种强有力的语言,灵活使用其语言元素,可以进行非常复杂和高级的数据库操作。
三, 数据库存储数据的特点
- 将数据放到表中,表再放到库中
- 一个数据库中可以有多个表,每个表都有一个名字,用来表示自己。表名具有唯一性
- 表具有一些特性,这些特性定义了数据库在表中如何存储,类似Java中"类"的设计
- 表有列组成,我们也成为字段。所有表都是由一个或多个列组成,每一列类似Java中的"属性"
- 表中的数据是按行存储的,每一行类似于Java中的"对象"
四, 初始mysql
MySQL产品的介绍
- MySQL数据隶属于MySQL AB公司,总部位于瑞典
- 08年被sun公司收购
- 09年sun被Oracle收购
优点:
- 成本低:开放源代码,一般可以免费使用
- 性能高:执行很快
- 简单:很容易和使用
DBMS分为两类
- 基于共享文件系统的DBMS(Access)
- 基于客户机-服务器的DBMS(MySQL Oracle SqlServlet)
MySQL的目录结构
- bin目录:用于放置一些可执行的文件。
- data目录:用于放置一些日志文件以及数据库。
- include目录:用于放置一些头文件。
- ilb目录:用于放置一系列的库文件。
- share目录:用于存放字符集语言等信息。
- my.ini:是MySQL数据库中使用的配置文件。
- my-huge.ini:适合超大型数据库的配置文件。
- my-large.ini:适合大型数据库的配置文件。
- my-medium.ini:适合中小型数据库的配置文件。
- my-small.ini:适合小型数据库的配置文件。
- my-template.ini:是配置文件的模板,MySQL配置向导将配置文件中选择项写入到my.ini文件中。
- my-innodb-heavy-4G.ini:表示配置文件只对于InnoDB存储引擎有效,而且服务器的内存不能小于4G。
- 需要注意的是,在上述的7个配置文件中,my.ini是MySQL正在使用的配置文件,改文件是一定会被读取的,其他配置文件都是适合不同的数据库配置文件的模板,会在某些特殊情况下被读取的,如果没有特殊需求,只需要配置my.ini文件即可。
MySQL产品的安装
- 属于c/s架构的软件,一般来讲安装服务端
- 企业版
- 社区版
MySQL服务的启动和停止
- 通过命令行
- net start mysql
- net stop mysql
- 方式二:计算机 -- 右击 -- 管理 -- 服务
MySQL服务的登陆和退出
-
登陆:
-
通过MySQL自带的客户端
只限于root用户
-
通过Windows自带的客户端
myslq [-h主机名 -p端口号] -u用户名 -p密码
mysql -h localhost -P 3306 -u root(用户名) -p(密码)
-
-
退出:
exit 或者 CTRL + C
MySQL常见的命令
查看数据库的版本:
- 在myslq客户端:select version();
- 在Windows命令提示符中输入:mysql --version
显示都有那些数据库:show databases ;
使用某个数据库:use 指定的库名;
查看表:show tables; / show tables from 库名称;
查看当前在那个库:select database();
创建表:create table 表名 (id int, name varchar(20));
查看表结构:desc 表名; describe的缩写
查看数据库的编码字符集:show create database 数据库名称;
查看表所有的列:show full columns from 表名;
查看表数据:select * from 表名;
插入行数据:insert into 表名 (列名1,列名2,...) values (值1,值2,...);
修改列操作:update 表名 set 列名='值' where id = ? ;
删除列:delete from 表名 where id=?;
MySQL的语法规范:
-
不区分大小写,但建议关键字大写,表名,列名小写
-
每条命令最好用分号结尾
-
每条命令根据需要,可以进行缩进 或者换行
-
注释
单行注释:#注释文字
单行注释:-- 注释文字
多行注释:/* 注释文字 */
五, DQL语言的学习(Data Query Language)
基础查询
-
语法:
select 查询列表 from 表名;
类似于:System.out.println(打印东西);
-
特点:
- 查询列表可以是:表中的字段,常量值,表达式,函数,也可以是多个
- 查询的结果是一个虚拟的表格
-
查询时注意的细节
- 先去打开指定的库。
- `` 着重号:键盘1旁边的,去掉也是没事的。用来区分是否是一个关键字的。
- 执行的时候选中要执行的命令即可。格式化也是一样的。
1. 查询表中的单个字段
- 格式:select 字段名 from 表名;
SELECT last_name FROM employees;
2. 查询表中的多个字段
-
格式:select 字段名,字段名 from 表名;
-
查询多个使用逗号隔开(英文)最后一个不用添加逗号
SELECT last_name,salary,email FROM employees;
3. 查询表中的所有字段
- 格式:select * from 表名;
SELECT
employee_id,
first_name,
last_name,
email,
phone_number,
job_id,
salary,
commission_pct,
manager_id,
department_id,
hiredate
FROM
employees ;
# * 代表所有字段
SELECT * FROM employees;
4. 查询常量值
- 格式:select 常量值;
- 注意:字符型和日期型的常量必须用单引号引起来,数值型不需要
SELECT 100; #100
SELECT 'john';#john
5. 查询表达式
- 可以使用加,减,乘,除,取余,取模
- 不可以使用加加,减减
SELECT 100%98; #2
6. 查绚函数
- 格式:select 函数(实参列表);
#version() 查看MySQL的版本号
SELECT VERSION();
7. 起别名
注意事项:
- 便于理解。
- 如果要查询的字段有重名的情况,使用起别名可以区分开来
- 如果别名有特殊符号:空格,#号,等等。建议把别名加上双引号/单引号
格式:
- select 查询的字段 as 结果;
# 方式一:使用 as
SELECT 100%98 AS 结果;
SELECT last_name AS 姓,first_name AS 名 FROM employees;
# 方式二:使用空格
SELECT last_name 姓,first_name 名 FROM employees;
#案例:查询salary,显示结果位 out put
SELECT salary AS 'out put' FROM employees;
8. 去重:
- 在字段名的前面加上一个关键字 distinct
- 格式:select distinct 字段名 from 表名;
#案例:查询员工表中涉及到的所有的部门编号
SELECT DISTINCT department_id FROM employees ;
9. +号的作用:
作用(回忆):
- 运算符,两个操作数都为数值型
- 连接符,只要有一个操作数为字符串
MySQL中的+号
仅仅只有一个功能:运算符
select 100+90; 两个操作数都为数值型,则做加法运算
select '123'+90;只要其中一方为字符型,试图将字符型数值转换成数值型。
如果转换成功,则继续做加法运算
select 'john'+90; 如果转换失败,则将字符型值转换成0
select null+10;只要其中一方为null,则结果肯定为null
10. 使用concat函数实现连接
-
功能:拼接字符
-
案例:查询员工名和性连接成一个字段,并显示位姓名 使用:concat(str,str,...)函数
#select last_name+first_name as 姓名 from employees; #错误❌的
SELECT CONCAT(last_name,first_name ) AS 姓名 FROM employees;
11. ifnull函数
- 功能:判断某子字段或表达式是否为null,如果为null,返回指定的值,否则返回原来的值
select ifnull (commission_pct,0) from employees;
12.isnull函数
- 功能:判断某字段或表达式是否为null,如果是,则返回1,否则返回0.
SELECT ISNULL(commission_pct),commission_pct FROM employees;
条件查询
-
语法:
select 查询列表 from 表名 where 筛选条件; #先执行表名,筛选条件,查询列表。
-
分类:
-
简单条件运算符
条件运算符:> < = !=或<> >= <=
-
按逻辑表达式筛选
逻辑运算符:&& || !
and or not
作用:用于连接条件表达式
&&或and:两个条件都为true,结果为true,反之为false
||或or:只要有一个条件为true,结果为true,反之为false
!或not:如果连接的条件本身为false,结果为true,反之为false
-
模糊查询
-
like
特点:
-
一般和统配符搭配使用,可以判断字符型或数值型
通配符:
% :任意多个字符,包含0个字符
_ :任意单个字符
-
-
between and
注意:
- 使用 between and 可以提高语句的简洁度
- 包含临界值
- 两个临界值不要调换顺序
-
in
含义:判断某字段的值是否属于in列表中的某一项
特点:
- 使用in提高语句简洁度
- in列表的值类型必须一致或兼容
- in括号里面的值不可以使用通配符(因为是等于不是like)
-
is null / is not null
注意:
- =或<>不能用于判断null值
- is null或is not null 可以判断null值
-
<=> 安全等于
is null V/S <=>
IS NULL:仅仅可以判断NULL值,可读性较高,建议使用
<=> :既可以判断NULL值,又可以判断普通的数值,可读性较低 -
-
一,按条件运算符筛选
-
案例1:查询工资>1200的员工信息
SELECT * FROM employees WHERE salary>12000;
-
案例2:查询部门编号不等于90号的员工名和部门编号
SELECT CONCAT(last_name,first_name),department_id FROM employees WHERE department_id <>90;
二,按逻辑表达式筛选
-
案例1:查询工资在1w到2w之间的员工名,工资以及奖金
SELECT last_name,salary,commission_pct FROM employees WHERE salary>=10000 AND salary <=20000;
-
案例2:查询部门编号不是在90到110之间的,或者工资高于15000的员工信息
SELECT * FROM employees # where not(department_id>=90 and department_id<=110) or salary>15000; WHERE department_id <90 OR department_id>110 OR salary >=15000;
三,模糊查询
1. like
-
案例1:查询员工名中包含字符a的员工信息
SELECT * FROM employees WHERE last_name LIKE '%a%';
-
案例2:查询员工名中第三个字符为n,第五个字符为l的员工名和工资
SELECT last_name,salary FROM employees WHERE last_name LIKE '__n_l%'
-
案例3:查询员工名中第二个字符为_的员工名(使用转义字符或使用escape关键字)
SELECT last_name FROM employees WHERE last_name LIKE '_$_%' ESCAPE '$'; #where last_name like '_\_%';
2. between and
-
案例1:查询员工编号在100到120之间的员工信息
SELECT * FROM employees #where employee_id >=100 and employee_id<=120; WHERE employee_id BETWEEN 100 AND 120;
3. in
-
案例1:查询员工的工种编号是 IT_PROG AD_VP AD_PRES 中的一个员工名和工种编号
SELECT last_name,job_id FROM employees #where job_id ='IT_PROG' or job_id ='AD_VP' OR job_id ='AD_PRES'; WHERE job_id IN('IT_PROG','AD_VP','AD_PRES');
4. is null / is not null
-
案例1:查询没有奖金的员工名和奖金率
SELECT last_name,commission_pct FROM employees WHERE commission_pct IS NULL;# 不可以使用=(等号)因为它不可以判断null值 #------------------以下为错误❌ SELECT last_name,commission_pct FROM employees WHERE commission_pct IS 12000;
5.安全等于:<=>
-
案例1:查询没有奖金的员工名和奖金率
SELECT last_name,commission_pct FROM employees WHERE commission_pct <=> NULL;
-
案例2:查询工资为12000的员工信息
SELECT last_name,salary FROM employees WHERE salary <=> 12000;
排序查询
-
引入:
SELECT * FROM employees;
-
语法
select 查询列表 from 表 【where 筛选条件】 order by 排序列表 【asc|desc】;
-
特点
- asc代表的是升序,desc代表的是降序如果不写,默认是升序.
- order by子句中可以支持单个字段,多个字段,表达式,函数,别名.
- order by子句一般是放在查询语句的最后面,limit子句除外.
-
执行顺序:from表,where筛选条件,select查询列表,order by 排序条件
案例:
-
案例1:查询员工信息,要求工资从高到底排序
SELECT * FROM employees ORDER BY salary DESC;
-
案例2:查询部门编号>=90的员工信息,按入职时间的先后进行排序【添加筛选条件】
SELECT * FROM employees WHERE department_id >=90 ORDER BY hiredate ASC;
-
案例3:按年薪的高低显示员工的信息和年薪【按表达式排序】
SELECT *,salary*12*(1+IFNULL(commission_pct,0)) '年薪' FROM employees ORDER BY salary*12*(1+IFNULL(commission_pct,0)) DESC;
-
案例4:按年薪的高低显示员工的信息和年薪【按别名排序】
SELECT *,salary*12*(1+IFNULL(commission_pct,0)) 年薪 FROM employees ORDER BY 年薪 DESC;
-
案例5:按姓名的长度显示员工的姓名和工资【按函数排序】
SELECT LENGTH(last_name) 字节长度,last_name,salary FROM employees ORDER BY LENGTH(last_name) DESC;
-
案例6:查询员工信息,要求先按工资升序,再按员工编号降序【按多个字段排序】
SELECT * FROM employees ORDER BY salary ASC ,employee_id DESC;
常见的函数
类似于Java的方法,将一组逻辑语句封装在方法中,对外暴露方法名
-
好处:1. 隐藏了实现细节 2. 提高代码的重用性
-
调用:select 函数名(实参列表) 【from 表】;
-
特点:
- 叫什么(函数名)
- 干什么(函数功能)
-
分类:
-
单行函数
如:concat,length,ifnull等
-
分组函数
功能:做统计使用,又称为统计函数,聚合函数,组函数
-
-
常见函数:
- 字符函数:
length concat substr instr trim upper lower lpad rpad replace - 数学函数:
round ceil floor truncate mod rand - 日期函数
now curdate curtime year month monthname day hour minute second str_to_date date_faormat datediff monthname - 其他函数:
version database user - 控制函数
if case
- 字符函数:
一,字符函数
-
length 获取参数值的字节个数
SELECT LENGTH('john');#4 SELECT LENGTH('呵呵呵');#9 (utf-8中文占3个字节) # 查看客户端使用的字符集 SHOW VARIABLES LIKE '%char%';
-
concat 拼接字符串
SELECT CONCAT(last_name,'_',first_name) 姓名 FROM employees;
-
upper,lower 变大写边小写
SELECT UPPER('jhon');#JHON SELECT LOWER('jHon');#jhon #实例:将姓变大写,名变小写,然后拼接 SELECT CONCAT(UPPER( last_name),LOWER (first_name)) 姓名 FROM employees;
-
substr,substring
注意:索引从1开始
#截取从指定索引处后面所有字符 SELECT SUBSTR('李莫愁爱上了陆展元',7); out_put;#陆展元 #截取从指定索引处指定字符长度的字符 SELECT SUBSTR('李莫愁爱上了陆展元',1,3); out_put;#李莫愁 #案例:姓名中首字符大写,其他字符小写然后用_拼接,显示出来 SELECT CONCAT( UPPER( SUBSTR(last_name,1,1)),'_',LOWER(SUBSTR(last_name,2)) ) FROM employees;
-
instr 返回子串第一次出现的索引,如果找不到返回0
SELECT INSTR('杨不悔爱上了殷六侠','殷六侠') AS out_put;#7
-
trim 前后去重
SELECT LENGTH(TRIM(' 张翠山 ')) out_put;#9 SELECT TRIM('a' FROM 'aaaaaaa张aa翠aa山aaaaa') out_put ;#张aa翠aa山
-
lpad 使用指定的字符实现左填充指定长度
SELECT LPAD('殷素素',10,"*") AS out_put;#*******殷素素 SELECT LPAD('殷素素',2,"*") AS out_put;#殷素
-
rpad 使用指定的字符实现右填充指定长度
SELECT RPAD('殷素素',12,"ab") AS out_put;#殷素素ababababa SELECT RPAD('殷素素',2,"ab") AS out_put;#殷素
-
replace 替换
SELECT REPLACE('张无忌爱上了周驰若','周驰若','赵敏') out_put;#张无忌爱上了赵敏 SELECT REPLACE('周驰若爱上了周驰若','周驰若','赵敏') out_put;#赵敏爱上了赵敏
二,数学函数
-
round 四舍五入
SELECT ROUND(2.5);#3 SELECT ROUND(-1.4);#-1 SELECT ROUND(-1.5);#-2 SELECT ROUND(1.5767,2);#1.58
-
ceil 向上取整,返回>=该参数的最小整数
SELECT CEIL(1.0001);#2 SELECT CEIL(-1.02);#-1
-
floor 向下取整,返回<=该参数的最大整数
SELECT FLOOR(9.99);#9 SELECT FLOOR(-9.99);#10
-
truncate 截断
SELECT TRUNCATE(1.6999,1);#1,6
-
mod 取余
mod(a,b) : a-a/b*b
mod(10,3) : 10-(10)/(3)*(3)=1
SELECT MOD(10,3);#1 SELECT MOD(-10,3);#-1
-
rand:获取随机数,返回0-1之间的小数
- 若要在i ≤ R ≤ j 这个范围得到一个随机整数R ,需要用到表达式
FLOOR(i + RAND() * (j – i + 1))# 7 到 12 的范围(包括7和12)内得到一个随机整数, 可使用以下语句: SELECT FLOOR(7 + (RAND() * 6));
- 也可以用round四舍五入函数来实现,考虑到最前与最后的取值概率会与中间的不相等,故加上0.5来消除这种概率上的差异,达到均匀分布:
产生[i,j]范围正整数:select round(rand()*(j-i+1)+i-0.5)# 7 到 12 的范围(包括7和12)内得到一个随机整数, 可使用以下语句: SELECT round( (RAND() * 6+6.5));
- 若要在i ≤ R ≤ j 这个范围得到一个随机整数R ,需要用到表达式
三.1,日期函数
-
now 返回当前系统日期+时间
SELECT NOW();
-
curdate 返回当前系统日期,不包含时间
SELECT CURDATE();
-
curtime 返回当前时间,不包含日期
SELECT CURTIME();
-
可以获取指定的部分,年,月,日,小时,分钟,秒
SELECT YEAR(NOW()) 年; SELECT YEAR('2020-12-1') 年; #查询入职时间 SELECT YEAR (hiredate) FROM employees; #查询月份中文 SELECT MONTH(NOW()) 月; #查询月份英文 SELECT MONTHNAME(NOW()) 月; #日day,小时hour,分钟minute ,秒second
-
datediff 两个日期之差
SELECT DATEDIFF(NOW(),'2000-11-08'); SELECT DATEDIFF('2020-1-3','2020-1-10');#-7
三.2,字符日期转换函数
-
str_to_date:将日期格式的字符转换成指定格式的日期
SELECT STR_TO_DATE('9-13-1999','%m-%d-%y');#1999-09-13 #查询入职如期 日期为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');
-
date_format:将日期转换成字符
SELECT DATE_FORMAT('2018/6/6','%Y年%m月%d日');#2018年06月06日 SELECT DATE_FORMAT(NOW(),'%Y年%m月%d日'); #查询有奖金的员工名和入职日期(xx月/xx日 xxxx年) SELECT last_name,DATE_FORMAT( hiredate,'%m月/%d日 %Y年') 入职日期,commission_pct FROM employees WHERE commission_pct IS NOT NULL;
四,其他函数
SELECT VERSION();#查看当前MySQL的版本
SELECT DATABASE();# 查看当前数据库
SELECT USER();# 查看当前用户
select md5('字符')#返回该字符的md5的密码形式
select password('字符');#返回该字符的密码形式
五,流程控制函数
-
if 函数 : if else的效果
格式:if(条件表达式 , 表达式成立返回的值 , 条件表达式不成立返回的值)
SELECT IF(10>5,'大','小');#大 SELECT last_name,commission_pct,IF(commission_pct IS NOT NULL,'有奖金','没奖金') FROM employees ;
-
case函数的使用一:switch case 的效果
-
java中:
switch(变量或表达式){ case 常量1:语句1;break; ... default:语句n;break; }
-
MySQL中:
case 要判断的字段或表达式 when 常量1 then 要显示的值1或语句1; when 常量2 then 要显示的值1或语句2; ... else 要显示的值n或语句n; end
-
案例:查询员工的工资,要求
部门号=30,显示的工资为1.1倍
部门号=40,显示的工资为1.2倍
部门号=50,显示的工资为1.3倍
其他部门,显示的工资为原工资SELECT department_id,salary 原始工资, CASE department_id WHEN 30 THEN salary*1.1 WHEN 40 THEN salary*1.2 WHEN 50 THEN salary*1.3 ELSE salary END 新工资 FROM employees ORDER BY department_id;
-
-
case函数的使用二:类似于 多种if
-
java中:
if(条件1){ 语句1; }else if(条件2){ 语句2; } ... else{ 语句n; }
-
MySQL中:
case when 条件1 then 要显示的值1或语句1 when 条件2 then 要显示的值2或语句2 ... else 要显示的值n或语句n end
-
案例:查询员工的工资的情况
如果工资>20000,显示A级别
如果工资>15000,显示B级别
如果工资>10000,显示C级别
否则,显示D级别SELECT employee_id,salary, CASE WHEN salary>20000 THEN 'A' WHEN salary>15000 THEN 'B' WHEN salary>10000 THEN 'C' ELSE 'D' END 级别 FROM employees ORDER BY salary DESC;
-
分组函数
-
功能:用作统计使用,又称为聚合函数或统计函数或组函数
-
分类:sum求和,avg平均值,max最大值,min最小值,count计算个数
-
特点:
-
sum,avg一般用于处理数值型
-
以上分组函数忽略null值
-
可以和distinct特配实现去重的运算
-
count函数的单独介绍
一般使用count(*)做统计行数
count(1)、count(*)与count(列名)的执行区别 -
和分组函数一同查询的字段要求是 group by 后的字段
-
-
简单的使用
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) 和,AVG(salary) 平均,MAX(salary) 最大,MIN(salary) 最小,COUNT(salary) 个数 FROM employees; SELECT SUM(salary) 和,ROUND(AVG(salary),2)平均,MAX(salary) 最大,MIN(salary) 最小,COUNT(salary) 个数 FROM employees;#平均值保留两位小数
-
参数支持那些类型
SELECT SUM(last_name),AVG(last_name) FROM employees;#不支持 SELECT SUM(hiredate), AVG(hiredate) FROM employees;#不支持 # sum avg 一般用于处理数值型 SELECT MAX(last_name),MIN(last_name) FROM employees; SELECT MAX(hiredate),MIN(hiredate) FROM employees; # max min 支持字符和日期型 SELECT COUNT(commission_pct) FROM employees; SELECT COUNT(last_name) FROM employees; # count 只支持不为null的值
-
是否忽略null
SELECT SUM(commission_pct),AVG(commission_pct),SUM(commission_pct)/35,SUM(commission_pct)/107 FROM employees; SELECT MAX(commission_pct),MIN(commission_pct) FROM employees; SELECT COUNT(commission_pct) FROM employees; SELECT commission_pct FROM employees;
-
和distinct搭配
SELECT SUM(DISTINCT salary),SUM(salary) FROM employees; SELECT COUNT(DISTINCT salary),COUNT(salary) FROM employees;
-
count 函数的详细介绍
SELECT COUNT(salary) FROM employees; SELECT COUNT(*)FROM employees;#统计行数 SELECT COUNT(1)FROM employees;#统计行数 效率: MYISAM 存储引擎下,COUNT(*) 的效率高 INNODB 存储引擎下,COUNT(*)和COUNT(1)的效率差不多,比COUNT(字段)要高一些
-
和分组函数数一同查询的字段有限制
SELECT AVG(salary),employee_idl FROM employees;#意义不大
分组查询
-
语法:
select 分组函数,列(要求出现在group by的后面) ⑤ from 表 ① 【where 筛选条件】 ② group by 分组的列表 ③ 【having 分组后的筛选】 ④ 【order by 子句】 ⑥
执行顺序:from,where,group by,having,select,order by
-
注意:
查询列表必须特殊,要求是分组函数和group by后出现的字段
-
特点:
-
分组查绚中的筛选条件分为两类
数据源 位置 关键字 分组前筛选 原始表 group by子句的前面 where 分组后筛选 分组后的结果集 group by子句的后面 having 分组函数做条件肯定是放在having子句中
能用分组前筛选的,优先考虑使用分组前筛选
-
group by 子句支持单个字段分组,多个字段分组(多个字段之间用逗号隔开没有顺序要求),表达式或函数(用的较少)
-
也可以添加排序(排序放在整个分组查询的最后)
-
引入
#查询每个部门的平均工资
SELECT department_id,AVG(salary) FROM employees
GROUP BY department_id ORDER BY department_id;
#案例1,查询每个工种的最高工资
SELECT MAX(salary),job_id
FROM employees GROUP BY job_id;
#案例2,查询每个位置的部门的个数
SELECT COUNT(*),location_id FROM departments GROUP BY location_id;
添加分组前的筛选条件
#案例1:查询邮箱中包含a字符的,每个部门的平均工资
SELECT AVG(salary),department_id FROM employees
WHERE email LIKE '%a%'
GROUP BY department_id;
#案例2:查询有奖金的每个领导手下员工的最高工资
SELECT MAX(salary),manager_id
FROM employees
WHERE commission_pct IS NOT NULL
GROUP BY employee_id;
添加分组后的筛选条件
#1. 查询每个部门的员工个数
SELECT COUNT(*) a,department_id
FROM employees
GROUP BY department_id;
#2. 根据1的结果进行筛选,查询那个部门的员工个数>2
SELECT COUNT(*) a,department_id
FROM employees
GROUP BY department_id
HAVING a>2;
#案例2:查询每个工种有奖金的员工的做高工资>12000的工种编号和最高工资
#1. 查询每个工种有奖金的员工的最高工资
#2. 根据1结果继续筛选,最高工资>12000
SELECT job_id,MAX(salary) a
FROM employees
WHERE commission_pct IS NOT NULL
GROUP BY job_id
HAVING a>12000;
#案例3:查询领导编号>102的每个领导手下的最低工资>5000的领导编号是哪个,以及其最低工资
#1. 查询每个领导手下的员工固定最低工资
#2. 添加筛选条件:编号>102
#3. 添加筛选条件:最低工资>5000
SELECT MIN(salary) a,manager_id
FROM employees
WHERE manager_id>102
GROUP BY manager_id
HAVING a>5000;
按表达式或函数分组
#案例:按员工姓名的长度分组,查询每一组的员工个数,筛选员工个数>5的有哪些
#1. 查询每个长度的员工个数
#2. 添加筛选条件
SELECT COUNT(*) a,LENGTH(last_name) b
FROM employees
GROUP BY b
HAVING a>5;
按多个字段分组
#案例:查询每个部门每个工种的员工的平均工资
SELECT AVG(salary),department_id,job_id
FROM employees
GROUP BY department_id,job_id;
添加排序
#案例:查询每个部门每个工种的员工的平均工资,并且按平均工资的高低显示
SELECT AVG(salary) a,department_id,job_id
FROM employees
GROUP BY department_id,job_id
HAVING a>10000
ORDER BY a DESC;
练习
USE myemployees;
#1. 查询各job_id的员工工资的最大值,最小值,平均值,总和,并按job_id升序
SELECT job_id,MAX(salary),MIN(salary),ROUND( AVG(salary),2),SUM(salary)
FROM employees
GROUP BY job_id
ORDER BY job_id ASC;
#2. 查询员工最高工资和最低工资的差距
SELECT MAX(salary)-MIN(salary)
FROM employees ;
#3. 查询各个管理者手下员工的最低工资,其中最低工资不能小于6000,没有管理者的员工不计算在内
SELECT MIN(salary) s
FROM employees
WHERE manager_id IS NOT NULL
GROUP BY manager_id
HAVING s>=6000;
#4. 查询所有部门的编号,员工数量和工资平均值,并按平均工资降序
SELECT department_id,COUNT(*),AVG(salary) a
FROM employees
GROUP BY department_id
ORDER BY a DESC;
#5. 选择具有各个job_id的员工人数
SELECT COUNT(*),job_id FROM employees GROUP BY job_id;
连接查询
一,含义
- 当查询中设计到了多个表的字段,需要使用多表连接
- 格式:select 字段1,字段2 from 表1,表2,...;
- 笛卡尔乘积:当查询多个表时,没有添加有效的连接条件,导致多个表所有行实现完全连接
- 如何解决:添加有效的来连接条件
二,分类
按年代分类
-
sql192
等值
非等值
自连接
也支持一部分外连接(用于Oracle,SQL server,MySQL不支持)
-
sql99【推荐使用】
内连接
- 等值
- 非等值
- 自连接
外连接
- 左外
- 右外
- 全外(mysql不支持)
交叉连接
按功能分类
-
内连接
等值连接,非等值连接,自连接
-
外连接:
左外连接,右外连接,全外连接
-
交叉连接:
三,sql92语法
-
等值连接
-
语法:
select 查询列表 from 表1 别名,表2 别名 where 表1.key=表2.key 【and 筛选条件】 【group by 分组字段】 【having 分组后的筛选】 【order by 排序字段】
-
特点:
- 多表等值连接的结果为多表的交集部分
- n表连接,至少需要n-1个连接条件
- 多表的顺序没有要求
- 一般需要为表起别名
- 可以搭配前面介绍的所有子句使用,比如排序,分组,筛选
-
-
非等值连接
-
语法:
select 查询列表 from 表1 别名,表2 别名 where 表1.key=表2.key 【and 筛选条件】 【group by 分组字段】 【having 分组后的筛选】 【order by 排序字段】
-
-
自连接
-
语法:
select 查询列表 from 表1 别名,表2 别名 where 表1.key=表2.key 【and 筛选条件】 【group by 分组字段】 【having 分组后的筛选】 【order by 排序字段】
-
四,sql99语法
语法:
select 查询列表
from 表1 别名
【连接类型】
join 表2 别名
on 连接条件
【where 筛选条件】
【group by 分组】
【having 筛选条件】
【order by 排序列表】
分类:
- 内连接(⭐):inner
- 外连接
- 左外(⭐):left 【outer】
- 右外(⭐):right 【outer】
- 全外:
- 交叉连接:
特点,语法:
-
内连接
语法:
select 查询列表 from 表1 别名 inner join 表2 别名 on 连接条件
分类:等值,非等值,自连接
特点:
- 添加排序,分组,筛选
- inner 可以省略
- 筛选条件放在where 后面,连接条件放在on后面,提高分离性,便于阅读
- inner join连接和sql92语法中的等值连接效果是一样的,都是查询多表的交集
- 表的顺序可以调换
-
外连接
语法:
select 查询列表 from 表1 别名 left|right|full outer join 表2 别名 on 连接条件
应用场景:用于查询一个表中有,另一个表没有的记录
特点:
- 外连接的查询结果为主表中的所有记录
如果从表中有和它匹配的,则显示匹配的值
如果从表中没有和它匹配的,则显示null
外连接查询结果=内连接查询结果+主表中有而从表没有的记录 - 左外连接,left join 左边的是主表
右外连接,right join 右边的是主表 - 左外和右外交换两个表的顺序,可以实现同样的效果
- 全外连接=内连接的结果+表1中有但表2没有的+表2中有但表1没有的
- outer可以省略
- 外连接的查询结果为主表中的所有记录
-
交叉连接
语法:
select 查询列表 from 表1 别名 cross join 表2 别名
特点:使用99语法实现笛卡尔乘积
sql99 和 sql99区别:
- 功能:sql99支持的较多
- 可读性:slq99实现连接条件和筛选条件的分离,可读性
代码实例:
USE girls;
SELECT * FROM beauty;
SELECT * FROM boys;
SELECT NAME,boyName FROM boys,beauty;#这种现象称之为笛卡尔积现象
SELECT NAME,boyName FROM boys,beauty
WHERE boys.id=beauty.boyfriend_id;#连接条件
sql92标准
-
等值连接
-
简单的等值连接
#案例1:查询女神名对应的男神名 SELECT *,boys.`boyName` FROM beauty,boys WHERE beauty.boyfriend_id=boys.id; #案例2:查询员工名对应的部门名 USE myemployees; SELECT last_name,department_name FROM employees,departments WHERE employees.`department_id`=departments.`department_id`;
-
为表起别名
-
提高语句的简洁度
-
区分多个重名的字段
注意:如果为表起了别名,则查询的字段就不能使用原来的表名区限定
#查询员工名,工种号,工种名 SELECT last_name,e.job_id,job_title FROM employees e,jobs j WHERE e.job_id=j.job_id;
-
-
两个表的顺序是否可以调换
#查询员工名,工种号,工种名 SELECT last_name,e.job_id,job_title FROM jobs j,employees e WHERE j.job_id= e.job_id;
-
可以加筛选
#案例:查询有奖金的员工名,部门名 SELECT last_name,department_name FROM employees e,departments d WHERE e.`department_id`=d.`department_id` AND commission_pct IS NOT NULL; #案例2:查询城市中第二个字符为o的部门名和城市名 SELECT department_name,city FROM departments d,locations l WHERE d.`location_id`=l.`location_id` AND l.`city` LIKE '_o%';
-
可以加分组
#案例1:查询每个城市的部门个数 SELECT COUNT(*),city FROM departments d,locations l WHERE d.`location_id`=l.`location_id` GROUP BY city; #案例2:查询有奖金的每个部门的部门名和部门的领导编号和该部门的最低工资 SELECT department_name,d.`manager_id`,MIN(salary) FROM employees e,departments d WHERE e.`department_id`=d.`department_id` AND commission_pct IS NOT NULL GROUP BY e.`department_id`;
-
可以加排序
#案例:查询每个工种的工种名和员工的个数,并且按员工个数降序 SELECT job_title,COUNT(*) a FROM employees e,jobs j WHERE e.`job_id`=j.`job_id` GROUP BY e.`job_id` ORDER BY a DESC;
-
可以实现三表连接
#案例:查询员工名,部门名和所在的城市 SELECT last_name,department_name,city FROM employees e,departments d,locations l WHERE e.department_id=d.`department_id` AND d.`location_id`=l.`location_id`;
-
-
非等值连接
#案例1:查询员工的工资和工资级别 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'; SELECT * FROM job_grades;
-
自连接
#案例:查询员工名和上级的名称 SELECT a.`last_name` 领导,b.`last_name` 员工 FROM employees a,employees b WHERE a.`employee_id`=b.`manager_id`;
sql99
-
内连接
-
等值连接
USE myemployees; #案例1,查询员工名,部门名(调换位置) SELECT last_name, department_name FROM employees e INNER JOIN departments d ON e.`department_id` = d.`department_id` ; #案例2,查询名字包含e的员工名和工种名(筛选) SELECT last_name, job_title FROM employees e INNER JOIN jobs j ON e.`job_id` = j.`job_id` WHERE last_name LIKE '%e%' ; #案例3,查询部门个数大于3的城市名和部门个数(分组+筛选) #1. 查询每个城市的部门个数 #2. 在1结果上筛选满足条件的 SELECT city, COUNT(*) FROM departments d INNER JOIN locations l ON d.`location_id` = l.`location_id` GROUP BY city HAVING COUNT(*) > 3 ; #案例4,查询那个部门的部门员工大于3的部门名和员工个数,并且按个数降序(排序) SELECT department_name, COUNT(*) FROM employees e INNER JOIN departments d ON e.`department_id` = d.`department_id` GROUP BY department_name HAVING COUNT(*) > 3 ORDER BY COUNT(*) DESC ; #案例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 department_name DESC;
-
非等值连接
#查询员工的工资级别 SELECT department_id,salary,grade_level FROM employees e JOIN job_grades j ON j.`lowest_sal`<salary AND salary<j.`highest_sal` #on e.`salary` between j.`lowest_sal` and j.`highest_sal` ORDER BY grade_level ; #查询每个工资级别的个数>20,并且按工资级别降序 SELECT COUNT(*),grade_level FROM employees e JOIN job_grades j ON e.`salary` BETWEEN j.`lowest_sal` AND j.`highest_sal` GROUP BY grade_level HAVING COUNT(*)>20 ORDER BY grade_level DESC;
-
自连接
#查询员工的名字,上级的名字 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%';
-
-
外连接
#引入:查询没有男朋友的女神名 USE girls; SELECT * FROM beauty; SELECT * FROM boys; -- 左外连接 SELECT b.name,bo.* FROM beauty b LEFT OUTER JOIN boys bo ON b.`boyfriend_id`=bo.`id` WHERE bo.`id` IS NULL; -- 右外连接 SELECT b.`name`,a.* FROM boys a RIGHT OUTER JOIN beauty b ON a.`id`=b.`boyfriend_id` WHERE a.`id` IS NULL; #案例1:查询那个部门没有员工 USE myemployees; #左外 部门主表 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; #右外 SELECT d.*,e.`employee_id` FROM employees e RIGHT OUTER JOIN departments d ON d.`department_id`=e.`department_id` WHERE e.`employee_id` IS NULL; #全外 MySQL不支持 USE girls; SELECT b.*,bo.* FROM beauty b FULL OUTER JOIN boys bo ON b.`boyfriend_id`=bo.id;
-
交叉连接
SELECT b.*,bo.* FROM beauty b CROSS JOIN boys bo;
子查询
-
含义:
嵌套在其他语句内部的select语句称为子查询或内查询
外面的语句可以是insert,update,delete,select等,一般select作为外面语句较多
外部的查询语句,称为主查询或外查询
-
分类:
-
按子查询出现的位置:
-
select后面
仅仅标量子查询
-
from 后面
支持表子查询
-
where或having后面 ★
标量子查询 √
列子查询 √
行自查询
-
exists后面
标量子查询
列子查询
行子查询
表子查询
-
-
按结果集的行列数不同:
- 标量子查询(结果集只有一行一列)
- 列子查询(结果集只有多行一列)
- 行子查询(结果集有多行多列)
- 表子查询(结果集一般为多行多列)
-
一,where或having后面
-
支持的查询:
- 标量子查询(单行子查询)
- 列子查询(多行子查询)
- 行子查询(多列多行)
-
特点:
-
子查询放在小括号内
-
子查询一般放在条件的右侧
-
标量子查询,一般搭配着单行操作符使用
> < >= <= <>
列子查询,一般搭配着多行操作符使用
IN,ANY/SOME,ALL
-
子查询的执行优先于主查询执行,主查询的条件用到了主查询的结果
-
二,select 后面
- 仅仅支持标量子查询
三,from后面
- 讲子查询结果充当一张表,要求必须起别名
四,exists后面
-
最终的结果看有没有值,有true,否则false
-
语法:
exists(完整的查询语句)
结果:1或0
代码实例
-
一,where或having后面
-
标量子查询
#案例1:谁的工资比Abel高? #1. 查询abel的工资 SELECT salary FROM employees WHERE last_name ='Abel'; #2. 查询员工的信息,满足salary>1结果 SELECT * fromemployees WHERE salary > (SELECT salary FROM employees WHERE last_name = 'Abel'); #案例2:返回job_id于141号员工相同,salary比143号员工多的员工 姓名,job_id和工资 #1. 查询141号员工的job_id SELECT job_id FROM employees WHERE employee_id=143; #2. 查询143号员工的salary SELECT salary FROM employees WHERE employee_id=143; #3. 查询员工 姓名,job_id和工资,要求job_id=1并且salary>2 SELECT e.last_name,e.job_id,e.salary FROM employees e WHERE e.`job_id`=(SELECT a.job_id FROM employees a WHERE a.`employee_id`=143) AND e.`salary` > ( SELECT b.salary FROM employees b WHERE b.`employee_id`=143); #案例3:返回公司工资最少的员工的last_name,job_id,salary SELECT last_name,job_id,salary FROM employees WHERE salary=(SELECT MIN(salary) FROM employees); #案例4:查询最低工资大于50号部门最低工资的部门id和其最低工资 #1. 查询50号部门的最低工资 SELECT MIN(salary)FROM employees WHERE department_id=50; #2. 查询每个部门的最低工资 SELECT MIN(salary),department_id FROM employees GROUP BY department_id; #3. 在2基础上筛选,满足min(salary)》1 SELECT MIN(salary),department_id FROM employees GROUP BY department_id HAVING MIN(salary)>( SELECT MIN(salary)FROM employees WHERE department_id=50); #非法使用标量子查询 #子查询的结果不是一行一列 SELECT MIN(salary),department_id FROM employees GROUP BY department_id HAVING MIN(salary)>( SELECT salary FROM employees WHERE department_id=250);
-
列子查询(多行子查询)
#案例1,返回location_id是1400或1700的部门中的所有员工姓名 #1. 查询location_id是1400或1700的部门编号 SELECT department_id FROM departments WHERE location_id IN(1400, 1700); #2. 查询员工姓名,要求部门号是1列表中的某一个 SELECT last_name FROM employees WHERE department_id IN (SELECT department_id FROMdepartments WHERE location_id IN (1400, 1700)) ; #案例2:返回其他工种中比job_id为'IT_PROG'工种任意一工资的员工的:工号,姓名,job_id,salary #1. 查询job_id为'IT_PROG'的工资 SELECT DISTINCT salary FROM employees WHERE job_id='IT_PROG'; #2. 查询员工的:工号,姓名,job_id,salary<1的任意一个 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'; #或 SELECT last_name,employee_id,job_id,salary FROM employees WHERE salary < (SELECT DISTINCT MAX(salary) FROM employees WHERE job_id = 'IT_PROG') AND job_id <> 'IT_PROG'; #案例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'; #或 SELECT last_name,employee_id,job_id,salary FROM employees WHERE salary < (SELECT DISTINCT MIN(salary) FROM employees WHERE job_id = 'IT_PROG') AND job_id <> 'IT_PROG'; #3. 行子查询(结果集一行多列或多行多列) #案例:查询员工编号最小并且工资最高的员工信息 SELECT * FROM employees WHERE (employee_id,salary)=( SELECT MIN(employee_id),MAX(salary) FROM employees); #1. 查询最小的员工编号 #2. 查询最高工资 #3. 查询员工信息 SELECT * FROM employees WHERE employee_id = (SELECT MIN(employee_id) FROM employees) AND salary = (SELECT MAX(salary) FROM employees);
-
行子查询(结果集一行多列或多行多列)
#案例:查询员工编号最小并且工资最高的员工信息 SELECT * FROM employees WHERE (employee_id,salary)=(SELECT MIN(employee_id),MAX(salary) FROM employees); #1. 查询最小的员工编号 #2. 查询最高工资 #3. 查询员工信息 SELECT * FROM employees WHERE employee_id = (SELECT MIN(employee_id) FROM employees) AND salary = (SELECT MAX(salary) FROM employees);
-
-
二,select 后面
#案例1:查询每个部门的员工个数 SELECT d.*,( SELECT COUNT(*) FROM employees e WHERE e.department_id=d.`department_id`) FROM departments d; #案例2:查询员工工号=102的部门名 SELECT (SELECT department_name FROM departments d INNER JOIN employees e ON d.department_id=e.department_id WHERE e.employee_id=102) 部门名;
-
三,from后面
#案例:查询每个部门的平均工资的工资等级 #1. 查询每个部门的平均工资 SELECT AVG(salary),department_id FROM employees GROUP BY department_id; #2. 连接1的结果集和job_grades表,筛选条件平均工资 between lowest_sal and highest_sal 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;
-
四,exists后面(相关子查询)
SELECT EXISTS(SELECT employee_id FROM employees);#1 SELECT EXISTS(SELECT employee_id FROM employees WHERE salary =30000);#0 #案例1:查询有员工的部门名 SELECT department_name FROM departments d WHERE EXISTS (SELECT * FROM employees e WHERE e.`department_id`=d.`department_id`); #in SELECT department_name FROM departments d WHERE d.`department_id` IN (SELECT department_id FROM employees); #案例2:查询没有女朋友的男生信息 #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 WHERE bo.`id`=beauty.`boyfriend_id`)
分页查询
-
应用场景:当要显示的数据,一页显示不全,需要分页提交sql请求
-
语法:
select 查询列表 ⑦ from 表 ① 连接类型 join 表2 ② on 连接条件 ③ where 筛选条件 ④ group by 分组字段 ⑤ having 分组后的筛选 ⑥ order by 排序的字段 ⑦ limit 【offset,】size; ⑧ offset要显示条目的起始索引(起始索引从0开始) size要显示的条目个数
-
执行顺序:from,join ,on,where,group by,having,select,order,limit
-
特点:
-
limit语句放在查询语句的最后
-
公式:
要显示的页数page,每页的条目数size
select 查询列表 from 表 (page-1)size ,size;
-
offset可以省略省略默认是0
-
代码示例
#案例1:查询前5条员工信息
SELECT * FROM employees LIMIT 0,5;
SELECT * FROM employees LIMIT 5;#offset可选
#案例2:查询第11条--第25条
SELECT* FROM employees LIMIT 10,15;
#案例3:有奖金的员工信息并且工资较高的前10名显示出来
SELECT * FROM employees
WHERE commission_pct IS NOT NULL
ORDER BY salary DESC
LIMIT 0,10;
union联合查询
-
union联合 合并:将多条查询语句的结果合并成一个结果
-
语法:
查询语句1 union 查询语句2 union 。。。;
-
应用场景:
查询的结果来自于多个表,且多个表没有直接关系,但查询的信息一致
-
特点:
- 要求多条查询语句的列数是一致的!
- 要求多条查询语句的查询的每一列的类型和顺序最好一致
- union 关键字默认去重,如果使用 union all 可以包含重复项
-
意义:
- 将一条比较复杂的查询语句拆分成多条语句
- 适用于多个表的时候,查询的列基本是一致的
代码实例:
#引入案例:查询部门编号>90或邮箱包含a的员工信息
SELECT * FROM employees WHERE email LIKE '%a%' OR department_id>90;
SELECT * FROM employees WHERE email LIKE '%a%'
UNION
SELECT * FROM employees WHERE department_id>90;
#案例:查询中国用户男性的信息以及外国用户男性的信息
SELECT id,cname,csex FROM t_ca WHERE csex='男'
UNION
SELECT t_id,tName,tGender FROM t_ula WHERE tGender='male';
查询总结
select 查询列表 ⑦
from 表1 别名 ①
连接类型 join 表2 ②
on 连接条件 ③
where 筛选条件 ④
group by 分组列表 ⑤
having 筛选 ⑥
order by 排序列表 ⑧
limit 起始条目索引,条目数; ⑨
六, DML语言的学习(Data Manipulation Language)
插入语句
-
方式一:
-
语法:
insert into 表名 (列名,...) values (值1,...);
-
特点:
-
插入的值的类型要于列的类型一致或兼容
-
不可以为null的列必须插入值。可以为null的列如何插入值?
方式一:将该列插入null值
方式二:讲该列省略
-
.列的顺序可以调换
-
列数和值得个数必须一致
-
可以省略列名,默认所有列,而且列的顺序和表中的顺序一致
-
除了数字类型以外,其他类型一律需要使用引号(单引号或双引号)引起来
-
-
-
方式二:
-
语法:
insert into 表名 set 列名=值,列名=值,...;
-
-
两种方式pk
-
方式一支持插入多行,方式二不支持
-
方式一支持子查询(省略INSERT),方式二不支持
insert into beauty(id,name,phone) select 26,'宋希','118';
-
修改语句
-
修改单表的记录
-
语法:
update 表名 set 字段=新值,字段=新值,... where 筛选条件;
-
-
修改多表的记录
-
sql92语法:
update 表1 别名,表2 别名 set 列=值,... where 连接条件 and 筛选条件;
-
sql99语法:
update 表1 别名 inner|left|right join 表2 别名 on 连接条件 set 列=值,... where 筛选条件;
-
-
代码实例:
#1. 修改单表的记录 #案例一:修改beauty表中性唐的女神的电话为13783454548 update beauty set phone ='13783454548' where name like '唐'; #案例2:修改boys表中id号为2的名称为张飞,魅力值10 update boys set boyname='张飞',usercp=10 where id=2; #2. 修改多表的记录 #案例1:修改张无忌的女朋友手机号为114 update boys bo inner join beauty b on bo.`id`=b.`boyfriend_id` set bo.`boyName`='张无忌'; #案例2:修改没有男朋友的女神的男朋友的编号都为2号 update boys bo right join beauty b on bo.`id`=b.`boyfriend_id` set b.`boyfriend_id`=2 where bo.`id` is null; select bo.*,b.* from boys bo right join beauty b on bo.`id`=b.`boyfriend_id`
删除语句
-
delete
-
单表的删除
语法:
delete from 表名 where 筛选条件 limit 条目数;
-
多表的删除
-
sql92:
delete 表1的别名,表2的别名 from 表1 别名,表2 别名 where 连接条件 and 筛选条件;
-
sql99:
delete 表1的别名,表2的别名 from 表1 别名 inner|left|right| join 表2 别名 on 连接条件 where 筛选条件;
-
-
-
truncate(不可以加where)直接删除
语法:
truncate table 表名;
底层是先把表删除,然后在创建一张一模一样的表
-
delete pk truncate
- delete可以加where条件,truncate不能加
- truncate删除,效率高一丢丢
- 假如要删除的表中的自增长列,
如果用delete删除后,再插入数据,自增长的值从断点开始
而truncate删除后,再插入数据,自增长的值从1开始 - truncate删除没有返回值,delete删除有返回值
- truncate删除不能回滚,delete删除可以回滚
-
代码实例:
#方式一:delete #1. 单表的删除 #案例1:手机号以9结尾的女神信息 DELETE FROM beauty WHERE phone` like '%9'; #2. 多表的删除 #案例:删除张无忌的女朋友的信息 delete d from beauty b inner join boys bo on b.boyfriend_id=bo.id where bo.boyName='张无忌'; #案例:删除黄晓明的信息以及他女朋友的信息 delete b,bo from beauty b inner join boys bo on b.boyfriend_id=bo.id where bo.boyName='黄晓明'; #方式二:truncate语句 #案例1:将魅力值>100的男神信息删除 truncate table boys; #不可以加筛选,只能全部清空
七, DDL语言的学习(Data Define Language)
库和表的管理
库的管理
-
库的创建create
create database 【if not exists】 库名 【character set 字符集名】;
-
库的修改alter
已过时5.1.1:rename database 旧库名 to 新库名; 目前不能修改库名。 更改库的字符集: alter database 库名 character set gbk;
-
库的删除drop
drop database if exists 库名;
表的管理
-
表的创建create
create table if not exists 表名( 字段名 字段的类型【(长度) 约束】, 字段名 字段的类型【(长度) 约束】, ... 字段名 字段的类型【(长度) 约束】 );
-
表的修改ALTER
ALTER TABLE 表名 ADD|DROP|MODIFY|CHANGE COLUMN 列名 【列类型 约束】;
2.1. 修改列名
ALTER TABLE 表名 CHANGE 【COLUMN】 旧列名 新列名 类型;
2.2. 修改列的类型或约束
ALTER TABLE 表名 MODIFY COLUMN 列名 新类型【新约束】;
2.3. 添加新列
ALTER TABLE 表名 ADD COLUMN 添加的列名 类型 【first|after 字段名】;
2.4. 删除列 (只有在库和表的时候才可以使用IF EXISTS)
ALTER TABLE 表名 DROP COLUMN 列名;
2.5. 修改表名
ALTER TABLE 旧表名 RENAME 【TO】 新表名; -
表的删除drop
DROP TABLE IF EXISTS 表名;
-
表的复制
#4.1 仅仅复制表的结构 CREATE TABLE 要复制的表名 LIKE 被复制的表名; #4.2 复制表的结构+数据(可以跨库 库名打点) CREATE TABLE 要复制的表名 SELECT 列,... FROM 被复制的表名 WHERE 筛选条件;
常见的数据类型介绍
数值
-
整形
整数类型 字节 范围 tinyint 1 有符号:-128~127 无符号:0~255 smallint 2 有符号:-32768~32767 无符号:0~65535 mediumint 3 有符号:-8388608~8388607 无符号:0~1677215 (好吧,反正很大,不用记住) int,integer 4 有符号:-2147483644~2147483647 无符号:0~4294967295 (好吧,反正很大,不用记住) bigint 8 有符号:-9223372036854775808~9223372036854775807 无符号:0~9223372036854775807*2+1 (好吧,反正很大,不用记住) 特点:
-
如果不设置无符号还是有符号,默认是有符号,如果像设置无符号,需要添加 unsigned 关键子
-
如果插入的数值超出了整型的范围,会报out of range 异常,并且插入临界值
-
如果不设置长度,会有默认的长度
长度代表了显示的最大宽度,如果不够会用0在左边填充,但必须搭配 zerofill 使用!,并且默认变为无符号整型
-
-
小数
-
定点数
定点类型 字节 范围 dec(m,d) decimal(m,d) m+2 最大取值范围于double相同,给定decimal的有效取值范围有m和d决定 -
浮点数
浮点数类型 字节 范围 float 4 土1.75494351E~土3.402823466E+38 double 8 土2.2250738585072014E-308~土1.7976931348623157E+308 特点:
-
m和d的意思:
m:整数部位+小数部位
d:小数部位(不够补零,多出四舍五入)
如果超出范围,会报out or range异常,并且插入临界值
-
d和m都可以省略
如果是decima,则m默认为10,D默认为0
如果是float和double,则会根据插入的数值的精度来决定精度
-
定点型的精确度较高,如果插入数值的精度较高如货币运算等则考虑使用
-
-
原则
- 所选择的类型越简单越好,能保存数值的类型越小越好。
字符
-
较短的字符串
字符串类型 最多字符数 描述及存储需求 特点 空间的消耗 效率 char(m) m,可以省略,默认为1 m为0~255之间的整数 固定长度的字节 比较消耗 高 varchar(m) m,不可以省略 m为0~65535之间的整数 可变长的字节 比较节省 低 其他:
binary和varbinary用于保存较短的二进制
enum用于保存枚举
set用于保存集合
-
较长的文本
text(文本)
blob(较大的二进制)
日期
日期和时间类型 | 字节 | 最小值 | 最大值 |
---|---|---|---|
date | 4 | 1000-01-01 | 9999-12-31 |
datetime | 8 | 1000-01-01 00:00:00 | 9999-12-31 23:59:59 |
timestamp | 4 | 1970010180001 | 2038年的某个时刻 |
time | 3 | -838:59:59 | 838:59:59 |
year | 1 | 1901 | 2155 |
- timestamp(时间戳)和datetime的区别
- timestamp支持的时间范围较小
- timestamp和实际时区有关,更能反应实际的日期,而datetime则只能反映出插入时的当地时区
- timestamp的属性受MySQL版本和sqlmode的影响
常见的约束
-
含义:一种限制,用于限制表中的数据,为了保证表中的数据的准确和可靠性
-
分类:六大约束
-
not null:非空,用于保证字段的值不能为空。比如姓名,学号
-
default:默认,用于保证该字段有默认值。比如性别
-
primary key:主键,用于保证该字段的值具有唯一性,并且非空。比如学号,员工编号
-
unique:唯一,用于保证该字段的值具有唯一性,可以为空。比如座位号
-
check:检查约束【MySQL中不支持】 。比如年龄,性别
-
foreign key:外键,用于限制两个表的关系,用于保证该字段的值必须来自主表的关联列的值,
在从表添加外键约束,用于引用主表中某列的值。比如学生表的专业编号,员工的部门编号,员工表的工种编号
-
-
添加约束的时机:
-
创建表时
-
列级约束
语法:直接在字段名和类型后面追加 约束类型即可。
六大约束语法上都支持,但外键约束没有效果,
只支持:默认,非空,主键,唯一
-
表级约束
除了非空,默认,其他的都支持。
语法:在各个字段的最下面
【constraint 约束名】 约束类型(字段名)
-
格式:
create table 表名( 字段名 字段类型 列级约束 列级约束, 字段名 字段类型, 表级约束, constraint 约束名 foreign key(字段名) references 主表(别引用列) )
-
-
修改表时
-
添加列级约束
alter table 表名 modify column 字段名 字段类型 新约束;
-
添加表级约束
alter table 表名 add 【constraint 约束名】 约束类型(字段名) 【外键的引用】;
-
-
-
约束的添加分类:
-
列级约束
-
表级约束
位置 支持的约束类型 是否可以起约束名 列级约束 列的后面 语法都支持,但外键没有效果 不可以 表级约束 所有列的下面 默认和非空不支持,其他支持 可以(主键没有效果)
-
-
主键和唯一的大对比
保证唯一性 是否允许为空 一个表中可以有多少个 是否允许组合 主键 √ × 至多有一个 √,但不推荐 唯一 √ √ 可以有多个 √,但不推荐 -
外键
-
要求在从表设置外键关系
-
从表的外键列的类型和主表的关联列的类型要求一致或兼容,名称无要求
-
主表的关联列必须是一个key(一般是主键或唯一)
-
插入数据时,先插入主表,再插入从表
删除数据时,先删除从表,在删除主表。
可以通过以下两种方式来删除主表的记录:
-
级联删除
在添加外键的后面添加【on delete cascade】
alter table stuinfo add constraint 约束别名 foreign key (从表列名) references (主表列名) on delete cascade;
-
级联制空
在添加外键的后面添加【on delete set null】
alter table stuinfo add constraint 约束别名 foreign key (从表列名) references (主表列名) on delete set null;
-
-
-
代码
#一,创建表时添加约束 1. 添加列级约束 use students; create table stuinfo( id int primary key,#主键 stuName varchar(20) not null,#非空 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) ) desc stuinfo; #查看stuinfo表中的所有的索引,包括主键,外键,唯一 show index from stuinfo; 2. 添加表级约束 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) #外键 ); #通用的写法 create table if not exists stuinfo( id int primary key, stuname varchar(20) not null, sex char(1), age int default 18, seat int unique, majorid int constraint fk_stuinfo_major foreign key (majorid) references major(id) ); 二,修改表时添加约束 drop table if exists stuinfo; create table stuinof( id int, stuname varchar(20), gender char(1), seat int, age int, majorid int ); #1. 添加非空约束 alter table stuinfo modify column stuname varchar(20) not null; #2. 添加默认约束 alter table stuinfo modify column age int default 18; #3. 添加主键 #3.1 列级约束 alter table stuinfo modify column id int primary key; #3.2 表级约束 alter table stuinfo add constraint my_emp_id_pk primary key (id); #4. 添加唯一 #4.1 列级约束 alter table stuinfo modify column seat int unique; #4.2 表级约束 alter table stuinfo add unique (seat); #5. 添加外键 alter table suinfo add constraint fk_stuinfo_major foreign key (majorid) references major(id); 三,修改表时删除约束 #1. 删除非空约束 alter table stuinfo modify column stuname varchar(20) null; #2. 删除默认约束 alter table stuinfo modify column age int; #3. 删除主键(两种) alter table stuinfo drop primary key; #4. 删除唯一 alter table stuinfo drop index seat; #5. 删除外键 alter table stuinfo drop foreign key fk_stuinfo_major; #查看stuinfo表中的所有的索引,包括主键,外键,唯一 show index from stuinfo;
标识列
-
又称为自增长列,含义:可以不用手动的插入值,系统提供默认的序列值【auto_increment】
-
特点:
-
标识列必须和主键搭配吗?不一定,但要求是一个key
-
一个表可以有几个标识列?至多一个!
-
不用手动插入,可以自动提供序列值,默认从1开始,步长为1
-
标识列的类型只能是数值型。
-
标识列可以通过 set auto_increment_increment=N 设置步长
可以通过 手动插入值 设置起始值
-
#一, 创建表时设置标识列
drop table if exists table_identity;
create table tab_identity(
id int primary key auto_increment,#添加主键自增
name varchar(20)
);
truncate table tab_identity;#清空表中数据
insert into tab_identity(id,name) values (null,'john');
insert into tab_identity(name) values ('lucy');
show variables like '%auto_increment%';#查看自增长值,起始值设置了没有效果,步长可以设置
set auto_increment_increment=3;#设置步长
#二, 修改表时设置标识列
alter table tab_identity modify column id int primary key auto_increment;
#三, 修改表时删除标识列
alter table tab_identity modify column id int;
八, TCL语言的学习(Transaction Control Language)
事物和事物处理
-
事物:一个或一组sql语句组成一个执行单元,这个执行单元要么全部执行,要么全部不执行。
-
存储引擎:
- 概念:在MySQL中的数据用各种不同的技术存储在文件(或内存)中。
- 通过show engines; 来查看MySQL支持的存储引擎。
- 在MySQL中用的最多的存储引擎有:innodb,myisam,memory等。其中innodb支持事物,而myisam,memory等不支持事物。
-
事物的ACID特点:(面试题)
-
原子性(Atomicity)
原子性是指事物是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。
-
一致性(Consistency)
事物必须使数据库从一个一致性状态变换到另外一个一致性状态。
-
隔离性(Isolation)
事物的隔离性是指一个事物的执行不能被其他事物干扰,即一个事物内部的操作及使用的数据对并发的其他事物是隔离的,并发执行的各个事物之间不能相互干扰。
-
持久性(Durability)
持久性是指一个事物一旦被提交,他对数据库中数据的改变就是永久性的,接下来的其他操作和数据库故障不应该对其有任何影响
-
-
事物的创建
-
隐式事物:
隐式事物:事物没有明显的开启和结束的标记
比如insert,update,delete语句
DELETE FROM 表 WHERE id=1; -
显示事务
前提:必须先设置自动提交功能为禁用
查看自动提交状态:show variables like 'autocommit';
设置自动提交为关闭:set autocommit=0;
#步骤1:开启事物 SET autocommit=0;#设置自动提交为关闭 START TRANSACTION;#开启事务(可以省略) #步骤2:编写事物中的SQL语句(SELECT INSERT UPDATE DELETE) 语句1; 语句2; ... #步骤3:结束事物 COMMIT;#提交事物 ROLLBACK;#回滚事物
-
-
并发事务
-
事务的并发问题是如何产生的?
多个事务 同时 操作同一个数据库的相同数据时
-
并发问题都有那些?
- 脏读:一个事务读取了其他事务还没有提交的数据,读到的是其他事务"更新"的数据
- 不可重复读:一个事务多次读取,结果不一样
- 幻读:一个事务读取了其他事务还没有提交的数据,只是读到的是 其他事务"插入"的数据
-
如何解决并发问题?
通过设置隔离级别来解决并发问题
-
-
事物的隔离级别:
脏读 可重复读 幻读 read uncommitted:读未提交 √ √ √ read committed:读已提交 × √ √ repeatable read:可重复读 × × √ serializable:串行化 × × × MySQL中默认第三个隔离级别 REPEATABLE READ:
Oracle中默认第二个隔离级别 READ COMMITTED:(支持读未提交和串行化)
查看隔离级别
select @@tx_isolation;
设置隔离级别
set session|global transaction isolation level 隔离级别; -
delete和truncate在事务使用时的区别
#演示delete 回滚删除失败 SET autocommit=0;#开启事务 START TRANSACTION; DELETE FROM account; ROLLBACK; #回滚事务 #演示truncate 回滚删除成功 SET autocomit=0; START TRANSACTION; TRUNCATE TABLE account; ROLLBACK;
-
演示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;#查看
九, 视图的讲解
一,含义
-
虚拟表,和普通的表一样使用
MySQL 5.1版本出现的新特性,本身是一个虚拟表,它的数据来自于表,通过执行动态生成
-
好处:
- 重用sql语句
- 简化复杂的sql操作,不必知道它的查询细节
- 保护数据,提高安全性
二,创建
-
语法:
create view 视图名 as 查询语句;
-
案例:
USE myemployees; #1. 查询姓名中包含a字符的员工名,部门名和工种信息 #创建 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 j.job_id = e.job_id; #使用 SELECT * FROM myv1 WHERE last_name LIKE '%a%'; #2. 查询各部门的平均工资级别 #创建视图查看每个部门的平均工资 CREATE VIEW myv2 AS SELECT AVG(salary) ag,department_id FROM employees GROUP BY department_id; #使用 SELECT myv2.ag,g.`grade_level` FROM myv2 JOIN job_grades g ON myv2.ag BETWEEN g.`lowest_sal` AND g.`highest_sal`;
三,修改
-
方式一:
create or replace view 视图名 as 查询语句;
-
方式二:
语法: alter view 视图名 as 查询语句;
四,删除
-
语法:要求具备删除视图的权限
drop view 视图名,视图名,...;
五,查看
- DESC 视图名;#查看结构
- SHOW CREATE VIEW 视图名;
六,使用
- 插入(和表的语法一样)
insert into 视图名 【(字段)】 values (值,...); - 修改
update 视图名 set 修改的字段='值' where 筛选条件; - 删除
delete 视图名 where 筛选条件; - select 字段 from 视图名;
- 视图的可更新性和视图中查询的定义有关系,以下类型视图是不能更新的。
- 包含以下关联字的sql语句:分组函数,distinct,group by,having,union,或者union all
- 常量视图
- select中包含子查询
- join
- from一个不能更新的视图
- where子句的子查询引用了from子句中的表
七,视图和表的对比
创建语法的关键字 | 是否实际占用物理空间 | 使用 | |
---|---|---|---|
视图 | create view | 只保存了sql逻辑 | 增删改查,一般不能增删改 |
表 | create table | 保存了数据 | 增删改查 |
十, 变量
-
系统变量
说明:变量由系统提供,不用自定义
语法:
SHOW TABLES;#查看当前数据库下的所有表 SHOW DATABASES;#查看当前连接下所有的库 SHOW FULL COLUMNS FROM table_name;#查看表所有的列 1. 查看所有的系统变量 show 【session|global】 variables;# 如果没有显示声明global还是session,则默认session 2. 查看满足条件的部分系统变量 show global|【session】 variables like '%char%'; 3. 查看指定的某个系统变量的值 select @@global|【session】.系统变量; #默认不写是会话的,注意打点调用 4. 为某个系统变量赋值 方式一: set global|【session】系统变量名=值; 方式二: set @@globall|【session】.系统变量=值; #注意打点调用 注意: 如果是全局级别需要加global,如果是会话级别,则需要加session,如果不写,则默认session
-
全局变量【global】
服务器层面上的,必须有super权限才能为系统变量赋值,作用域为整个服务器,也就是针对所有连接(会话)有效,不能跨重启
-
会话变量【session】
服务器为每一个连接的客户端都提供了系统变量,作用域为当前的连接(会话),新建一个连接就是失效
-
-
自定义变量
说明:变量是用户自定义的,不是由系统的
使用步骤:
声明
赋值
使用(查看,比较,运算等)-
用户变量
作用域:针对当前会话(连接)有效,同于会话变量的作用域。
应用位置:应用在任何地方,也就是 begin end 里面或 begin end 外面。赋值的操作符:=或:= #①声明并初始化 SET @用户变量名=值; SET @用户变量名:=值; SELECT @用户变量名:=值; #②赋值(更新用户变量的值) 方式一:通过SET或SELECT SET @用户变量名=值; SET @用户变量名:=值; SELECT @用户变量名:=值; 方式二:通过SELECT INTO SELECT 字段 INTO @变量名 FROM 表; #③使用(查看用户变量的值) SELECT @用户变量名;
-
局部变量
作用域:仅仅在定义它的 begin end 中有效。
应用位置:应用在 begin end 中的第一句话#①声明 DECLARE 变量名 类型; DECLARE 变量名 类型 DEFAULT 值; #②赋值 方式一:通过SET或SELECT SET 局部变量名=值; SET 局部变量名:=值; SELECT @局部变量名:=值; 方式二:通过SELECT INTO SELECT 字段 INTO 局部变量名 FROM 表; #③使用 SELECT 局部变量名;
-
用户变量和局部变量对比
作用域 定义和使用的位置 语法 用户变量 当前会话 会话中的任何地方 必须加@符号,不用限定类型 局部变量 BEGIN END中 只能在BEGIN END中,且为第一句话 一般不用加@符号,需要限定类型
-
代码:
#一,系统变量
#1》全局变量
#①查看所有的全局变量
SHOW GLOBAL VARIABLES;
#②查看部分的全局变量
SHOW GLOBAL VARIABLES LIKE '%char%';
#③查看指定的全局变量的值
SELECT @@global.autocommit;#查看自动提交(1默认自动提交)
SELECT @@tx_isolation;#查看隔离级别
#④为某个指定的全局变量赋值
SET @@global.autocommit=1;
#2》会话变量
SHOW SESSION VARIABLES;
SHOW VARIABLES;
#②查看部分的会话变量
SHOW VARIABLES LIKE '%char%';
#③查看指定的某个会话变量
SELECT @@session.tx_isolation;#查看隔离级别
SELECT @@tx_isolation;
#④为某个会话变量赋值
方式一:
SET @@session.tx_isolation='read-uncommitted';#设置隔离级别
SET @@tx_isolation='read-uncommitted';
方式二:
SET SESSION tx_isolation='read-committed';
#二,自定义变量
#1. 用户变量
#案例
#声明并初始化
SET @name='john';
SET @name=100;
SET @count=1;
#赋值
SELECT COUNT(*) INTO @count
FROM employees;
#查看
SELECT @count;
#2. 局部变量
#案例:声明两个变量并赋初始值,并求和,并打印
#1. 用户变量
SET @m=1;
SET @n=2;
SET @sum=@m+@n;
SELECT @sum;
#2. 局部变量
DECLARE m INT DEFAULT 1;
DECLARE n INT DEFAULT 2;
DECLARE SUM INT ;
SET SUM=m+n;
SELECT SUM;
十, 存储过程和函数
- 说明:都类似于Java中的方法,将一组完成特定功能的逻辑语句包装起来,对外暴露名字
- 好处:
- 提高代码的重用性
- 简化操作
- 减少了编译次数并且减少了和数据库服务器的连接次数,提高了效率
存储过程
-
使用过程
-
创建
CREATE PROCEDURE 存储过程名(参数列表价) BEGIN 存储过程体(一组合法的SQL语句) END
注意:
-
参数列表包含三部分
参数模式 参数名 参数类型举例:IN stuname VARCHAR(20)
-
参数模式:
in:该参数可以作为输入,也就是该参数需要调用方传入值(默认不写就是in)
out:该参数可以作为输出,也就是该参数可以作为返回值
inout:该参数既可以作为输入又可以作为输出,也就是该参数既需要传入值,又可以返回值 -
如果存储过程体仅仅只有一句话,BEGIN END 可以省略。
-
存储过程体中的每条SQL语句的结尾要求必须加分号。
-
存储过程的结尾可以使用 delimiter 重新设置
语法:
delimiter 结束标记
案例:
DELIMITER $
-
-
调用
call 存储过程名(参数列表) 结束标记
举例:
调用in模式的参数:call sp1('值');
调用out模式的参数:set @name; call sp1 (@name); select @name;
调用inout模式的参数:set @name=值; call sp1(0@name); select @name;
-
查看
SHOW CREATE PROCEDURE 存储过程名;
-
删除
语法:(一次只能删除一个) drop procedure 存储过程名;
修改存储过程没有
-
代码:
#一,创建
#1. 空参列表cmd中使用
#案例:插入到admin表中五条记录
SELECT * FROM admin;
DELIMITER $ #结束标记
CREATE PROCEDURE myp1()
BEGIN
INSERT INTO admin(username,`password`)
VALUES ('john1','0000'),('lily','0000'),('rose','0000'),('jack','0000'),('tom ','0000');
END $
#调用
CALL myp1()$
#查看
SELECT * FROM admin$
#2. 创建带in模式参数的存储过程
#案例1:创建存储过程实现 根据女神名,查询对应的男神
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('Angelababy')$
#如果出现 incorrect string value 是与字符集有关,需要设置字符集
#set names gbk$
#案例2:创建存储过程实现,用户是否登陆成功
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.`password`=username
AND admin.`password`=PASSWORD;
SELECT IF (result>0,'成功','失败');#使用
END $
#调用
CALL myp3('lyt','6666')$
#3. 创建带out模式的存储过程
#案例1:根据女神名,返回对应的男神名(返回一个)
CREATE PROCEDURE myp4(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 $
#调用
#set @bName$ #定义变量(可以不定义直接用)
CALL myp4('唐艺昕',@bName)$#调用
SELECT @bName$ #查看
#案例2:根据女神名,返回对应的男神名和男神魅力值(两个返回值)
CREATE PROCEDURE myp5(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 myp5('唐艺昕',@bName,@usercp)$
SELECT @bName,@usercp$
#4. 创建带inout模式参数的存储过程
#案例1.传入a和b两个值,最终a和b都翻倍并返回
CREATE PROCEDURE myp6(INOUT a INT ,INOUT b INT)
BEGIN
SET a=a*2;
SET b=b*2;
END
#使用
#先定义两个变量
SET @m=10$
SET @n=20$
CALL myp6(@m,@n)$#调用方法
#查看值
SELECT @m,@n$
#二,删除存储过程
DROP PROCEDURE `myp2`;
DROP PROCEDURE `myp3`;
DROP PROCEDURE `myp4`;
DROP PROCEDURE `myp5`;
#三,查看存储过程的信息
SHOW CREATE PROCEDURE 存储过程名;
#-----------------------------------------------总和案例:
#一,创建存储过程实现传入用户名和密码,插入到admin表中
CREATE PROCEDURE test_p1(IN username VARCHAR(20),IN loginpwd VARCHAR(20))
BEGIN
INSERT INTO admin(admin.username,PASSWORD)
VALUES(username,loginpwd);
END $
#二,创建存储过程实现出啊如女神编号,返回女神名称和女神电话
CREATE PROCEDURE test_p2 (IN id INT,OUT NAME VARCHAR(20),OUT phone VARCHAR(20))
BEGIN
SELECT b.name,b.phone INTO NAME,phone
FROM beauty b
WHERE b.id=id;
END $
#三,创建存储过程或函数实现传入两个女神生日,返回大小
CREATE PROCEDURE test_p3(INT bir1 DATETIME,INT bir2 DATETIME,OUT result INT)
BEGIN
SELECT DATEDIFF(bir1,bir2) INT reslt;
END $
#四,创建存储过程或函数实现传入一个日期,格式化成XXX年XX月XX日并返回
CREATE PROCEDURE test_p4(IN mydate DATETIME,OUT strDate VARCHAR(50))
BEGIN
SELECT DATE_FORMAT(mydate,'%y年%m月%d日') INTO strDate;
END $
CALL test_p4(NOW(),@str)$
SELECT @str $
#五,传入:小昭 返回:小昭 and 张无忌
CREATE PROCEDURE test_p5(IN beautyName VARCHAR(20),OUT str VARCHAR(50))
BEGIN
SELECT CONCAT(beautyName,' and ',IFNULL(boyName,'null')) INTO str
FROM boys bo
RIGHT JOIN beauty b
ON b.boyfriend_id=bo.id
WHERE b.name=beautyName;
END $
CALL test_p5('柳岩',@str)$
SELECT @str $
#六,创建存储过程或函数,根据传入的条目数和其实索引,查询beauty表的记录
CREATE PROCEDURE test_p5(IN startIndex INT,IN size INT)
BEGIN
SELECT * FROM beauty LIMIT startIndex,size;
END $
test_p5(3,3)$
函数
-
区别:
存储过程:可以有0个返回,也可以有多个返回,适合做批量插入,批量更新
函数:有且仅有一个返回,适合做处理数据后返回一个结果 -
使用过程
-
创建
create function 函数名(参数列表) returns 返回类型 begin 函数体 end
注意:
- 参数列表包含两部分:参数名 参数类型
- 函数体:肯定会有return语句,如果没有会报错
如果return语句没有放在函数体的最后也不报错,但不建议 - 函数体中仅有一句话,则可以省略begin end
- 使用 delimiter 语句设置结束标记
-
调用
select 函数名(参数列表) 结束标记
-
查看
SHOW CREATE FUNCTION 函数名 结束标记
-
删除
#一次只能删一个 DROP FUNCTION 函数名;
-
代码
#------------------案例演示
#1. 无参有返会
#案例:返回公司的员工个数
DELIMITER $ #设置结束标记
CREATE FUNCTION myf1() RETURNS INT
BEGIN
DECLARE c INT DEFAULT 0;#定义局部变量
SELECT COUNT(*) INTO c#赋值
FROM employees;
RETURN c;
END $
SELECT myf1()$ #调用
#2. 有参有返
#案例1:根据员工名,返回它的工资
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')$
#案例2:根据部门名,返回该部门的平均工资
CREATE FUNCTION myf3(d_name VARCHAR(15)) RETURNS DOUBLE
BEGIN
DECLARE s DOUBLE DEFAULT 0.0;#定义局部变量
SELECT AVG(salary) INTO s
FROM employees e
INNER JOIN departments d
ON e.department_id = d.department_id
WHERE d.department_name=d_name;
RETURN s;
END $
SELECT myf3('IT')$
#三,查看函数
SHOW CREATE FUNCTION myf3$
#四,删除函数
DROP FUNCTION myf1;
#案例
#一,创建函数,实现传入两个float,返回二者只和
CREATE FUNCTION test_f1(num1 FLOAT,num2 FLOAT) RETURNS FLOAT
BEGIN
DECLARE SUM FLOAT DEFAULT 0;
SELECT SUM=num1+num2;
RETURN SUM;
END $
SELECT test_f1(1,2)$
十一, 流程控制结构
顺序结构
- 程序从上往下一次执行
分支结构
- 程序从两条或多条路径中选择一条去执行
-
if函数
功能:实现简单的双分支
语法:select if(表达式1,表达式2,表达式3)
执行顺序发:如果表达式1成立,则if函数返回表达式2的值,否则返回表达式3的值
应用:任何地方 -
case结构
-
类似于Java中的switch语句,一般用于实现等值判断
语法:
case 变量|表达式|字段 when 要判断的值 then 返回的值1或语句1; when 要判断的值 then 返回的值1或语句2; ... else 要返回的值n或语句n; end case;
-
类似于Java中的多重if语句,一般用于实现区间判断
语法:
case when 要判断的条件1 then 返回的值1或语句1; when 要判断的条件2 then 返回的值1或语句2; ... else 要返回的值n或语句n; end case;
-
- 特点:
①可以作为表达式,嵌套在其他语句中使用,可以放在任何地方,begin end 中或者begin end外面
可以作为独立的语句嵌套去使用,只能放在begin end中
②如果when中的值满足或条件成立,则执行对应的then后面的语句,并且结束case
如果都不满足,则执行else中的语句或值
③else可以省略,如果else省略了,并且所有when条件都不满足,则返回null
-
if结构
功能:实现多重分支
语法:
if 条件1 then 语句1; elseif 条件2 then 语句2; ... end if;
应用场合:应用在begin end中
循环结构
- 程序在满足一定的条件的基础上,重复执行一段代码
- 分类:
while,loop,repeat - 循环控制:
iterate 类似于 continue 继续 结束本次循环继续下一次
leave 类似于 break 跳出 结束当前所在的循环
-
while
语法:
【标签:】while 循环条件 do 循环体; end while【标签】;
特点:先判断后执行
位置:begin end中 -
loop
语法:
【标签:】loop 循环体; end loop 【标签】;
特点:没有条件的死循环
位置:begin end中 -
repeat
语法:
【标签:】 repeat 循环体; until 结束循环的条件 end repeat 【标签】;
特点:先执行后判断
位置:begin end中
- 对比:
- 这三种都可以省略名称,但如果循环中添加了循环控制语句(leave或iterate)则必须添加名称
代码:
#2. case结构
#案例:
#创建存储过程,根据传入的成绩,来显示等级,比如传入的成绩:90-100 显示A,80-90 显示B,80-60 C,否则显示D
DELIMITER $ #定义结束标记
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(66)$
#3. if结构
#案例1:根据传入的成绩,来显示等级,比如传入的成绩:90-100 返回A,80-90 返回B,80-60 C,否则返回D
CREATE FUNCTION test_if(score INT) RETURN 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(88)$
#二,循环结构
#1. 没有添加循环控制语句
#案例:批量插入,根据次数插入到admin表中多条记录
CREATE PROCEDURE pro_while1 (IN insertCount INT)
BEGIN
DECLARE i INT DEFAULT 1;#定义局部变量默认为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)$
#2. 添加leave语句
#案例:批量插入,根据次数插入到admin表中多条记录,如果次数>20则停止
TRUNCATE TABLE admin$ #删除库中的数据
DROP PROCEDURE test_while1$#删除存储过程
CREATE PROCEDURE test_while1(IN insertCount INT)
BEGIN
DECLARE i INT DEFAULT 1;#定义局部变量默认为1
a:WHILE i<=insertCount DO
INSERT INTO admin(username,`password`) VALUES(CONCAT('xiaoha',i),'0000');
IF i>=20 THEN LEAVE a;#结束循环
END IF;
SET i=i+1;#自增
END WHILE a;
END $
#调用
CALL test_while1(100)$
#3. 添加iterate语句
#案例:批量插入,根据次数插入到admin表中多条记录,只插入偶数次
TRUNCATE TABLE admin$ #删除库中的数据
DROP PROCEDURE test_while1$#删除存储过程
CREATE PROCEDURE test_while1(IN insertCount INT)
BEGIN
DECLARE i INT DEFAULT 0;#定义局部变量默认为1
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('xiaoha',i),'0000');
END WHILE a;
END $
CALL test_while1(100)$
#-----------------------------------流程控制经典题--------------------------------
/*
一,已知表stringcontent
其中字段:
id 自增长
content varchar(20)
向该表插入指定个数的,随机的字符串
*/
DROP TABLE IF EXISTS stringcontent;
CREATE TABLE stringcontent(
id INT PRIMARY KEY AUTO_INCREMENT,
content VARCHAR(20)
);
DELIMITER $
CREATE PROCEDURE test_randstr_insert(IN insertCount INT)
BEGIN
DECLARE i INT DEFAULT 1;#定义一个循环变量i,表示插入次数
DECLARE str VARCHAR(26) DEFAULT 'abcdefghigklimopqrstuvwxyz';
DECLARE startIndex INT DEFAULT 1;#代表起始索引
DECLARE len INT DEFAULT 1;#代表截取的字符的长度
WHILE i<=insertCount DO
SET len=FLOOR(RAND()*(20-startIndex+1)+1);#产生一个随机的整数,代表 截取长度,1-(26-startIndex+1)
SET startIndex=FLOOR(RAND()*26+1);#产生一个随机的整数,代表起始索引1-26
INSERT INTO stringcontent(content) VALUES (SUBSTR(str,startIndex,len));
SET i=i+1;#循环变量更新
END WHILE;
END$