Java高效率复习-MySQL下篇[MySQL]

news2024/12/25 12:32:13

前言

本文章的语言描述会比上篇多一些

数据库的创建修改与删除

标识符命名规则

  • 数据库名、表名不得超过30个字符,变量限制为29个
  • 必须只能包含A-Z,a-z,0-9,_等63个字符
  • 数据库名、表名、字段名等对象名中间不要包含空格
  • 同一个MySQL软件中,数据库不能同名;同一个库中,表不能重名;同一个表中,字段不能重名
  • 必须保证你的字段没有和保留字、数据库系统或常用方法冲突。如果坚持使用,请在SQL语句中使用`(着重号)引起来(不建议这么做)
  • 保持字段名和类型的一致性:在命名字段并为其指定数据类型的时候一定要保证一致性,假如数据类型在一个表里是整数,那在另一个表中可别变成其他类型了

创建数据库

创建数据库的方式有三种:

#创建和管理表
SELECT * FROM ORDER;
SELECT * FROM `ORDER`;#使用着重号来区别保留字或关键字
#如何创建数据库
#创建数据库也是要有用户权限的
#root是根目录,我们学习都是用的root

数据库是不能改名的,有些图形工具可以改名的原因是,底层创建了一个新数据库,然后把要修改的数据库数据全部复制到新数据库里,然后删除旧数据库,所以一般不要修改数据库名,数据表是可以修改的
字符集默认使用utf8mb4
在这里插入图片描述

#显示创建的数据库的字符集和其他信息
#SHOW CREATE DATABASE databaseName;
#CREATE DATABASE `mytest1` 
#/*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci */
#方式1:
CREATE DATABASE mytest1;
SHOW CREATE DATABASE mytest1;#默认utf8mb4
#方式2:指定字符集
CREATE DATABASE mytest2 CHARACTER SET 'gbk';
SHOW CREATE DATABASE mytest2;#gbk
#方式3(推荐使用):判断是否存在
#执行完毕后,字符集仍然是gbk,可以发现并不会覆盖和影响,只是不报错了
CREATE DATABASE IF NOT EXISTS mytest2 CHARACTER SET 'utf8';
#如果不存在,则正常创建
CREATE DATABASE IF NOT EXISTS mytest3 CHARACTER SET 'utf8';
SHOW CREATE DATABASE mytest3;#UTF8
#显示当前所有的数据库
SHOW DATABASES;#可以看到三个mytest数据库在列表中
#在非图形用户界面或者没有进入特定数据库时,是需要通过点击或use来进入数据库的
#切换\进入\使用数据库
USE atguigudb;#进入后,才可以进行表的操作,否则是提示命令表不存在
#查看当前数据库都存有哪些表
#没有表的话,图形中会是空的,命令行会提示:Empty set (0.00 sec),如果表没有数据也会是这样
USE mytest1;#进入数据库
SHOW TABLES;#显示该数据库的全部数据表
USE atguigudb;
SHOW TABLES;
#查看当前使用的数据库
SELECT DATABASE()FROM DUAL;#函数
#查看指定数据库下保存的表
SHOW TABLES FROM mysql;

修改数据库

#修改数据库
#更改数据库字符集
SHOW CREATE DATABASE mytest2;#查看创建结构
#将gbk修改成utf8
ALTER DATABASE mytest2 CHARACTER SET 'utf8';
SHOW CREATE DATABASE mytest2;#修改成功
#删除数据库
#方式1:
DROP DATABASE mytest1;#不能执行2次
SHOW DATABASES;
#方法2:
DROP DATABASE IF EXISTS mytest1;#如果不存在则不做任何处理
DROP DATABASE IF EXISTS mytest2;
SHOW DATABASES;

常见的数据类型_创建表的两种方式

在这里插入图片描述
数据类型
在这里插入图片描述
在这里插入图片描述

#如何创建数据表
USE atguigudb;
SHOW CREATE DATABASE atguigudb;#默认使用的是utf8
SHOW TABLES;
#方式1:
CREATE TABLE IF NOT EXISTS myemp1(#需要用户具备创建表的权限
id INT,
emp_name VARCHAR(15),#使用VARCHAR来定义字符串,必须在使用VARCAHR时指明其长度
hire_date DATE	
);
DESC myemp1;#查看表结构
#如果创建表时没有指明使用的字符集,则默认使用表所在的数据库的字符集
SHOW CREATE TABLE myemp1;#显示数据库创建时的语句
SELECT*FROM myemp1;#查看表数据
#方式2:
CREATE TABLE myemp2
AS 
SELECT employee_id ,last_name,salary
FROM employees;
DESC myemp2;SELECT*FROM myemp2;#有字段的
DESC employees;
#说明1:查询语句中字段的别名,可以作为新创建的表的字段的名称
#说明2:此时的查询语句可以结构比较丰富,使用前面讲过的各种SELECT
CREATE TABLE myemp3
AS
SELECT e.employee_id emp_id,e.last_name lname,d.department_name 
FROM employees e JOIN departments d
ON e.department_id = d.department_id;
SELECT*FROM myemp3
DESC myemp3;
#练习1:创建一个表employees_copy,实现对employees表的复制,包括表数
CREATE TABLE employees_copy
AS
SELECT * FROM employees;
SELECT*FROM employees_copy;
#练习2:创建一个表employees_blank,实现对employees表的复制,不包括表数
CREATE TABLE employees_blank
AS
SELECT * 
FROM employees
WHERE department_id > 10000;
SELECT * FROM employees_blank;
DESC employees_blank;

修改表_重命名表_删除表_清空表

#修改表_重命名表_删除表_清空表
DESC myemp1;
#修改表 --> ALTER TABLE
#添加一个字段
ALTER TABLE myemp1
ADD salary DOUBLE(10,2);#默认添加到表中的最后一个字段
DESC myemp1;
ALTER TABLE myemp1
ADD phone_number VARCHAR(20) FIRST;#FIRST:第一个
DESC myemp1;
ALTER TABLE myemp1
ADD email VARCHAR(45) AFTER emp_name;#AFTER:指定添加在某字段的后面
DESC myemp1;
#修改一个字段:数据类型、长度、默认值(略)
ALTER TABLE myemp1
MODIFY emp_name VARCHAR(25);#将emp_name的类型从VARCHAR(15)改为VARCHAR(25)
DESC myemp1;
ALTER TABLE myemp1
MODIFY emp_name VARCHAR(35) DEFAULT 'aaa'; #指定了默认值,如果插入数据为NULL,则默认为'aaa'
DESC myemp1;
#重命名一个字段
ALTER TABLE myemp1
CHANGE salary monthly_salary DOUBLE(10,2);
DESC myemp1;
ALTER TABLE myemp1
CHANGE email my_email VARCHAR(50);#类型也是可以修改的
DESC myemp1;
#删除一个字段
ALTER TABLE myemp1
DROP COLUMN my_email;
DESC myemp1;
#重命名表
#方式1:
RENAME TABLE myemp1
TO myemp11;
DESC myemp11;
#方式2:
ALTER TABLE myemp2
RENAME TO myemp12;
DESC myemp12;
#删除表:不光将结构删除,同时表中的数据也被删除,释放表空间
DROP TABLE IF EXISTS myemp2;
DROP TABLE IF EXISTS myemp12;
#清空表:只删除表数据,表结构不变 --> 包括自增列也会重置,而delete from 和 回滚事务 是无法重置自增列自增长值的
SELECT * FROM employees_copy;
TRUNCATE TABLE employees_copy;
SELECT * FROM employees_copy;

DCL中的COMMIT和ROLLBACK的使用

COMMIT表示提交,在数据库中是默认提交的,如果不是默认提交,则每次执行完数据库操作,如果不提交,则所有数据都会回滚,就像你写Word文档,但是没有保存,一旦没有保存,这些数据都会消失。

#DCL中COMMIT和ROLLBACK
#COMMIT:提交数据。一旦执行COMMIT,则数据就被永久的保存在数据库中,意味着数据不可以回滚
#ROLLBACK:回滚数据。一旦执行ROLLBACK,则可以实现数据的回滚。回滚到最近的一次COMMIT最后
#对比TRUNCATE TABLE 和 DELETE FROM
#相同点:都可以实现对表中所有数据的删除,同时保留表结构
#不同点:
#	TURNCATE TABLE:一旦执行此操作,表数据全部清除。同时,数据是不可以回滚的
#	DELETE FROM:一旦执此操作,表数据可以全部清除(不带WHERE)同时,数据是可以实现回滚的
/*
DDL 和 DML的说明
① DDL的操作一旦执行,就不可回滚SET autocommit = FALSE对DDL操作无效.
  (因为在执行完DDL操作之后,一定会执行一次COMMIT,而此操作不受SET autocomiit=false的影响)
② DML的默认情况下,一旦执行,也是不可回滚的。但是,如果在执行DML之前,
  执行了 SET autocommit = FALSE,则执行的DML操作就可以实现回滚
*/
#演示:DELETE FROM
#1)
COMMIT;#先提交一下
#2)
SELECT*FROM myemp3;#106条数据
#3)
SET autocommit = FALSE;
#4)
DELETE FROM myemp3;
#5)
SELECT*FROM myemp3;#0条数据
#6)
ROLLBACK;#回滚数据
#7)
SELECT*FROM myemp3;#106条数据

阿里MySQL命名规范及MySQL8.0DDL的原子化

阿里开发规范:
【参考】TRUNCATE TABLE比DELETE速度快,且使用的系统和事务日志资源少,但TRUNCATE无事务且不触发TRIGGER,有可能造成事故,故不建议在开发代码中使用此语句
说明:TRUNCATE TABLE在功能上与不带WHERE子句的DELETE语句相同
内容扩展

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

#测试MySQL8.0的新特性:DDL的原子化
CREATE DATABASE mytest;
USE mytest;
CREATE TABLE book1(
book_id INT,
book_name VARCHAR(255)
);
SHOW TABLES;
DROP TABLE book1,book2
SHOW TABLES;
#区别是,如果是5.7版本的MySQL,上面的删除数据表语句,会将book1删除掉
#这里是没有book2的,所以肯定报错,但是8.0中,如果报错了就不会删除book1
#这也是DDL的原子化:要么都成功,要么就回滚

创建管理表练习

练习需知

#--------------------------------------------------------------------------------
#在查询所有数据库或所有表时,都是在后加上了s
#表示复数,SHOW DATABASES;SHOW TABLES;
#如下代码不要执行,只看代码语法结构,将实际表代入即可
#增加字段:
ALTER TABLE employees
ADD salary INT;
#修改字段:类型、位置
ALTER TABLE employees
MODIFY salary INT AFTER birth_date;#MODIFY salary INT FIRST
#修改字段名:
ALTER TABLE employees
CHANGE birth birthday DATE;
#删除字段:
ALTER TABLE employees
DROP COLUMN department_id;
#修改表名
RENAME employees TO emp1;
ALTER TABLE employees
RENAME TO emp1;
#查询所有表
SHOW TABLES;#默认当前数据库
SHOW TABLES FROM test03_company;#指定数据库
SHOW TABLES FROM atguigudb;#跨数据库查询
#使用查询复制式建表
CREATE TABLE myemp01
AS
SELECT * FROM employees;#没有问题,当前数据表
CREATE TABLE myemp02
AS
SELECT * FROM atguigudb.`departments`;#跨库,必须指定库名+表名 以.作为引用
#--------------------------------------------------------------------------------

练习1

#创建和管理表练习
#练习1:
#1.创建数据库test01_office,指明字符集为utf8。并在此数据库执行下述操作
CREATE DATABASE IF NOT EXISTS test01_office CHARACTER SET 'utf8';
USE test01_office;
#2.创建表dept01
/*
字段	类型
id	INT(7)
NAME	VARCHAR(25)
*/
CREATE TABLE IF NOT EXISTS dept01(
id INT(7),
`NAME` VARCHAR(25)
);
DESC dept01;
#3.将表departments的数据插入新表dept02表中
CREATE TABLE IF NOT EXISTS dept02
AS
#我们当前在test01_office数据库下,该数据库下是没有departments表的
#我们需要额外指定这张表来源于哪个数据库
SELECT *
FROM atguigudb.departments;
DESC dept02;SELECT*FROM dept02;
#4.创建表emp01
/*
字段		类型
id		INT(7)
first_name	VARCHAR(25)
last_name	VARCHAR(25)
dept_id		INT(7)
*/
CREATE TABLE IF NOT EXISTS emp01(
id INT(7),
first_name VARCHAR(25),
last_name VARCHAR(25),
dept_id INT(7)
);
DESC emp01;
#5.将列last_name的长度增加到50
ALTER TABLE emp01
MODIFY last_name VARCHAR(50);
DESC emp01;#查看表结构
#6.根据表employees创建emp2
CREATE TABLE IF NOT EXISTS emp02
AS
SELECT * FROM atguigudb.`employees`;
DESC emp02;SELECT*FROM emp02;
SHOW TABLES FROM test01_office;
#7.删除表emp01
DROP TABLE emp01;
ROLLBACK;#无效,因为执行完DDL操作,会自动提交
#8.将表emp02重命名为emp01
ALTER TABLE emp02
RENAME TO emp01;
#REANME TABLE emp01 TO emp02;
SHOW TABLES FROM test01_office;
#9.在表dept02和emp01中添加新列test_column,并检查所做的操作
ALTER TABLE emp01
ADD test_column VARCHAR(10);DESC emp01
ALTER TABLE dept02
ADD test_column VARCHAR(10);DESC dept02;
#10.直接删除表emp01中的列department_id
ALTER TABLE emp01
DROP COLUMN department_id;DESC emp01;

练习2

#1、创建数据库test02_market
CREATE DATABASE IF NOT EXISTS test02_market;
USE test02_market;
SHOW CREATE DATABASE test02_market;
#2、创建数据表customers
CREATE TABLE IF NOT EXISTS customers(
c_num INT,
c_name VARCHAR(50),
c_contact VARCHAR(50),
c_city VARCHAR(50),
c_birth DATE
);
SHOW TABLES;
#3、将c_contact字段移动到c_birth字段后面
ALTER TABLE customers
MODIFY c_contact VARCHAR(50) AFTER c_birth;
DESC customers;
#4、将c_name字段数据类型改为VARCHAR(70)
ALTER TABLE customers
MODIFY c_name VARCHAR(70);
DESC customers;
#5、将c_contact字段改名为c_phone
ALTER TABLE customers
CHANGE c_contact c_phone VARCHAR(50);
DESC customers;
#6、增加c_gender字段到c_name后面,数据类型为char(1)
ALTER TABLE customers
ADD c_gender CHAR(1) AFTER c_name;
DESC customers;
#7、将表名改为customers_info
RENAME TABLE customers#DESC customers;
TO customers_info;
SHOW TABLES;DESC customers_info;
#8、删除字段c_city
ALTER TABLE customers_info
DROP COLUMN c_city;
DESC customers_info;

练习3

#练习3:
#1、创建数据库test03_company
CREATE DATABASE IF NOT EXISTS test03_company CHARACTER SET 'utf8';
USE test03_company;
#2、创建表offices
CREATE TABLE IF NOT EXISTS offices(
officeCode INT,#办公号
city VARCHAR(30),#城市
address VARCHAR(50),#地址
country VARCHAR(50),#国家
postalCode VARCHAR(25)#邮政编码
);
DESC offices;
#3、创建表employees
CREATE TABLE IF NOT EXISTS employees(
empNum INT,#员工号
lastName VARCHAR(50),#姓
firstName VARCHAR(50),#名字
mobile VARCHAR(25),#住处
`code` INT,#代码...码
jobTitle VARCHAR(50),#职称
birth DATE,#生日
note VARCHAR(255),#记录
sex VARCHAR(5)#姓别
);
DESC employees;
#4、将表employees的mobile字段修改到code字段后面
ALTER TABLE employees
MODIFY mobile VARCHAR(20) AFTER `code`;
DESC employees;
#5、将表employees的birth字段改名为birthday
ALTER TABLE employees
CHANGE birth birthday DATE;
DESC employees;
#6、修改sex字段,数据类型为char(1)
ALTER TABLE employees
MODIFY sex CHAR(1);
DESC employees;
#7、删除字段note
ALTER TABLE employees
DROP COLUMN note;
DESC employees;
#8、增加字段名favoriate_activity,数据类型为varchar(100)
ALTER TABLE employees
ADD favoriate_activity VARCHAR(100);
DESC employees;
#9、将表employees的名称修改为employees_info
RENAME TABLE employees 
TO employees_info;
/*
ALTER TABLE employees
RENAME TO employees_info
*/
#DESC employees;
DESC employees_info;
SHOW TABLES;

数据处理之增删改

在实际开发场景中,我们总是会不断向数据表中增加数据、修改数据、删除数据等等操作,这里涉及到了增删改操作,数据库语言核心是:增删改查

DML之添加数据

#数据增删改
#储备工作
USE atguigudb;
CREATE TABLE IF NOT EXISTS emp1(
id INT,
`name` VARCHAR(15),
hire_date DATE,
salary DOUBLE(10,2)
);
DESC emp1;
SELECT * 
FROM emp1;#空
#添加数据
#方式1:一条一条的添加数据
#①没有指明添加的字段
#正确的
INSERT INTO emp1
#如果没有写小数点,则会隐式转换
VALUES(1,'Tom','2000-12-21',3400.00);#一定要按声明的字段的先后顺序添加
SELECT * 
FROM emp1;#1条
#错误的
#INSERT INTO emp1
#VALUES(2,3400,'2000-12-21','Jerry');#一定要按声明的字段的先后顺序添加
SELECT * 
FROM emp1;#1条
#方式2:
INSERT INTO emp1(id,hire_date,salary,`name`)#类似于Java的构造器
VALUES(2,'2000-12-21',3400,'Jerry');
SELECT * 
FROM emp1;#2条
INSERT INTO emp1(id,salary,`name`)
VALUES(3,4500.00,'shk');#其他没有指定的字段则为NULL,如果有默认值则为默认值
SELECT * 
FROM emp1;#3条 如果有约束的haul,则无法添加NULL 也就是 NOT NULL的非空约束(暂略)
#③
INSERT INTO emp1(id,`name`,salary)
VALUES 
(4,'zixuan',5000);
INSERT INTO emp1(id,`name`,salary)
VALUES 
(5,'美',99999); 
SELECT * FROM emp1;
#同时插入多条数据
INSERT INTO emp1 VALUES(6,'cat','2022-4-20',0.00),
(7,'tianyi','2012-7-12',8102.00);
SELECT * FROM emp1;
#方式2:将查询结果插入到表中
SELECT * FROM emp1;
INSERT INTO emp1(id,`name`,salary,hire_date)
#AS只能放到新表,而这个可以是新,也可以是旧
#查询语句
SELECT employee_id,last_name,salary,hire_date#查询的字段一定要与添加到的表的字段一一对应
FROM employees
WHERE department_id IN (70,60);#70 60 | 60 70顺序不影响结果
SELECT * FROM emp1;
DESC emp1;
DESC employees;
#说明:emp1表中要添加数据的字段的长度不能低于employees表中查询字段的长度
#否则容易精度丢失,可以更大,但是不能更小

在这里插入图片描述

DML之更新删除操作_MySQL8.0新特性之计算列

更新数据和删除数据

在这里插入图片描述
在这里插入图片描述

#更新数据(或修改数据)
SELECT*FROM emp1;SELECT*FROM emp1 WHERE id = 4;
#UPDATE ... SET ... WHERE ...
#可以实现批量修改数据
UPDATE emp1
SET hire_date = '2004-09-04' 
WHERE id = 4;#一定要添加条件,否则会全部修改掉
SELECT*FROM emp1;SELECT*FROM emp1 WHERE id = 4;
#同时修改一条数据的多个字段
UPDATE emp1
SET hire_date = CURDATE(),salary=6000
WHERE id = 5;
SELECT*FROM emp1;SELECT*FROM emp1 WHERE id = 5;
#题目:将表中姓名中包含字符a的提薪20%
UPDATE emp1
SET salary = salary*1.2
WHERE `name` LIKE '%a%';SELECT*FROM emp1;
#修改数据时,是可能存在不成功的情况的。原因:语法不对、有约束等等
UPDATE employees
SET department_id = 10000
WHERE employee_id = 102;
#删除数据 DELETE FROM ... WHERE ... 如果没有where,则会删除掉表中的所有数据
SELECT*FROM emp1;SELECT*FROM emp1 WHERE id = 1;
DELETE FROM emp1
WHERE id = 1;
SELECT*FROM emp1;
#由于约束影响,删除也是可能失败的
DELETE FROM departments
WHERE department_id = 50;
#小结:DML操作默认情况下,执行完以后都会自动提交数据
#如果希望执行完以后不自动提交数据,则需要使用 SET autocommit = FALSE;

MySQL8.0新特性计算列
在这里插入图片描述
在这里插入图片描述

#MySQL8.0新特性:计算列
USE atguigudb;
CREATE TABLE IF NOT EXISTS test1(
a INT,
b INT,
#GENERATED -> 生成|ALWAYS -> 总是|| VIRTUAL -> 虚拟
#这些关键字的单个翻译有点抽象,总翻译大概为 始终作为(a+b)虚拟生成
c INT GENERATED ALWAYS AS (a+b) VIRTUAL#此时c字段就作为了我们的计算列
);
INSERT INTO test1(a,b)
VALUES(10,20);
SELECT*FROM test1;DESC test1;
#更新数据
UPDATE test1#将a修改为100,我们只有一行,所以可以不加where条件
SET a = 100;
SELECT * FROM test1;#c变成了 120(a=100+b=20)

这关键字有点抽象,大概意思理解就行,该计算列的应用场景并不多,先知道有就行

DDL和DML的综合案列

#DDL和DML的综合案例
#1、创建数据库test01_library
CREATE DATABASE IF NOT EXISTS test01_library CHARACTER SET 'utf8';
USE test01_library;
#2、创建books表
CREATE TABLE IF NOT EXISTS books(
id INT,
`name` VARCHAR(50),
`authors` VARCHAR(100),
price FLOAT,
pubdate YEAR,
note VARCHAR(100),
num INT
);
DESC books;SELECT*FROM books;
#3、向books表中添加数据
INSERT INTO books
VALUES(1,'Tal of AAA','Dickes',23,'1995','novel',11),
(2,'EmmaT','Jane lura',35,'1993','joke',22),
(3,'Story of jane','Jane Tim',40,'2001','novel',0),
(4,'Lovey Day','George Byron',20,'2005','novel',30),
(5,'Old land','Honore Blade',30,'2010','law',0),
(6,'The Battle','Upton Sara',30,'1999','medicine',40),
(7,'Rose Hood','Richard haggard',28,'2008','cartoon',28);
SELECT*FROM books;
#4、将小说类型(novel)的书的价格都增加5
UPDATE books
SET price = price+5 
WHERE note = 'novel';
SELECT*FROM books;
#5、将名称为EmmaT的书的价格改为40,并将说明改为drama
UPDATE books
SET price = 40,note = 'drama' 
WHERE `name` = 'EmmaT';
SELECT*FROM books;
#6、删除库存为0的记录
DELETE FROM books 
WHERE num = 0;
SELECT * FROM books;
#补回删掉的两条数据
INSERT INTO books VALUES(3,'Story of jane','Jane Tim',40,'2001','novel',0),(5,'Old land','Honore Blade',30,'2010','law',0);
#7、统计书名中包含a字母的书
SELECT *
FROM books
WHERE `name` LIKE '%a%';
#8、统计书名中包含a字母的书的数量和库存总量
SELECT COUNT(*),SUM(num)
FROM books
WHERE `name` LIKE '%a%';
#9、找出"novel"类型的书,按照价格降序排列
SELECT *
FROM books
WHERE note = 'novel'
ORDER BY price DESC;
#10、查询图书信息,按照库存量降序排列,如果库存量相同的按照note升序排列
SELECT *
FROM books
ORDER BY num DESC,note ASC;
#11、按照note分类统计书的数量
SELECT note,COUNT(*)
FROM books
GROUP BY note;
#12、按照note分类统计书的库存量,显示库存量超过30本的
SELECT note,SUM(num)
FROM books
GROUP BY note
HAVING SUM(num) >= 30;
#13、查询所有图书,每页显示5本,显示第二页
SELECT*FROM books
LIMIT 5,5;
#14、按照note分类统计书的库存量,显示库存量最多的
SELECT note,SUM(num) sum_num
FROM books
GROUP BY note
ORDER BY sum_num DESC
LIMIT 0,1;
#15、查询书名达到10个字符的书,不包括里面的空格
SELECT `name`
FROM books
WHERE CHAR_LENGTH(REPLACE(`name`,' ','')) >= 10;
#16、查询书名和类型,其中note值为novel显示小说,law显示法律,medicine显示医药
#cartoon显示卡通,joke显示笑话
SELECT `name`,note,CASE note WHEN 'novel' THEN '小说'
				WHEN 'law' THEN '法律'
				WHEN 'medicine' THEN '医药'
				WHEN 'cartoon' THEN '卡通'
				WHEN 'joke' THEN '搞笑'
				ELSE '其他'
				END "类型"
FROM books;
#17、查询书名、库存,其中num值超过30本的,显示滞销,大于0并低于10的
#显示畅销,为0则显示需要无货
SELECT `name` AS "书名",num AS "库存",CASE WHEN num >30 THEN '滞销'
						WHEN num >0 AND num <10 THEN '畅销'
						WHEN num = 0 THEN '无货'
						ELSE '正常'
						END "显示状态"
FROM books;
#18、统计每一种note的库存量,并合并总量
SELECT IFNULL(note,'合计库存总量') AS note,SUM(num)
FROM books
GROUP BY note WITH ROLLUP;
#19、统计每一种note的数量,并合并总量
SELECT IFNULL(note,'合计库存总量') AS note,COUNT(*)
FROM books
GROUP BY note WITH ROLLUP;
#20、统计库存量前三名的图书
SELECT *
FROM books
ORDER BY num DESC
LIMIT 0,3;
#21、找出最早出版的一本书
SELECT * 
FROM books
ORDER BY pubdate ASC
LIMIT 0,1;
#22、找出novel中价格最高的一本书
SELECT * 
FROM books
WHERE note = 'novel'
ORDER BY price DESC
LIMIT 0,1;
#23、找出书名中字数最多的一本书,不包含空格
SELECT *
FROM books
ORDER BY CHAR_LENGTH(REPLACE(`name`,' ','')) DESC
LIMIT 0,1;

数据类型概述_字符集设置

MySQL中的数据类型

在这里插入图片描述
常见的属性
在这里插入图片描述

#MySQL数据类型
#关于属性:character set name

#创建数据库时指明字符集
CREATE DATABASE IF NOT EXISTS dbtest12 CHARACTER SET 'utf8';
USE dbtest12;
SHOW CREATE DATABASE dbtest12;
#创建表的时候,指明表的字符集
CREATE TABLE temp(
id INT
)CHARACTER SET 'utf8';
SHOW CREATE TABLE temp;
#创建表,指明字段时,可以指定字段的字符集
CREATE TABLE temp1(
id INT,
NAME VARCHAR(15) CHARACTER SET 'gbk'
);
SHOW CREATE TABLE temp1;
#没有指明,就向上指明 
#字段->表->库->ini配置文件
SHOW VARIABLES LIKE 'character_%';

数据类型精讲

整型数据类型讲解

类型介绍
整数类型一共有5种,包括TINYINT、SMALLINT、MEDIUMINT、INT(INTEGER)和BIGINT
区别如下:
在这里插入图片描述
在这里插入图片描述

#整型数据类型
#本章的内容测试建议使用MySQL5.7进行测试
#类型后面小括号里的数是表示类型宽度
USE dbtest12;#进入指定数据库
CREATE TABLE test_int1(
f1 TINYINT,
f2 SMALLINT,
f3 MEDIUMINT,
f4 INTEGER,
f5 BIGINT
);
DESC test_int1;
INSERT INTO test_int1(f1)
VALUES(12),(-12),(-128),(127);
SELECT*FROM test_int1;
#INSERT INTO test_int1(f1)超出了范围
#VALUES(128);
CREATE TABLE test_int2(
f1 INT,
f2 INT(5),#这里的宽度指定,意义不大
#显示宽度为5,当insert不足五位时,使用0填充
f3 INT(5) ZEROFILL#配合ZEROFILI才有意义 并且会自动添加 UNSIGNED 不为负数 无符号 因为是补充0
);
INSERT INTO test_int2(f1,f2)
VALUES(123,123),(123456,123456);#超出了长度,但是执行没有问题
SELECT * FROM test_int2;
DESC test_int2;
INSERT INTO test_int2(f3)
VALUES(123),(123456);#如果不足位数,则用0补充
SELECT * FROM test_int2;
DESC test_int2;
SHOW CREATE TABLE test_int2;
#关于类型宽度基本上没有什么大用,了解就行了

无符号
在这里插入图片描述
在这里插入图片描述
因为无符号,所以直接取值正数的范围,一共是10位

CREATE TABLE test_int3(
f1 INT UNSIGNED#无符号
);
DESC test_int3;
INSERT INTO test_int3
VALUES(2412321);
SELECT * FROM test_int3;
#INSERT INTO test_int3
#VALUES(4294967296);#4294967295是最大范围,这里超出了最大范围

扩展

在这里插入图片描述
一般都是用INT类型,在Java中也是这样
在这里插入图片描述
在这里插入图片描述

浮点数、定点数与类型讲解

浮点类型

浮点数和定点数类型的特定是可以处理小数,你可以把整数看成小数的一个特例。因此,浮点数和定点数的使用场景,比整数大多了。MySQL支持的浮点类型,分别是FLOAT、DOUBLE、REAL

  • FLOAT表示单精度浮点数
  • DOUBLE表示双精度浮点数
#浮点类型
CREATE TABLE test_double1(
f1 FLOAT,
f2 FLOAT(5,2),
f3 DOUBLE,
f4 DOUBLE(5,2)
);
DESC test_double1;
INSERT INTO test_double1(f1,f2)
VALUES(123.45,123.45);
SELECT * FROM test_double1;
INSERT INTO test_double1(f3,f4)
VALUES(123.45,123.456);#小数位四舍五入了 123.456 - > 123.46
INSERT INTO test_double1(f3,f4)
VALUES(123.45,1234.456);#Out of range value for column 'f4' at row 1
INSERT INTO test_double1(f3,f4)
VALUES(123.45,999.995);#Out of range value for column 'f4' at row 1
#测试FLOAT和DOUBLE的精度问题
CREATE TABLE test_double2(
f1 DOUBLE
);
INSERT INTO test_double2
VALUES(0.47),(0.44),(0.19);
SELECT SUM(f1)
FROM test_double2;#1.0999999999999999 ≠ 1.1
SELECT SUM(f1) = 1.1,1.1 = 1.1#1表示true
FROM test_double2;#0表示false

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
定点数类型
decimal(M,D)

#定点数类型
CREATE TABLE test_decimal1(
f1 DECIMAL,#默认10,0
f2 DECIMAL(5,2)#指明了 M,D 整数5位,小数2位
);
DESC test_decimal1;
INSERT INTO test_decimal1(f1)
VALUES(123),(123.45);#有四舍五入情况
SELECT * FROM test_decimal1;
INSERT INTO test_decimal1(f2)
VALUES(999.99);#f2的最大可添加数范围
SELECT * FROM test_decimal1;
INSERT INTO test_decimal1(f2)
VALUES(67.567);#四舍五入
SELECT * FROM test_decimal1;
INSERT INTO test_decimal1(f2)#超出了设定的最大范围
VALUES(1267.567);#Out of range value for column 'f2' at row 1
INSERT INTO test_decimal1(f2)
VALUES(999.995);#f2的最大可添加数范围,但是四舍五入是无法进行的
#修改上面测试精度表的字段类型
/*

#测试FLOAT和DOUBLE的精度问题
CREATE TABLE test_double2(
f1 DOUBLE
);

*/
ALTER TABLE test_double2
MODIFY f1 DECIMAL(5,2);
DESC test_double2;
#可以看到差距,定点数类型的相加是正常的
#精准1.1
SELECT * FROM test_double2;
SELECT SUM(f1)
FROM test_double2;
SELECT SUM(f1) = 1.1,1.1=1.1
FROM test_double2;#1表示 true 0表示false null表示其他情况

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
位类型
BIT

#位类型:BIT
CREATE TABLE test_bit1(
f1 BIT,
f2 BIT(5),
f3 BIT(64)#BIT(65) --> for column f3(max=64)
);
DESC test_bit1;
INSERT INTO test_bit1(f1)
VALUES(0),(1);
SELECT*FROM test_bit1;#b'0' b'1'
#b是SQLYong呈现的原因
#--------------------------
#Data too long for column 'f1' at row 1
INSERT INTO test_bit1(f1)
VALUES(2);#2是十进制的体现样式 如果十进制2转换为二进制2则是 10 占2位,而BIT默认只占一位,我们的f2可以插入的
INSERT INTO test_bit1(f2)
VALUES(31);
SELECT*FROM test_bit1;
INSERT INTO test_bit1(f2)#Data too long for column 'f2' at row 1
VALUES(32);#超过了5位

在这里插入图片描述
先说关于BIT类型查询区别
在SQLYong中,查询的BIT数据会带个b前缀
在这里插入图片描述
下面我们看看在cmd命令行中的样子,因为8.0显示有点问题,我们只看5.7的样子
5.7的样子
在这里插入图片描述

在这里插入图片描述
可以看到,以16进制的形式展现的数据
通过函数我们也可以展示指定的进制
在这里插入图片描述
在这里插入图片描述

SELECT BIN(f1),BIN(f2),#二进制展示数据函数
HEX(f1),HEX(F2)#十六进制
FROM test_bit1;

上面的命令行是8.0版本的,可能显示乱码是因为8.0需要显式指定进制,只是猜测,不去深究
在这里插入图片描述

SELECT f1+0,f2+0#开发中用的很少很少,先了解了解即可
FROM test_bit1;#此时+0以后,可以以十进制的方式显示数据

日期与时间类型

在上MySQL纯代码复习(上)中我们有介绍过关于日期与时间的函数,这里将介绍关于本质的类型详细

YEAR类型

#YEAR类型
CREATE TABLE test_year(
f1 YEAR,
f2 YEAR(4)
);
DESC test_year;#我们会发现即使是没有指定4,也会是4
INSERT INTO test_year(f1)
VALUES('2021'),(2022);
SELECT*FROM test_year;#最小1901 最大2155
INSERT INTO test_year(f1)
VALUES('2155');
INSERT INTO test_year(f1)
VALUES('2156');#Out of range value for column 'f1' at row 1
INSERT INTO test_year(f1)
VALUES('69'),('70');
INSERT INTO test_year(f1)
VALUES(0),('00');
SELECT*FROM test_year;#最小1901 最大2155

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
建议尽量写四位的YEAR

date类型
在这里插入图片描述

#date类型
CREATE TABLE test_date1(
f1 DATE
);
DESC test_date1;
INSERT INTO test_date1
VALUES('2020-10-01'),('20221001'),(20221001);
INSERT INTO test_date1
VALUES('00-01-01'),('000101'),('69-10-01'),('691001'),('70-01-01'),('700101'),('99-01-01'),('990101');
SELECT * FROM test_date1;
INSERT INTO test_date1
VALUES(000301),(690301),(700301),(990301);#存在隐式转换
INSERT INTO test_date1
VALUES(CURDATE()),(CURRENT_DATE()),(NOW());
SELECT * FROM test_date1;

time类型
在这里插入图片描述

#time类型
CREATE TABLE test_time1(
f1 TIME
);
DESC test_time1;
INSERT INTO test_time1
VALUES('2 12:30:29'),('12:35:29'),('12:40'),('2 12:40'),('1 05'),('45');
SELECT * FROM test_time1;
INSERT INTO test_time1
VALUES('123520'),(124011),(1210);
INSERT INTO test_time1
VALUES(NOW()),(CURRENT_TIME());
SELECT*FROM test_time1;

datetime类型
在这里插入图片描述

#datetime类型
CREATE TABLE test_datetime1(
dt DATETIME
);
DESC test_datetime1;
INSERT INTO test_datetime1
VALUES('2021-01-01 06:50:30'),('20210101065030');
INSERT INTO test_datetime1
VALUES('99-01-01 00:00:00'),('990101000000'),('20-01-01 00:00:00'),('200101000000');
INSERT INTO test_datetime1
VALUES(20200101000000),(200101000000),(19990101000000),(990101000000);
SELECT*FROM test_datetime1;

timestamp类型
在这里插入图片描述

CREATE TABLE test_timestamp1(
ts TIMESTAMP
);
DESC test_timestamp1;
INSERT INTO test_time1
VALUES('1999-01-01 03:04:50'),('19990101030450'),('99-01-01 03:04:05'),('99010103040');
INSERT INTO test_timestamp1
VALUES('2020@01@01@00@00@00'),('20@01@01@00@00@0');
INSERT INTO test_timestamp1
VALUES(CURRENT_TIMESTAMP()),(NOW());
SELECT*FROM test_timestamp1;
#Incorrect datetime value: '2038-01-20 03:14:07' for column 'ts' at row 1
INSERT INTO test_timestamp1
VALUES('2038-01-20 03:14:07');

TIMESTAMP和DATETIME的区别
在这里插入图片描述
在这里插入图片描述

#对比DATETIME和TIMESTAMP
CREATE TABLE temp_time(
d1 DATETIME,
d2 TIMESTAMP
);
INSERT INTO temp_time VALUES('2021-9-2 14:45:32','2021-9-2 14:45:52');
INSERT INTO temp_time VALUES(NOW(),NOW());
SELECT*FROM temp_time;
#修改当前的时区
SET time_zone = '+9:00';
SELECT*FROM temp_time;
SET time_zone = '+8:00';
SELECT*FROM temp_time;

文本字符串类型(含ENUM、SET)

在这里插入图片描述
CHAR和VARCHAR类型
在这里插入图片描述
char类型
在这里插入图片描述

#char类型
CREATE TABLE test_char1(
c1 CHAR,#默认只有一个字符
c2 CHAR(5)
);
DESC test_char1;
INSERT INTO test_char1(c1)
VALUES('a');
SELECT * FROM test_char1;
#Data too long for column 'c1' at row 1
INSERT INTO test_char1(c1)
VALUES('ab');#超出了指定的长度,添加失败
INSERT INTO test_char1(c2)#c2长度是5
VALUES('ab');
INSERT INTO test_char1(c2)#c2长度是5
VALUES('hello');
INSERT INTO test_char1(c2)#c2长度是5
VALUES('尚');
INSERT INTO test_char1(c2)#c2长度是5
VALUES('硅谷');
SELECT * FROM test_char1;
INSERT INTO test_char1(c2)#c2长度是5
VALUES('尚硅谷教育');
SELECT * FROM test_char1;
#字符不等于字节
#Data too long for column 'c2' at row 1
INSERT INTO test_char1(c2)#c2长度是5
VALUES('尚硅谷IT教育');#超过了5个字符
SELECT CONCAT(c2,'***')
FROM test_char1;#检索数据时,会把空格去掉
INSERT INTO test_char1(c2)
VALUES('ab  ');#自己写的空格也会被去掉
SELECT CHAR_LENGTH(c2)
FROM test_char1;

varchar类型

在这里插入图片描述
在这里插入图片描述

#varchar类型
CREATE TABLE test_varchar1(
`name` VARCHAR#错误 因为是可变长度的字符串
);
#Column length too big for column 'name' (max = 21845); use BLOB or TEXT instead
CREATE TABLE test_varchar2(
`name` VARCHAR(65535)
);
#最大值应该是 21845 --> 21845 *3 = 65535
CREATE TABLE test_varchar3(
`name` VARCHAR(5)
);
INSERT INTO test_varchar3
VALUES('尚硅谷'),('尚硅谷教育');#3个汉字 = 9个字节 3*3 
#Data too long for column 'name' at row 1
INSERT INTO test_varchar3
VALUES('尚硅谷IT教育');

TEXT类型
在这里插入图片描述

#Text类型
CREATE TABLE IF NOT EXISTS test_text(
tx TEXT
);
INSERT INTO test_text
VALUES('atguigu   ')
SELECT CHAR_LENGTH(tx)
FROM test_text;

在这里插入图片描述
ENUM类型
在这里插入图片描述

#enum类型
CREATE TABLE test_enum(
season ENUM('春','夏','秋','冬','unknow')#unknow表示未知 5选一 枚举法
);
INSERT INTO test_enum
VALUES('春'),('秋');
SELECT*FROM test_enum;
#Data truncated for column 'season' at row 1
#不能添加2个和非枚举值的数据
INSERT INTO test_enum
VALUES('春,秋');
INSERT INTO test_enum
VALUES('人');
#添加unknow值
INSERT INTO test_enum
VALUES('unknow');
SELECT*FROM test_enum;
INSERT INTO test_enum
VALUES('UNKNOW');#忽略大小写的
SELECT*FROM test_enum;
INSERT INTO test_enum
VALUES(1),('3');#通过索引值添加
SELECT*FROM test_enum;
INSERT INTO test_enum
VALUES(NULL);
SELECT*FROM test_enum;#没有限制字段非空,所以可以添加NULL

SET类型
在这里插入图片描述

#set类型
CREATE TABLE IF NOT EXISTS test_set(
s SET('A','B','C')
);
INSERT INTO test_set(s) VALUES('A'),('A,B');#可多选的
SELECT*FROM test_set;
#插入重复的SET类型成员时,MySQL会自动删除重复的成员
INSERT INTO test_Set(s) VALUES('A,B,C,A');
SELECT*FROM test_set;#并没有重复A
#Data truncated for column 's' at row 1
INSERT INTO test_Set(s) VALUES('A,B,C,D');
SELECT *
FROM test_set;#并没有D
#结合使用
CREATE TABLE temp_mu1(
gender ENUM('男','女'),
hobby SET('吃饭','睡觉','躺着','写代码')
);
INSERT INTO temp_mu1
VALUES('男','睡觉,躺着');
SELECT*FROM temp_mu1;
#只能选一个枚举值
INSERT INTO temp_mu1
VALUES('男,女','睡觉,躺着');#Data truncated for column 'gender' at row 1

二进制字符串类型

在这里插入图片描述
在这里插入图片描述

#BINARY与VARBINARY
CREATE TABLE test_binary1(
f1 BINARY,#默认1
f2 BINARY(3),
#f3 VARBINARY 和VARCHAR一样,必须指定长度
f4 VARBINARY(10)
);
DESC test_binary1;
INSERT INTO test_binary1(f1,f2)
VALUES('a','abc');
SELECT * FROM test_binary1;
#Data too long for column 'f1' at row 1
INSERT INTO test_binary1(f1)
VALUES('abc');
INSERT INTO test_binary1(f2,f4)
VALUES('ab','ab');
SELECT * FROM test_binary1;
SELECT LENGTH(f2),LENGTH(f4)
FROM test_binary1;#和CHAR于VARCAHR去呗不大

BLOB类型
在这里插入图片描述
在这里插入图片描述

#blob类型
CREATE TABLE IF NOT EXISTS test_blob1(
id INT,
img MEDIUMBLOB
);
INSERT INTO test_blob1(id)
VALUES(1001);
SELECT*FROM test_blob1;

在这里插入图片描述
上面是通过SQLYong的操作添加的图片

JSON类型
在这里插入图片描述

#json
CREATE TABLE test_json(
js json
);
INSERT INTO test_json(js)
#格式 key:valye
VALUES('{"name":"songhk","age":18,"address":{"province":"beijing","city":"beijing"}}');
SELECT js -> '$.name' AS `NAME`,js -> '$.age' AS age,js -> '$.address.province' AS province,
js -> '$.address.city' AS city
FROM test_json;

空间类型

小结及类型使用建议

在这里插入图片描述

约束

数据完整性与约束的分类

#约束
#如何查看表中的约束
SELECT*FROM information_schema.`TABLE_CONSTRAINTS`
WHERE table_name = '表名称';#语法格式
SELECT*FROM information_schema.`TABLE_CONSTRAINTS`
WHERE table_name = 'employees';

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

非空约束

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

CREATE DATABASE dbtest13;
USE dbtest13;
#NOT NULL(非空约束)
#在CREATE TABLE时添加约束
CREATE TABLE test1(
id INT NOT NULL,
last_name VARCHAR(15) NOT NULL,
email VARCHAR(25),
salary DECIMAL(10,2)
);
DESC test1;
INSERT INTO test1(id,last_name,email,salary)
VALUES(1,'TOM','tom@126.com',3400);
SELECT*FROM test1;
#Column 'last_name' cannot be null 非空约束
INSERT INTO test1(id,last_name,email,salary)
VALUES(2,NULL,'tom@126.com',3400);
SELECT*FROM test1;
#Column 'id' cannot be null 非空约束
INSERT INTO test1(id,last_name,email,salary)
VALUES(NULL,'Jerry','jerry@126.com',3400);
#错误代码: 1364
#Field 'last_name' doesn't have a default value
#如果没有赋值,则会先去查看该字段是否有默认值存在
INSERT INTO test1(id,email)
VALUES(2,'abc@126.com');
UPDATE test1
SET email = NULL
WHERE id = 1;
#Column 'last_name' cannot be null
UPDATE test1
SET last_name = NULL
WHERE id = 1;
#在ALTER TABLE时添加约束
SELECT*FROM test1;
DESC test1;
#Invalid use of NULL value 存在NULL值
ALTER TABLE test1
MODIFY email VARCHAR(25) NOT NULL;#有一条记录,email此时是空值
DESC test1;
UPDATE test1
SET email = 'tom@126.com'
WHERE id = 1;
ALTER TABLE test1
MODIFY email VARCHAR(25) NOT NULL;
DESC test1;#如果有多条记录,只要有一个是NULL值,就不允许修改
#在ALTER TABLE时删除约束
ALTER TABLE test1
MODIFY email VARCHAR(25) NULL;
DESC test1;#可以看到NULL从NO变成了YES

唯一性约束

用来限制某个字段的值不能重复
在这里插入图片描述
在这里插入图片描述
关键字:UNQIUE
特点
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

#unique(唯一约束)
#列级约束 vs 表级约束
#列级约束:将此约束声明在对应的字段后面
#表级约束:将表中所有的字段都声明完,在所有字段的后面声明的约束
#在CREATE TABLE时添加约束
CREATE TABLE test2(
id INT UNIQUE,#id普遍都是唯一非空的 列级约束
last_name VARCHAR(15),
#email varchar(25) unique,邮箱一般都是唯一的
email VARCHAR(25),
salary DECIMAL(10,2),#工资不唯一
#表级约束
#这单词好记着呢:con(包含头)str(字符串缩写)a(变量a)int(整形) str a int a = straint
#这名字含义:uk(unique约束)_test2(表名)_email(字段名)
CONSTRAINT uk_test2_eamil UNIQUE(email)#记得在上一行加逗号
#UNIQUE(email)也可以不起约束的名
);
DESC test2;
#查看表中的约束
SELECT * FROM information_schema.`TABLE_CONSTRAINTS`
WHERE table_name = 'test2';
#在创建唯一约束的时候,如果不给唯一约束命名,就默认和列名相同
INSERT INTO test2(id,last_name,email,salary)
VALUES(1,'Tom','tom@126.com',4500);
SELECT * FROM test2;#添加成功
#刻意不修改id字段
#错误代码: 1062
#Duplicate entry '1' for key 'id' 
INSERT INTO test2(id,last_name,email,salary)
VALUES(1,'Tom','tom1@126.com',4600);
#刻意不修改email字段
#错误代码: 1062
#Duplicate entry 'tom@126.com' for key 'uk_test2_eamil'
INSERT INTO test2(id,last_name,email,salary)
VALUES(1,'Tom1','tom@126.com',4600);
INSERT INTO test2(id,last_name,email,salary)
VALUES(2,'Tom1',NULL,4600);#传入NULL
SELECT*FROM test2;#唯一约束允许出现多个NULL值
DESC test2;
INSERT INTO test2(id,last_name,email,salary)
VALUES(3,'Tom2',NULL,4600);#传入NULL 仍然没有问题
#在ALTER TABLE时添加约束
DESC test2;
#先将不是唯一的值修改掉
UPDATE test2
SET salary = 5000
WHERE id = 3;
#方式1:添加形式
ALTER TABLE test2
ADD CONSTRAINT uk_test2_sal UNIQUE(salary);#add unique(salary)
DESC test2;#可以看到salary的key有了一个UNI的属性
#方式2:修改形式
ALTER TABLE test2
MODIFY last_name VARCHAR(15) UNIQUE;#有点像列级约束的形式
DESC test2;#可以看到last_name的key有了一个UNI的属性
#复合的唯一性约束
CREATE TABLE `USER`(
id INT,
`name` VARCHAR(15),
`password` VARCHAR(25),
#表级约束(其实只能用表级约束来声明复合约束)
CONSTRAINT uk_user_name_password UNIQUE(`name`,`password`)
);
DESC `USER`;
INSERT INTO `USER`
VALUES(1,'Tom','abc');
INSERT INTO `USER`
VALUES(1,'Tom1','abc');
SELECT *
FROM `USER`;
#案例:复合的唯一性约束案例
#学生表
CREATE TABLE student(
sid INT,#学生
sname VARCHAR(20),#姓名
tel CHAR(11) UNIQUE KEY,#电话
carid CHAR(18) UNIQUE KEY#身份证号
);
#课程表
CREATE TABLE course(
cid INT,#课程编号
cname VARCHAR(20)#课程名称
);
#选课表
CREATE TABLE student_course(
id INT,
sid INT,
cid INT,
scoure INT,#成绩
UNIQUE KEY(sid,cid)#复合唯一
#学生的课程成绩唯一性 -->符合 
#学号是唯一的,其次是课程编号,这样做可以保证成绩只有一条
);
INSERT INTO student VALUES(1,'张三','13710011002','101223199012015623');#成功
INSERT INTO student VALUES(2,'李四','13710011003','101223199012015624');#成功
INSERT INTO course VALUES(1001,'Java'),(1002,'MySQL');#成功
SELECT * FROM student;
SELECT * FROM course;
INSERT INTO student_course VALUES
(1,1,1001,89),#本表id 学生id 选课id 成绩
(2,1,1002,90),
(3,2,1001,88),
(4,2,1002,56);#成功
SELECT * FROM student_course;
#Duplicate entry '2-1002' for key 'sid'
INSERT INTO student_course VALUES
(5,2,1002,67);#不可以再次添加成绩 --> 复合约束
#-----------------------------------------------
#删除唯一性约束
#必须通过索引来删除,关键词为唯一索引名,和设置的约束名相同
#如果没有指定则和字段名相同,如果是复合函数,则以()中第一个字段名来命名
#先查看表中的约束
SELECT * FROM information_schema.`TABLE_CONSTRAINTS`
WHERE table_name = 'student_course';
SELECT * FROM information_schema.`TABLE_CONSTRAINTS`
WHERE table_name = 'test2';
#我们去删除test2的唯一约束,这个表里全部都是唯一约束
DESC test2;
SHOW CREATE TABLE test2;
#可以通过show index from 表名称;查看表的索引
SHOW INDEX FROM test2;
ALTER TABLE test2
DROP INDEX uk_test2_sal;
ALTER TABLE test2
DROP INDEX last_name;
SELECT * FROM information_schema.`TABLE_CONSTRAINTS`
WHERE table_name = 'test2';
SHOW INDEX FROM test2;

主键约束

在这里插入图片描述
也可以这么理解,主键用于唯一标识某一条记录,所以主键是绝对不可以为空或者重复的
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

USE dbtest13;
#主键约束
#PRIMARY KEY 
#primary key
#在CREATE TABLE时添加约束
CREATE TABLE test3(
id INT PRIMARY KEY,
last_name VARCHAR(15) PRIMARY KEY, #一个表中最多只能有一个主键约束
salary DECIMAL(10,2),
email VARCHAR(25)
);0
#主键约束特征:非空且唯一,用于唯一的标识条中的一条记录
CREATE TABLE test4(
id INT PRIMARY KEY,#列级约束创建
last_name VARCHAR(15),#primary key 一个表中最多只能有一个主键约束
salary DECIMAL(10,2),
email VARCHAR(25)
);
CREATE TABLE test5(
id INT,#
last_name VARCHAR(15),#primary key 一个表中最多只能有一个主键约束
salary DECIMAL(10,2),
email VARCHAR(25),
#表级约束	
CONSTRAINT pk_test5_id PRIMARY KEY(id)
);
SELECT * FROM information_schema.`TABLE_CONSTRAINTS`#PRIMARY KEY
WHERE table_name = 'test4';#PRIMARY
SELECT * FROM information_schema.`TABLE_CONSTRAINTS`#PRIMARY KEY
WHERE table_name = 'test5';#PRIMARY
INSERT INTO test4(id,last_name,salary,email)
VALUES(1,'Tom',4500,'tom@126.com');
SELECT * FROM test4;
#Duplicate entry '1' for key 'PRIMARY' 重复
INSERT INTO test4(id,last_name,salary,email)
VALUES(1,'Tom',4500,'tom@126.com');
#Column 'id' cannot be null 不能为NULL
INSERT INTO test4(id,last_name,salary,email)
VALUES(NULL,'Tom',4500,'tom@126.com');
CREATE TABLE `USER1`(
id INT,
`name` VARCHAR(15),
`password` VARCHAR(15),
PRIMARY KEY(`name`,`password`)
);
INSERT INTO `USER1`
VALUES(1,'Tom','abc');
INSERT INTO `USER1`
VALUES(1,'Tom1','abc');
SELECT * FROM `USER1`;
#Column 'name' cannot be null
INSERT INTO `USER1`
VALUES(1,NULL,'abc');
#在ALTER TABLE时添加约束
CREATE TABLE test6(
id INT,
last_name VARCHAR(15),
salary DECIMAL(10,2),
email VARCHAR(25)
);
DESC test6;
#为id添加主键约束
ALTER TABLE test6
ADD PRIMARY KEY(id);
DESC test6;
#删除主键约束(实际开发中,根本不会删除主键)
ALTER TABLE test6
DROP PRIMARY KEY;
DESC test6;

主键列自增策略

在这里插入图片描述
在这里插入图片描述

#主键列自增
#AUTO_INCREMENT
#auto_increment
/*
CREATE TABLE test7(
id int auto_increment,
last_name varchar(15) UNIQUE AUTO_INCREMENT
);
*/
CREATE TABLE test7(
id INT PRIMARY KEY AUTO_INCREMENT,
last_name VARCHAR(15)
);
INSERT INTO test7(last_name)
VALUES('Tom');#id不指定,默认自增从1开始
SELECT * FROM test7;#当我们向主键(包含AUTO_CREMENT)的字段上添加0或者NULL时,实际上会自动的往上添加指定的自增值
INSERT INTO test7(id,last_name)
VALUES(10,'Tom');
SELECT * FROM test7;
INSERT INTO test7(id,last_name)
VALUES(4,'Tom');
SELECT * FROM test7;
INSERT INTO test7(last_name)
VALUES('Tom');
SELECT * FROM test7;#并不能回溯自增长的值
#在ALTER TABLE时添加
CREATE TABLE test8(
id INT PRIMARY KEY,
last_name VARCHAR(15)
);
DESC test8;
ALTER TABLE test8
MODIFY id INT AUTO_INCREMENT;
DESC test8;
#在ALTER TABLE时删除
ALTER TABLE test8
MODIFY id INT;
DESC test8;

MySQL8.0新特性-自增变量的持久化
在5.7中演示
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
5.7重启服务器后,自增列会重新获取自增列值,根据表最后的自增列取值

在8.0中演示

在这里插入图片描述
5.7是放在内存中维护的,而8.0是放在一个重做日志里,是持久化的
在这里插入图片描述

外键约束

FOREIGN KEY
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

#MySQL8.0新特性-自增变量的持久化
#foreign key(外键约束)
#在CREATE TABLE时添加
#主表和从表:父表和子表
#*-------------
#①先创建主表
CREATE TABLE dept1(
dept_id INT,
dept_name VARCHAR(15)
);
#②再创建从表
#④创建
CREATE TABLE emp1(
emp_id INT PRIMARY KEY AUTO_INCREMENT,
emp_name VARCHAR(15),
department_id INT,
#表级约束
CONSTRAINT fk_emp1_dept_id 
FOREIGN KEY (department_id) 
REFERENCES dept1(dept_id)#REFERENCES -> 参照
);#要求从表参照主表的字段必须是主键
#上述操作报错,因为主表中dept_id上没有主键约束或唯一行约束
#③添加
ALTER TABLE dept1
ADD PRIMARY KEY (dept_id);
DESC dept1;
DESC emp1;
#演示外键效果
/*
Cannot add or update a child row: 
a foreign key constraint fails 
(`dbtest13`.`emp1`, CONSTRAINT `fk_emp1_dept_id` FOREIGN KEY (`department_id`) 
REFERENCES `dept1` (`dept_id`))

*/
INSERT INTO emp1
VALUES(1001,'Tom',10);
#为主表增加参照字段值
INSERT INTO dept1
VALUES(10,'IT');
#重新插入数据
INSERT INTO emp1
VALUES(1001,'Tom',10);
SELECT * FROM emp1;
#不能直接删除主表的数据,要先删除从表的数据
#Cannot delete or update a parent row: a foreign key CONSTRAINT fails
#删除失败
DELETE FROM dept1
WHERE dept_id = 10;
#更新失败
UPDATE dept1
SET dept_id = 20
WHERE dept_id = 10;#同上
#在ALTER TABLE时 添加外键约束
CREATE TABLE dept2(
dept_id INT PRIMARY KEY,
dept_name VARCHAR(15)
);
CREATE TABLE emp2(
emp_id INT PRIMARY KEY AUTO_INCREMENT,
emp_name VARCHAR(15),
department_id INT
);
ALTER TABLE emp2
ADD CONSTRAINT fk_emp2_dept_id FOREIGN KEY 
(department_id) REFERENCES dept2(dept_id);
SELECT * FROM information_schema.`TABLE_CONSTRAINTS`
WHERE table_name = 'emp2'; 	
#删除外键约束
#一个表可以声明有多个外键约束
USE atguigudb;
SELECT * FROM information_schema.`TABLE_CONSTRAINTS`
WHERE table_name = 'employees'; 
#---------------------------
USE dbtest13;
SELECT * FROM information_schema.`TABLE_CONSTRAINTS`
WHERE table_name = 'emp1'; 
ALTER TABLE emp1
DROP FOREIGN KEY fk_emp1_dept_id;
SELECT * FROM information_schema.`TABLE_CONSTRAINTS`
WHERE table_name = 'emp1'; 
#再手动的删除外键约束对应的普通索引
SHOW INDEX FROM emp1;
ALTER TABLE emp1
DROP INDEX fk_emp1_dept_id;
SHOW INDEX FROM emp1;

应用场景
在这里插入图片描述
在这里插入图片描述

检索约束与默认值约束

CHECK约束
在这里插入图片描述

#check约束
CREATE TABLE test10(
id INT,
last_name VARCHAR(15),
salary DECIMAL(10,2) CHECK(salary > 2000)
);
INSERT INTO test10
VALUES(1,'Tom1',2500);
INSERT INTO test10
VALUES(2,'Tom1',1500);#如果是8.0就会添加失败
SELECT*FROM test10;
DESC test10;

default约束

#DEFAULT约束
#在CREATE TABLE时添加默认值约束
CREATE TABLE test11(
id INT,
last_name VARCHAR(15),
salary DECIMAL(10,2) DEFAULT 2000
);
DESC test11;
INSERT INTO test11(id,last_name,salary)
VALUES(1,'Tom',3000);
SELECT * FROM test11;
INSERT INTO test11(id,last_name)
VALUES(2,'Tom1');
SELECT * FROM test11;#默认值是2000,没有赋值就是2000
#在ALTER TABLE添加约束
CREATE TABLE test12(
id INT,
last_name VARCHAR(15),
salary DECIMAL(10,2)
);
DESC test12;
ALTER TABLE test12
MODIFY salary DECIMAL(10,2) DEFAULT 2500;
DESC test12;
#在ALTER TABLE删除约束
ALTER TABLE test12
MODIFY salary DECIMAL(8,2);
DESC test12;

扩展
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

视图

什么是数据库对象?

在这里插入图片描述

视图的理解

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

创建视图

在这里插入图片描述
在这里插入图片描述

#准备工作
CREATE DATABASE dbtest14;
USE dbtest14;
CREATE TABLE emps
AS 
SELECT *
FROM atguigudb.`employees`;
CREATE TABLE depts
AS 
SELECT *
FROM atguigudb.`departments`;
#如何创建视图
#针对于单表
#下面就是创建了一个视图
#情况1:视图中的字段与基表的字段有对应关系
CREATE VIEW vu_emp1
AS
SELECT employee_id,last_name,salary
FROM emps;

#查看视图
SELECT*FROM vu_emp1;
#确实视图中字段名的方式1:
CREATE VIEW vu_emp2
AS
SELECT employee_id emp_id,last_name lname,salary#查询语句中,字段的别名会作为视图中字段的名称出现
FROM emps
WHERE salary > 8000;

SELECT * FROM vu_emp2;
#确定视图中字段名的方式2:
CREATE VIEW vu_emp3(emp_id,`name`,monthly_sal)#类型和个数必须相同
AS
SELECT employee_id,last_name,salary
FROM emps
WHERE salary > 8000;

SELECT*FROM vu_emp3;

#情况2:视图中的字段在基表中可能没有对应的字段
CREATE VIEW vu_emp_sal
AS
SELECT department_id,AVG(salary) avg_sal
FROM emps
WHERE department_id IS NOT NULL
GROUP BY department_id;

SELECT*FROM vu_emp_sal;

#针对于多表
CREATE VIEW vu_emp_dept
AS
SELECT e.employee_id,e.department_id,d.department_name
FROM emps e JOIN depts d
ON e.department_id = d.department_id

SELECT * FROM vu_emp_dept;

#利用视图对数据进行格式化
CREATE VIEW vu_emp_dept1
AS
SELECT CONCAT(e.last_name,"(",d.department_name,')') emp_info
FROM emps e JOIN depts d
ON e.department_id = d.department_id

SELECT * FROM vu_emp_dept1;

#基于视图创建视图
CREATE VIEW vu_emp4
AS
SELECT employee_id,last_name
FROM vu_emp1;

SELECT *
FROM vu_emp4;

#查看视图
#语法1:查看数据的表对象、视图对象
SHOW TABLES;#包含了表和视图
#语法2:查看视图的结构
DESC vu_emp1;
#语法3:查看视图的属性信息
SHOW TABLE STATUS LIKE 'vu_emp1';	
#语法4:查看视图的详细定义信息
SHOW CREATE VIEW vu_emp1;

更新视图数据与视图的删除

#先查看基表和视图中的数据,此时101号员工的工资
SELECT * FROM vu_emp1;
#基表或视图它们修改都会互相影响
SELECT employee_id,last_name,salary
FROM emps;
#更改视图中101号员工工资为20000,查询基表和视图发现都修改为了20000
UPDATE vu_emp1
SET salary = 20000
WHERE employee_id = 101;
#更改基表中101号员工工资为10000,查询视图和基表发现都修改为了10000
UPDATE emps
SET salary = 10000
WHERE employee_Id = 101;

DELETE FROM vu_emp1
WHERE employee_id = 101;#复制的表并没有复制来外键约束等等,可以直接删除
#先查看基表和视图中的数据,此时101已被删除
SELECT * FROM vu_emp1;
#基表或视图它们修改都会互相影响
SELECT employee_id,last_name,salary
FROM emps;
#上面一般情况下都是可以执行成功的


#不能更新视图中的数据
SELECT * FROM vu_emp_sal;

UPDATE vu_emp_sal
SET avg_sal = 5000;#The target table vu_emp_sal of the UPDATE is not updatable
DELETE FROM vu_emp_sal
WHERE department_id = 30;

#修改视图
DESC vu_emp1

#方式1
CREATE OR REPLACE VIEW vu_emp1
AS
SELECT employee_id,last_name,salary,email
FROM emps
WHERE salary > 7000;
DESC vu_emp1;

#方式2:
ALTER VIEW vu_emp1
AS
SELECT employee_id,last_name,salary,hire_date
FROM emps
DESC vu_emp1;

#删除视图
DESC vu_emp4;
DROP VIEW vu_emp4;
SHOW TABLES;
DROP VIEW vu_emp2,vu_emp3;
SHOW TABLES;

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

视图练习

#视图练习
USE dbtest14;
#练习1:
#1.使用表emps创建视图employee_vu,其中包括姓名(LAST_NAME),员工号(EMPLOYEE_ID),部门号(DEPARTMENT_ID)
CREATE OR REPLACE VIEW employee_vu(lname,emp_id,dept_id)
AS
SELECT last_name,employee_id,department_id
FROM emps;
#2.显示视图的结构
DESC employee_vu;
#3.查询视图中的全部内容
SELECT * FROM employee_vu;
#4.将视图中的数据限定在部门号是80的范围内
CREATE OR REPLACE VIEW employee_vu(lname,emp_id,dept_id)
AS
SELECT last_name,employee_id,department_id
FROM emps
WHERE department_id = 80;
#查看
SELECT * FROM employee_vu;


#练习2:
#1.创建视图emp_v1,要求查询电话号码以'011'开头的员工姓名和工资、邮箱
CREATE OR REPLACE VIEW emp_v1
AS
SELECT last_name,salary,email
FROM emps
WHERE phone_number LIKE '011%';
#2.要求将视图emp_v1修改为查询电话号码以'011'开头的并且邮箱中包含e字符的员工姓名和邮箱、电话号码
CREATE OR REPLACE VIEW emp_v1
AS
SELECT last_name,email,phone_number,salary
FROM emps
WHERE phone_number LIKE '011%' AND email LIKE '%e%';
SELECT*FROM emp_v1;
#3.向emp_v1插入一条记录,是否可以?
DESC emps;#非空约束,不允许插入NULL
INSERT INTO emp_v1#Field of view 'dbtest14.emp_v1' underlying table doesn't have a default value
VALUES('Tom','tom@126.com','01012345');
#4.修改emp_v1中员工的工资,每人涨薪1000
UPDATE emp_v1
SET salary = salary + 1000;
SELECT * FROM emp_v1;
#5.删除emp_v1中姓名为Olsen的员工
DELETE FROM emp_v1#如果有外键不一定能删
WHERE last_name = 'Olsen';
#6.创建视图emp_v2,要求查询部门的最高工资高于12000的部门id和其最高工资
CREATE OR REPLACE VIEW emp_v2(dept_id,max_sal)
AS
SELECT department_id,MAX(salary)
FROM emps
GROUP BY department_id
HAVING MAX(salary) > 12000;
SELECT * FROM emp_v2;
#7.向emp_v2中插入一条记录,是否可以?
INSERT INTO emp_v2(dept_id,max_sal)
VALUES(4000,20000);#The target table emp_v2 of the INSERT is not insertable-into
#8.删除刚才的emp_v2和emp_v1
DROP VIEW IF EXISTS emp_v1,emp_v2;
SHOW TABLES;

存储过程与存储函数

在这里插入图片描述

存储过程使用说明

在这里插入图片描述
在这里插入图片描述
特征
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
需要使用DILIMITER的原因是,首先我们在存储过程里定义多条不同的SQL语句,并且这些语句也都是需要分号来结束的,但是如果我们没有设置分隔符的话,到了第一条SQL语句结束就直接结束存储过程了,就会直接报错,所以我们需要区分开SQL语句结束和存储过程结束
在这里插入图片描述

存储过程的创建与调用

#存储过程的创建与调用
#0.准备工作
CREATE DATABASE dbtest15;
USE dbtest15;

CREATE TABLE employees
AS
SELECT * 
FROM atguigudb.`employees`;
CREATE TABLE departments
AS
SELECT *
FROM atguigudb.`departments`;
DESC employees;DESC departments;
#1.创建存储过程
#举例1:创建存储过程select_all_data(),查看emps表的所有数据
DELIMITER $
CREATE PROCEDURE select_all_data()
BEGIN
	SELECT * FROM employees;
END $ 
DELIMITER ;
#存储过程里不可以有注释
#上面第一行先将结束符号设置为美元符号,然后创建存储过程的begin end里表示存储了一条SQL语句,为了不让该语句默认结束,所以修改了结束符号
#在存储完后,再将结束符号修改回分号,调用存储过程时,分号就生效了
#2.存储过程的调用
CALL select_all_data();#使用CALL关键字调用
#举例2:创建存储过程avg_employee_salary(),返回所有员工的平均工资
DELIMITER //
CREATE PROCEDURE abg_employee_salary()
BEGIN 
	SELECT AVG(salary)
	FROM employees;
END //
DELIMITER ;
CALL abg_employee_salary();
#举例3:创建存储过程show_max_salary(),用来查看"emps"表的最高薪资值
DELIMITER //
CREATE PROCEDURE show_max_salary()
BEGIN
	SELECT MAX(salary)
	FROM employees;
END //
DELIMITER ;
CALL show_max_salary();

#带 OUT
#举例4:创建存储过程show_min_salary(),查看"emps"表的最低薪资值。并将最低薪资值
#通过OUT参数"ms"输出
DELIMITER //
CREATE PROCEDURE show_min_salary(OUT ms DOUBLE)
BEGIN
	SELECT MAX(salary) INTO ms
	FROM employees;
END //
DELIMITER ;
#调用,需要传入输出的变量
CALL show_min_salary(@ms);
CALL show_min_salary(@ds);#可以不参数同名
SELECT @ds FROM DUAL;
SELECT @ms;

#带IN
#举例5:创建存储过程show_someone_salary(),查看"emps"表中的某个员工的薪资,并用IN参数empname输入员工姓名
DELIMITER //
CREATE PROCEDURE show_someone_salary(IN empname VARCHAR(20))
BEGIN
	SELECT salary 
	FROM employees
	WHERE last_name = empname;
END //
DELIMITER ;
#上面参数的格式 IN name type
#调用 IN
#调用方式1:	
CALL show_someone_salary('Abel');
#调用方式2:
SET @empname := 'Abel';#自定义变量用一个@符号 系统是2个@@
CALL show_someone_salary(@empname);
SET @name := 'Abel'; #冒号等于是明示的赋值符号
CALL show_someone_salary(@name);#形参名仍然可以随意,个数和类型对就行

#IN+OUT
#举例6:创建存储过程show_someone_salary2(),查看"emps"表的某个员工的薪资
#并用IN参数empname输入员工姓名,用OUT参数empsalary输出员工薪资
DELIMITER //
CREATE PROCEDURE show_someone_salary2(IN empname VARCHAR(20),OUT empsalary DOUBLE)
BEGIN
	SELECT salary INTO empsalary
	FROM employees
	WHERE last_name = empname;
END //
DELIMITER ;
#调用
SET @empname := 'Abel';#如果是King的话,会报错,因为有2个King
CALL show_someone_salary2(@empname,@empsalary);
SELECT @empname;
SELECT @empsalary;

#INOUT
#举例7:创建存储过程show_mgr_name(),查询某个员工领导的姓名
#并用INOUT参数"empname"输入员工姓名,输出领导的姓名
DELIMITER $$
CREATE PROCEDURE show_mgr_name(INOUT empname VARCHAR(25))
BEGIN
	SELECT last_name INTO empname
	FROM employees
	WHERE employee_id = (
		SELECT manager_id
		FROM employees
		WHERE last_name = empname
		);
END $$
DELIMITER ;
SET @empname := 'Abel';#想多行执行就一定要写个分号
CALL show_mgr_name(@empname);
SELECT @empname;
#合着就是一个变量两用 既是形参又是返回值,感觉比IN + OUT 要简洁点

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

存储函数的创建与调用

#存储函数
#举例1:创建存储函数,名称为email_by_name(),参数定义为空,
#该函数查询Abel的email,并返回,数据类型为字符串类型
#下面是需要添加的特性,特性也只是解决方式的一种
	#DETERMINISTIC -> 确定性的
	#CONTAINS SQL -> 包含SQL的
	#READS SQL DATA -> 可读SQL的数据
DELIMITER //
CREATE FUNCTION email_by_name()
RETURNS VARCHAR(25)
	DETERMINISTIC
	CONTAINS SQL
	READS SQL DATA
BEGIN
	RETURN( SELECT email FROM employees
	WHERE last_name = 'Abel');
	
END //
DELIMITER ;
#还可以通过 SET GLOBAL log_bin-trust_function_creators = 1;

#调用
SELECT email_by_name();
#举例2:创建存储函数,名称为email_by_id(),参数插入emp_id,该函数查询emp_id的email
#并返回,数据类型为字符串型
#创建函数前,执行此语句,保证函数的创建会成功
SET GLOBAL log_bin_trust_function_creators = 1;

DELIMITER //
CREATE FUNCTION email_by_id(emp_id INT)
RETURNS VARCHAR(25)
BEGIN
	RETURN(SELECT email 
	FROM employees
	WHERE employee_id = emp_id); 
END //
DELIMITER ;

#调用
SELECT email_by_id(101);
#变量
SET @emp_id := 102;
SELECT email_by_id(@emp_id);

#举例3:创建存储函数count_by_id(),参数传入dept_id,该函数查询dept_id部门的
#员工人数,并返回,数据类型为整形
DELIMITER //
CREATE FUNCTION count_by_id(dept_id INT)
RETURNS INT 
BEGIN
	RETURN(
		SELECT COUNT(*)
		FROM employees
		WHERE department_id = dept_id
	);
END //

DELIMITER ;
#调用
SELECT count_by_id(50);
SET @dept_id := 30;
SELECT count_by_id(@dept_id);

80P
在这里插入图片描述
在这里插入图片描述

存储过程与存储函数的查看修改和删除

#存储过程、存储函数的查看
#1.使用SHOW CREATE语句查看存储过程和函数的创建信息
SHOW CREATE PROCEDURE show_mgr_name;

SHOW CREATE FUNCTION count_by_id;
#2.使用SHOW STATUS语句查看存储过程和存储函数的状态信息
SHOW PROCEDURE STATUS;#会全部列举出来

SHOW PROCEDURE STATUS LIKE 'show_max_salary';#模糊查询

SHOW FUNCTION STATUS LIKE 'email_by_id';
#3.从information_schema.Routines表查看存储过程和函数的信息
SELECT * FROM information_schema.`ROUTINES`
WHERE ROUTINE_NAME = 'email_by_id' AND ROUTINE_TYPE = 'FUNCTION'; #区分大小写的

SELECT * FROM information_schema.`ROUTINES`
WHERE ROUTINE_NAME = 'show_min_salary' AND ROUTINE_TYPE = 'PROCEDURE'; 

#存储过程、存储函数的修改
ALTER PROCEDURE show_max_salary
SQL SECURITY INVOKER
COMMENT '查询最高工资';

#删除
DROP FUNCTION IF EXISTS count_by_id;
DROP PROCEDURE IF EXISTS show_min_salary;

扩展

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

存储过程和存储函数练习

存储过程的练习

#存储过程和存储函数练习
#准备工作
CREATE DATABASE test15_pro_func;
USE test15_pro_func;
#1.创建存储过程insert_user(),实现传入用户名和密码,插入到admim表中
CREATE TABLE admin(
id INT PRIMARY KEY AUTO_INCREMENT,
user_name VARCHAR(15) NOT NULL,
pwd VARCHAR(25) NOT NULL
);
DELIMITER //
CREATE PROCEDURE insert_user(IN user_name VARCHAR(15),IN pwd VARCHAR(25))
BEGIN
	INSERT INTO admin(user_name,pwd)
	VALUES(user_name,pwd);
END //
DELIMITER ;
#调用
CALL insert_user('MP7','血腥运动-隐秘');
SET @user_name := 'P90';SET @pwd := '二西莫夫-隐秘';
CALL insert_user(@user_name,@pwd);
SELECT * FROM admin;
#2.创建存储过程get_phone(),实现传入女神编号,返回女神姓名和电话
CREATE TABLE beauty(
id INT PRIMARY KEY AUTO_INCREMENT,
`name` VARCHAR(15) NOT NULL,
phone VARCHAR(15) UNIQUE,
birth DATE
);
INSERT INTO beauty(`name`,phone,birth)
VALUES
('朱茵','13201233453','1982-02-12'),
('孙燕姿','13501233653','1980-12-09'),
('东雪莲','13651238755','1983-08-21'),
('邓紫棋','17843283452','1991-11-12'),
('刘若英','18635575464','1989-05-18'),
('杨超越','13761238755','1994-05-11');
SELECT * FROM beauty;

DELIMITER //
CREATE PROCEDURE get_phone(IN id INT,OUT `name` VARCHAR(15),OUT phone VARCHAR(15))
BEGIN 
	SELECT b.`name`,b.phone INTO `name`,phone
 	FROM beauty b
	WHERE b.id = id;
END //
DELIMITER ;

#调用
CALL get_phone(1,@`name`,@phone);
SELECT @`name`,@phone;

#3.创建存储过程date_diff(),实现传入两个女神生日,返回日期即那个大小

DELIMITER //
CREATE PROCEDURE date_diff(IN birth1 DATE,IN birth2 DATE,OUT sum_date INT)
BEGIN 
	SELECT DATEDIFF(birth1,birth2) INTO sum_date;
END //
DELIMITER ;
#上面相当于写成了通用函数,结果不需要从任何表来

#调用
SET @birth1 := '1992-09-08';
SET @birth2 := '1992-10-30';
CALL `date_diff`(@birth1,@birth2,@sum_date);
SELECT @sum_date;

#4.创建存储过程format_date(),实现传入一个日期,格式化成xx年xx月xx日并返回

DELIMITER //
CREATE PROCEDURE format_date(IN my_date DATE,OUT str_date VARCHAR(25))
BEGIN
	SELECT DATE_FORMAT(my_date,'%y年%m月%d日') INTO str_date;
END //
DELIMITER ;

#调用
CALL format_date('1999-10-10',@str_date);
SELECT @str_date;

#5.创建存储过程beauty_limit(),根据传入的起始索引和条目数,查询女神表的记录
DELIMITER //
CREATE PROCEDURE beauty_limit(IN start_index INT,IN size INT)
BEGIN
	SELECT *
	FROM beauty
	LIMIT start_index,size;
END //
DELIMITER ;

#调用
CALL beauty_limit(1,4);

#创建带inout模式参数的存储过程
#6.传入a和b两个值,最终a和b都翻倍返回
DELIMITER //
CREATE PROCEDURE add_double(INOUT a INT,INOUT b INT)
BEGIN 
	SET a = a*2;
	SET b = b*2;
END //
DELIMITER ;

#调用
SET @a = 3,@b = 5;
CALL add_double(@a,@b);
SELECT @a,@b;

#7.删除题目5的存储过程
DROP PROCEDURE IF EXISTS beauty_limit;

#8.查看题目6中存储过程的信息
SHOW CREATE PROCEDURE add_double;

SHOW PROCEDURE STATUS LIKE 'add_double';

存储函数的练习

#存储函数的练习
#0.准备工作
USE test15_pro_func;
CREATE TABLE employees
AS
SELECT * FROM atguigudb.employees;
CREATE TABLE departments
AS
SELECT * FROM atguigudb.`departments`;

#无参返回
#1.创建函数get_count(),返回公司的员工个数
DELIMITER //
CREATE FUNCTION get_count()
RETURNS INT 
BEGIN 
	RETURN(SELECT COUNT(*) FROM employees);
END //
DELIMITER ;
#如果没有首次创建函数则需要去设置一下关于信息表的全局活动表函数创建的设置
#调用
SELECT get_count() FROM DUAL;#和普通函数调用没有区别

#有参返回
#2.创建函数ename_salary(),根据员工姓名,返回它的工资

DELIMITER //
CREATE FUNCTION ename_salary(last_name VARCHAR(15))
RETURNS DOUBLE
BEGIN 
	RETURN(SELECT salary 
	FROM employees e
	WHERE e.last_name = last_name);
END //
DELIMITER ;

#调用
SELECT ename_salary('Abel');

#3.创建函数dept_sal(),根据部门名,返回该部门的平均工资

DELIMITER //
CREATE FUNCTION dept_sal(dept_name VARCHAR(15))
RETURNS DOUBLE
BEGIN
	RETURN(SELECT AVG(e.salary)
	FROM employees e JOIN departments d
	ON e.department_id = d.department_id
	WHERE d.department_name = dept_name);
END //
DELIMITER ;

#调用
SELECT dept_sal('IT');

#4.创建函数add_float(),实现传入两个float,返回二者之和
DELIMITER //
CREATE FUNCTION add_float(value1 FLOAT,value2 FLOAT)
RETURNS FLOAT
BEGIN
	RETURN(SELECT value1+value2);
END //
DELIMITER ;

#调用
SELECT add_float(1,2);
SET @v1 := 1.2;
SET @v2 := 2.4;
SELECT add_float(@v1,@v2); 

变量、流程控制、游标

系统变量

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

#变量
#1.变量:系统变量(全局系统变量、会话系统变量) vs 用户自定义变量

#2.查看全局系统变量
SHOW GLOBAL VARIABLES;
#查看会话系统变量
SHOW SESSION VARIABLES;

SHOW VARIABLES;#默认会话系统变量

#查询部分系统变量

SHOW GLOBAL VARIABLES LIKE 'admin_%';

#查看指定系统变量
SELECT @@global.max_connections;
#全局的系统变量 session会报错
SELECT @@session.max_connections;

SELECT @@session.character_set_client;

SELECT @@session.pseudo_thread_id;

SELECT @@character_set_client;#先查看会话系统变量,再查询去哪聚系统变量

#修改系统变量的值

#方式1:
SET @@global.max_connections = 161;
#方式2:
SET GLOBAL max_connections = 171;
SELECT @@global.max_connections;#针对于当前的数据库实例是有效的
#一旦重启MySQL服务,就失效了

#会话系统变量:
#方式1:
SET @@session.character_set_client = 'gbk';
#方式2:
SET SESSION character_set_client = 'utf8';
SELECT @@session.character_set_client;

#针对与当前会话是有效的,一旦结束会话,重新建立起新的会话,就失效了

用户变量

会话变量

#成员变量
/*
①用户变量:会话用户变量 vs 局部变量
②会话用户变量:使用@开头,作用域为当前会话
*/
/*
①变量的声明和赋值
#方式1:"="或":="
SET @用户变量 = 值;
SET @用户变量 := 值;

#方式2:":=" 或INTO关键字
SELECT @用户变量 := 表达式 [FROM 等子句]
SELECT 表达式 INTO @用户变量 [FROM 等子句]

②使用
SELECT @变量名
*/
#准备工作
CREATE DATABASE dbtest16;

USE dbtest16;

CREATE TABLE employees
AS
SELECT * FROM atguigudb.`employees`;
CREATE TABLE departments
AS
SELECT * FROM atguigudb.`departments`;
#---------------------------------------------
#测试:

#方式1:
SET @m1 = 1;
SET @m2 := 2;
SET @sum := @m1 + @m2;
SELECT @m1,@m2,@sum;

#方式2:
SELECT @count := COUNT(*) FROM employees; 
SELECT @count;

SELECT AVG(salary) INTO @avg_sal FROM employees;
SELECT @avg_sal;#保存在了会话中,不会一次消失

在这里插入图片描述

局部变量
在这里插入图片描述
在这里插入图片描述

#局部变量

/*
1、局部变量必须满足:
局部变量必须①使用DECLARE声明 ②声明并使用在BEGIN ... END 中(使用在存储过程、存储函数中)
①DECLARE的方式声明的局部变量必须声明在BEGIN中的首行的位置
2、声明格式:
DECLARE 变量名 类型 [default 值]:#如果没有DEFAULT子句,初始值为NULL
3、赋值:
方式1:
SET 变量名 = 值;
SET 变量名 := 值;
方式2:
SELECT 字段名或表达式 INTO 变量名 FROM 表;
4、使用
SELECT 局部变量名;
*/

#举例:
DELIMITER //
CREATE PROCEDURE test_var()
BEGIN
	#声明局部变量
	DECLARE a INT DEFAULT 0;
	DECLARE b INT;
	#DECLARE a,b int default 0;
	DECLARE emp_name VARCHAR(25);
	#赋值
	SET a = 1;
	SET b := 2;
	
	SELECT last_name INTO emp_name  FROM employees WHERE employee_id = 101;
	
	#使用
	SELECT a,b,emp_name;
END //
DELIMITER ;
#调用存储过程
CALL test_var();

#举例1:声明局部变量,并分别赋值为employees表中employee_id为102的last_name和salary
DELIMITER //
CREATE PROCEDURE test_pro()
BEGIN
	#声明
	DECLARE emp_name VARCHAR(25);
	DECLARE sal DOUBLE(10,2) DEFAULT 0.0;
	#赋值
	SELECT last_name,salary INTO emp_name,sal
	FROM employees
	WHERE employee_id = 102;
	#使用
	SELECT emp_name,sal;
END //
DELIMITER ;
CALL test_pro();

#举例2:声明两个变量,求和并打印(分别使用会话用户变量、局部变量的方式实现)
#方式1:使用会话用户变量
SET @v1 = 10;
SET @V2 := 20;
SET @result := @v1 + @v2;
#查看
SELECT @result;
#方式2:使用局部变量
DELIMITER //
CREATE PROCEDURE add_value() 
BEGIN 
	#声明
	DECLARE value1,value2,`sum` INT;
	#赋值
	SET value1 = 10;
	SET value2 := 100;
	SET `sum` = value1 + value2;
	#使用
	SELECT `sum`;
END //
DELIMITER ;
#调用存储过程
CALL add_value();
#举例3:创建存储过程"different_salary"查询某员工和他领导的薪资差距,并用IN参数emp_id接收员工id
#用OUT参数dif_salary输出薪资差距结果
DELIMITER //
CREATE PROCEDURE different_salary(IN emp_id INT,OUT dif_salary DOUBLE)
BEGIN
	#分析:查询出emp_id员工的工资;查询出emp_id员工的管理者的id;
	#查询管理者id的工资;计算两个工资的差值
	#声明变量
	DECLARE emp_sal DOUBLE DEFAULT 0.0;#记录员工的工资
	DECLARE mgr_sal DOUBLE DEFAULT 0.0;#记录管理者的工资
	DECLARE mgr_id INT DEFAULT 0;#记录管理者的id
	#赋值
	SELECT salary INTO emp_sal FROM employees WHERE employee_id = emp_id;
	SELECT manager_id INTO mgr_id FROM employees WHERE employee_id = emp_id;
	SELECT salary INTO mgr_sal FROM employees WHERE employee_id = mgr_id;
	SET dif_salary = mgr_sal - emp_sal;
END //
DELIMITER ;
#调用
SET @emp_id := 102;#会话用户变量
SET @dif_sal := 0;
CALL different_salary(@emp_id,@dif_sal); 
SELECT @dif_sal;

程序出错的处理机制

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

#定义条件和处理程序
#演示:
#Field 'email' doesn't have a default value 非空约束
INSERT INTO employees(last_name)
VALUES('Tom');
#错误演示:
DELIMITER //
CREATE PROCEDURE UpdateDataNoCondition()
	BEGIN
		SET @x = 1;
		UPDATE employees SET email = NULL WHERE last_name = 'Abel';
		SET @x = 2;
		UPDATE employees SET email = 'aabbel' WHERE last_name = 'Abel';
		SET @x = 3;
	END //
DELIMITER ;

#调用存储过程
CALL UpdateDataNoCondition();#错误代码: 1048 Column 'email' cannot be null
SELECT @x;# x = 1;
#定义条件
#格式:DECLARE 错误名称 CONDITION FOR 错误码(或错误条件)

#举例1:定义"Field_Not_Be_NULL"错误名与MySQL中违反非空约束的错误类型是"ERROR 1048(23000)"对应
#方式1:使用MySQL_error_code
DECLARE Field_Not_Be_NULL CONDITION FOR 1048;
#方式2:使用sqlstate_value
DECLARE Field_Not_Be_NULL CONDITION FOR SQLSTATE '23000';#需要添加SQLSTATE 否则隐式转换且为方式1

#举例2:定义"ERROR 1148(42000)"错误,名称为command_not_allowed
#方式1:使用MySQL_error_code
DECLARE command_not_allowed CONDITION FOR 1148;
#方式2:使用sqlstate_value
DECLARE command_not_allowed CONDITION FOR SQLSTATE '42000';

#定义处理程序
#格式:DECLARE 处理方式 HANDLER FOR 错误类型 处理语句

#方式1:捕获sqlstate_value
DECLARE CONTINUE HANDLER FOR SQLSTATE '42SO2' SET @info = 'NO_SUCH_TABLE'

#方式2:捕获mysql_error_value
DECLARE CONTINUE HANDLER FOR 1146 SET @info = 'NO_SUCH_TABLE';

#方法3:先定义条件,再调用
DECLARE no_such_table CONDITION FOR 1146;
DECLARE CONTINUE HANDLER FOR NO_SUCH_TABLE SET @info= 'NO_SUCH_TABLE';

#方法4:使用SQLWARNING
DECLARE EXIT HANDLER FOR SQLWARNING SET @info = 'ERROR';

#方法5:使用NOT FOUND
DECLARE EXIT HANDLER FOR NOT FOUND SET @info = 'NO_SUCH_TABLE';

#方法6:使用SQLEXCEPTION
DECLARE EXIT HANDLER FOR SQLEXCEPTION SET @info = 'ERROR';


#案例的处理
DROP PROCEDURE UpdateDataNoCondition;

#重定义存储过程

DELIMITER //
CREATE PROCEDURE UpdateDataNoCondition()
	BEGIN
		#声明处理程序
		#处理方式1:
		# DECLARE关键字 +CONTINUE(处理方式=跳过)HANDLER FOR(关键字) 1048(错误类型=违反非空约束) set @prc_value = 1(处理语句)
		DECLARE CONTINUE  HANDLER FOR 1048 SET @prc_value = -1;
		#处理方式2:
		#DECLARE CONTINUE  HANDLER FOR SQLSTATE '23000' SET @prc_value = -1;
		SET @x = 1;
		UPDATE employees SET email = NULL WHERE last_name = 'Abel';
		SET @x = 2;
		UPDATE employees SET email = 'aabbel' WHERE last_name = 'Abel';
		SET @x = 3;
	END //
DELIMITER ;
#调用存储过程
CALL UpdateDataNoCondition();
#查看变量
SELECT @x,@prc_value;#@prc_value = 3;说明跳过了错误语句并且执行到了最后才结束

#再举一个列子:
#创建一个名称为"InsertDataWithCondition"的存储过程
CREATE TABLE IF NOT EXISTS departments
AS 
SELECT *
FROM atguigudb.`departments`;
DESC departments;
ALTER TABLE departments
ADD CONSTRAINT uk_dept_name UNIQUE(department_id);

#定义存储过程
DELIMITER //
CREATE PROCEDURE InsertDataWithCondition()
	BEGIN
		SET @x = 1;
		INSERT INTO departments(department_name) VALUES('测试');
		SET @x = 2;
		INSERT INTO departments(department_name) VALUES('测试');
		SET @x = 3;
	END //
DELIMITER ;
#调用存储过程
#错误代码: 1062
#Duplicate entry '0' for key 'uk_dept_name'
CALL InsertDataWithCondition();
SELECT @x;#2
#删除此存储过程
DROP PROCEDURE InsertDataWithCondition;
#重新定义存储过程(考虑到错误的处理程序)

DELIMITER //
CREATE PROCEDURE InsertDataWithCondition()
	BEGIN
		#处理程序
		#方式1:
		#declare exit HANDLER FOR 1062 set @pro_value = -1;
		#方式2:
		#declare exit HANDLER FOR sqlstate '23000' set @pro_value = -1;
		#方式3
		#定义条件:先给错误起个名字
		DECLARE duplicate_entry CONDITION FOR 1062;
		DECLARE EXIT HANDLER FOR duplicate_entry SET @pro_value = -1;
		SET @x = 1;
		INSERT INTO departments(department_name) VALUES('测试');
		SET @x = 2;
		INSERT INTO departments(department_name) VALUES('测试');
		SET @x = 3;
	END //
DELIMITER ;
#调用存储过程
CALL InsertDataWithCondition();
SELECT @x,@pro_value;

分支结构IF的使用

在这里插入图片描述
在这里插入图片描述

#流程控制
#分支结构之 IF

#举例1:
DELIMITER //
CREATE PROCEDURE test_if()
BEGIN
	#情况1:
	#声明局部变量
	#declare stu_name varchar(15);
	#IF stu_name is null
	#	then select 'stu_name is null';
	#end IF;
	#情况2:二选一
	#DECLARE email varchar(25) default 'aaa';
	#if email is null 
	#	then select 'email is null';
	#else
	#	select 'email is not null';
	#end if;
	#情况3:多选一
	DECLARE age INT DEFAULT 20;
	IF age > 40
		THEN SELECT '中老年';
	ELSEIF age > 18
		THEN SELECT '青壮年';
	ELSEIF age > 8
		THEN SELECT '青少年';
	ELSE	
		SELECT '婴幼儿';
	END IF;
		
END //
DELIMITER ;
#调用
CALL test_if();
DROP PROCEDURE IF EXISTS test_if;
CALL test_if();

#举例2:声明存储过程"update_salary_by_eid1",定义IN参数emp_id,输入员工编号
#判断该员工薪资如果低于8000元并且入职时间超过5年,就涨薪500元,否则就不变
DELIMITER //
CREATE PROCEDURE update_salary_by_eid1(IN emp_id INT)
	BEGIN
		#声明局部变量
		DECLARE emp_sal DOUBLE;
		DECLARE hire_year DOUBLE;
		#赋值
		SELECT salary INTO emp_sal FROM employees WHERE employee_id = emp_id;
		SELECT DATEDIFF(CURDATE(),hire_date)/365 INTO hire_year FROM employees WHERE employee_id = emp_id;
		#判断
		IF emp_sal < 8000 AND hire_year >= 5
			THEN UPDATE employees SET salary = salary + 500 WHERE employee_id = emp_id;
		END IF;
	END //
DELIMITER ;
#调用存储过程
CALL update_salary_by_eid1(104);
SELECT salary,employee_id
FROM employees
WHERE employee_id = 104;
#查看
SELECT DATEDIFF(CURDATE(),hire_date)/365,employee_id,salary 
FROM employees
WHERE salary < 8000 AND DATEDIFF(CURDATE(),hire_date)/365 >= 5;
#举例3:声明存储过程"update_salary_by_eid2",定义IN参数emp_id,输入员工编号
#判断该员工薪资如果低于9000元且入职时间超过5年,就涨薪500元;否则就涨薪100元
DELIMITER //
CREATE PROCEDURE update_salary_by_eid2(IN emp_id INT)
	BEGIN
		#声明局部变量
		DECLARE emp_sal DOUBLE;
		DECLARE hire_year DOUBLE;
		#赋值
		SELECT salary INTO emp_sal FROM employees WHERE employee_id = emp_id;
		SELECT DATEDIFF(CURDATE(),hire_date)/365 INTO hire_year FROM employees WHERE employee_id = emp_id;
		#判断
		IF emp_sal < 9000 AND hire_year >= 5
			THEN UPDATE employees SET salary = salary + 500 WHERE employee_id = emp_id;
		ELSE 
			UPDATE employees SET salary = salary + 100 WHERE employee_id = emp_id;
		END IF;
	END //
DELIMITER ;
#调用
CALL update_salary_by_eid2(103);
CALL update_salary_by_eid2(104);
#查看
SELECT * FROM employees
WHERE employee_id IN (103,104);

#举例4:声明存储过程"update_salary_by_eid3",定义IN参数emp_id,输入员工编号
#判断该员工薪资如果低于9000元,就更新薪资为9000元;薪资如果大于等于9000元且低于10000的
#但是奖金比列为NULL的,就更新奖金比例为0.01;其他的涨薪100元
DELIMITER //
CREATE PROCEDURE update_salary_by_eid3(IN emp_id INT)
	BEGIN
		#声明变量
		DECLARE emp_sal DOUBLE;#记录员工工资
		DECLARE bonus DOUBLE;#记录员工奖金率
		#赋值
		SELECT salary INTO emp_sal FROM employees WHERE employee_id = emp_id;
		SELECT commission_pct INTO bonus FROM employees WHERE employee_id = emp_id;
		#判断
		IF emp_sal < 9000 
			THEN UPDATE employees SET salary = 9000 WHERE employee_id = emp_id;
		ELSEIF emp_sal < 10000 AND bonus IS NULL
			THEN UPDATE employees SET commission_pct = 0.01 WHERE employee_id = emp_id;
		ELSE 
			UPDATE employees SET salary = salary + 100 WHERE employee_id = emp_id;
		END IF;
	END //
DELIMITER ;

#调用
CALL update_salary_by_eid3(102);
SELECT salary,employee_id
FROM employees
WHERE employee_id = 102;

分支结构CASE的使用

#CASE的使用
#举例
DELIMITER //
CREATE PROCEDURE test_case() 
	BEGIN
		#演示1:case ... when ...
		/*		
		declare var INT default 2;
		case var 
			when 1 then select 'var = 1';
			when 2 then select 'var = 2';
			when 3 then select 'var = 3';
			else select 'other value';
		end case;
		*/
		#演示2:case when ... then ...
		DECLARE var1 INT DEFAULT 10;
		CASE 
		WHEN var1 > 100 THEN SELECT '三位数';
		WHEN var1  >= 10 THEN SELECT '两位数';
		ELSE SELECT '个位位';
		END CASE;
	END//
DELIMITER ;
DROP PROCEDURE test_case;
#调用
CALL test_case();

#举例2:声明存储过程"update_salary_by_eid4",定义IN参数emp_id,输入员工编号
#判断该员工薪资如果低于9000元,就更新薪资为9000元,薪资大于等于9000元且低于10000的,
#但是奖金比列为NULL的,就更新奖金比例为0.01;其他的涨薪100元
DELIMITER //
CREATE PROCEDURE update_salary_by_eid4(IN emp_id INT)
	BEGIN
		#局部变量的声明
		DECLARE emp_sal DOUBLE;#记录员工的工资
		DECLARE bonus DOUBLE;#记录员工的奖金率
		#局部变量的赋值
		SELECT salary INTO emp_sal FROM employees WHERE employee_id = emp_id;
		SELECT commission_pct INTO bonus FROM employees WHERE employee_id = emp_id;
		CASE 
		WHEN emp_sal < 9000 THEN UPDATE employees SET salary = 9000 WHERE employee_id = emp_id;
		WHEN emp_sal < 10000 AND bonus IS NULL THEN UPDATE employees SET commission_pct = 0.01
		WHERE employee_id = emp_id;
		ELSE UPDATE employees SET salary = salary + 100 WHERE employee_id = emp_id;
		END CASE;
	END //
DELIMITER ;
#调用
CALL update_salary_by_eid4(103);
SELECT * FROM employees
WHERE employee_id = 103;

#举例3:声明存储过程update_salary_by_eid5,定义IN参数emp_id,输入员工编号
#判断该员工的入职年限,如果是0年,薪资涨50;如果是1年,薪资涨100;
#如果是2年,薪资涨200;如果是3年,薪资涨300;如果是4年,薪资涨400;其他的涨薪500
DELIMITER //
CREATE PROCEDURE update_salary_by_eid5(IN emp_id INT)
	BEGIN 
		#声明局部变量
		DECLARE hire_year INT;#记录员工入职工资的总时间(单位:年)
		#赋值
		SELECT ROUND(DATEDIFF(CURDATE(),hire_date)/365,0) INTO hire_year
		FROM employees WHERE employee_id = emp_id;
		#判断
		CASE hire_year 
			WHEN 0 THEN UPDATE employees SET salary = salary + 50 WHERE employee_id = emp_id;
			WHEN 1 THEN UPDATE employees SET salary = salary + 100 WHERE employee_id = emp_id;
			WHEN 2 THEN UPDATE employees SET salary = salary + 200 WHERE employee_id = emp_id;
			WHEN 3 THEN UPDATE employees SET salary = salary + 300 WHERE employee_id = emp_id;
			WHEN 4 THEN UPDATE employees SET salary = salary + 400 WHERE employee_id = emp_id;
			ELSE	UPDATE employees SET salary = salary + 500 WHERE employee_id = emp_id; 
		END CASE;
	END //
DELIMITER ;

CALL update_salary_by_eid5(101);

SELECT * FROM employees
WHERE employee_id = 101;

LOOP_WHILE_REPEAT三种循环结构

loop_leave
在这里插入图片描述

#循环结构之LOOP
/*
[loop_label:] LOOP
	循环执行的语句
END LOOP [loop_label]
*/
#举例1:
DELIMITER //
CREATE PROCEDURE test_loop()
BEGIN
	#声明局部变量
	DECLARE num INT DEFAULT 1;
	loop_label:LOOP
		#重新赋值
		SET num = num + 1;
		IF num >= 10 THEN LEAVE loop_label;
		END IF;
	END LOOP loop_label;
	#查看num值
	SELECT num;
END //
DELIMITER ;

#调用存储过程
CALL test_loop();#10

#举例2:当市场环境变好时,公司为了奖励大家,决定给大家涨工资
#声明存储过程"update_salary_loop()",声明OUT参数num,输出循环次数
#存储过程中实现循环给大家涨薪,薪资涨为原来的1.1倍。知道全工资的平均薪资达到12000结束,并统计循环次数
SELECT AVG(salary) FROM employees;


DELIMITER //
CREATE PROCEDURE update_salary_loop(OUT num INT)
BEGIN 
	#声明
	DECLARE avg_sal DOUBLE;#记录员工平均工资
	DECLARE loop_count INT DEFAULT 0;#记录循环次数
	#获取员工的平均工资
	SELECT AVG(salary) INTO avg_sal FROM employees;
	loop_label:LOOP
		#结束循环的条件
		IF avg_sal >=12000
			THEN LEAVE loop_label;
		END IF;
		#如果低于12000,更新员工的工资
		UPDATE employees SET salary = salary * 1.1;	
		#更新avl_sal变量值
		SELECT AVG(salary) INTO avg_sal FROM employees;
		#记录循环次数
		SET loop_count = loop_count + 1;
	END LOOP loop_label;
	#给num赋值
	SET num = loop_count;
END //
DELIMITER ;
#调用
SET @num := 0; 
CALL update_salary_loop(@num);
SELECT @num;
SELECT AVG(salary) FROM employees;

while
在这里插入图片描述



#循环结构之WHILE
/*
[while_label:]WHILE 循环条件 DO
	循环体
END WHILE [while_label];
*/

#举例1:
DELIMITER //
CREATE PROCEDURE test_while()
	BEGIN
		#初始化条件
		DECLARE num INT DEFAULT 1;
		#循环条件
		WHILE num <= 10 DO 
			#循环体
			#(略)
			#迭代条件
			SET num = num + 1;
		END WHILE;
		SELECT num;
	END //
DELIMITER ;
 
 CALL test_while();
 
 #举例2:市场环境不好时,公司为了渡过难关,决定暂时降低大家的薪资
 #声明存储过程"update_salary_while()",声明OUT参数num,输出循环次数
 #存储过程中实现循环给大家降薪,薪资降为原来的90%。直到公司的平均薪资
 #达到5000结束。并统计循环次数
DELIMITER //
CREATE PROCEDURE update_salary_while(OUT num INT)
BEGIN
	#声明变量
	DECLARE avg_sal DOUBLE ;#记录平均工资
	DECLARE while_count INT DEFAULT 0;#记录循环次数
	#赋值	
	SELECT AVG(salary) INTO avg_sal FROM employees;
	WHILE avg_sal > 5000 DO
		UPDATE employees SET salary = salary * 0.9;
		SET while_count = while_count + 1;
		SELECT AVG(salary) INTO avg_sal FROM employees;
	END WHILE;	
	#给num赋值
	SET num = while_count;	 
END //
DELIMITER ; 
#调用
CALL update_salary_while(@num);
SELECT @num;
SELECT AVG(salary) FROM employees;
/*
凡是循环结构,一定具备4个要素
1.初始化条件
2.循环条件
3.循环体
4.迭代条件
*/


repeat

在这里插入图片描述

#循环结构之REPEAT
/*
[repeat_label:]REPEAT
	循环体的语句
UNTIL 结束循环的条件表达式
END REPEAT [repeat_label]
*/
#举例1:
DELIMITER //
CREATE PROCEDURE test_repeat()
BEGIN
	#声明变量
	DECLARE num INT DEFAULT 1;
	REPEAT 
		SET num = num +1;
	UNTIL num >= 10
	END REPEAT;
	#查看
	SELECT num;
END //
DELIMITER ;

#调用
CALL test_repeat();
#举例2:当市场环境变好时,公司为了奖励大家,决定给大家涨工资
#声明存储过程"update_salary_repeat()",声明OUT参数num,输出循环次数
#存储过程中实现循环给大家涨薪,涨薪为原来的1.15倍。直到全公司的平均
#薪资达到13000结束,并统计循环次数
DELIMITER //
CREATE PROCEDURE update_salary_repeat(OUT num INT)
BEGIN
	#声明变量
	DECLARE avg_sal DOUBLE ;#记录平均工资
	DECLARE repeat_count INT DEFAULT 0;#记录循环次数
	#赋值 
	SELECT AVG(salary) INTO avg_sal FROM employees;
	REPEAT
		UPDATE employees SET salary = salary * 1.15;
		SET repeat_count = repeat_count + 1;
		SELECT AVG(salary) INTO avg_sal FROM employees;
		UNTIL avg_sal > 13000
	END REPEAT;	
	#给num赋值
	SET num = repeat_count;
END //
DELIMITER ;

#调用
CALL update_salary_repeat(@num);#至少执行一次 相当于Java的 do...while
SELECT @num;

LEAVE和ITERATE的使用

在这里插入图片描述
LEAVE的使用

在这里插入图片描述

#LEAVE的使用
#举例1:创建存储过程"leave_begin()",声明int类型的IN参数num。给BEGIN...END加标记名
#并在BEGIN...END中使用IF语句判断num参数的值
/*
如果num<=0,则使用LEAVE语句退出BEGIN...END
如果num=1,则查询“employees”表的平均薪资
如果num=2,则查询“employees”表的最低薪资
如果num>2,则查询“employees”表的最高薪资
IF语句结束后查询“employees”表的总人数
*/

DELIMITER //
CREATE PROCEDURE leave_begin(IN num INT)
begin_label:BEGIN
	IF num <= 0
		THEN LEAVE begin_label;
	ELSEIF num = 1
		THEN SELECT AVG(salary) FROM employees;
	ELSEIF num = 2
		THEN SELECT MIN(salary) FROM employees;
	ELSE
		SELECT MAX(salary) FROM employees;
	END IF;
	#查询总人数
	SELECT COUNT(*) FROM employees;
END //
DELIMITER ;	

#调用
SET @num :=3;
CALL leave_begin(@num);

#举例2:当市场环境不好时,公司为了渡过难关,决定暂时降低大家的薪资
#声明存储过程“leave_while()”,声明OUT参数num,输出循环次数,存储过程中使用WHILE
#循环给大家降低薪资为原来的90%,直到全公司平均薪资小于10000,并统计循环次数
DELIMITER //
CREATE PROCEDURE leave_while(OUT num INT)
leave_label:BEGIN
	#
	DECLARE avg_sal DOUBLE;#记录平均工资
	DECLARE while_count INT DEFAULT 0;#记录循环的次数
	SELECT AVG(salary) INTO avg_sal FROM employees;
	while_label:WHILE TRUE DO
		IF avg_sal <= 10000 THEN
			LEAVE while_label;
		END IF;
		UPDATE employees SET salary = salary * 0.9;
		SELECT AVG(salary) INTO avg_sal FROM employees;
		SET while_count = while_count + 1;
	END WHILE;
	#赋值
	SET num = while_count;

END //

DELIMITER ;

#调用
CALL leave_while(@num);
SELECT @num;

ITERATE的使用

在这里插入图片描述

在这里插入图片描述

#ITERATE的使用
/*
举例:定义局部变量num,初始值为0,循环结构中执行num+1操作
如果num<10,则继续执行循环
如果num>15,则退出循环结构


*/
DELIMITER //
CREATE PROCEDURE test_iterate()
BEGIN
	DECLARE num INT DEFAULT 0;
	loop_label:LOOP
		#赋值
		SET num = num+1;
		IF num < 10 
			THEN ITERATE loop_label;
		ELSEIF num > 15 
			THEN LEAVE loop_label;
		END IF;
		SELECT '2022-12-6号 冻成狗';
	END LOOP;
END //
DELIMITER ;

#调用
CALL test_iterate();

游标的使用

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

#游标
SELECT * FROM employees;
#游标的使用
/*
游标使用的步骤
①声明游标
②打开游标
③使用游标(从游标中获取数据)
④关闭游标
*/

#举例:创建存储过程“get_count_by_limit_total_salary()”,声明IN参数 limit_total_salary,
#DOUBLE类型;声明OUT参数total_count,INT类型。函数的功能可以实现累加薪资最高的几个员工的薪资值
#直到薪资总和达到limit_total_salary参数的值,返回累加的人数给total_count 	
DELIMITER //
CREATE PROCEDURE get_count_by_limit_total_salary(IN limit_total_salary DOUBLE,OUT total_count INT)
BEGIN 
	#声明局部变量
	DECLARE sum_sal DOUBLE DEFAULT 0.0;#记录累加的薪资值
	DECLARE emp_sal DOUBLE;#记录每一个员工的工资
	DECLARE emp_count INT DEFAULT 0;#记录累加的人数
	
	#1.声明游标
	DECLARE emp_cursor CURSOR FOR SELECT salary FROM employees ORDER BY salary DESC;
	#2.打开游标
	OPEN emp_cursor;
	REPEAT 
		#3.使用游标	
		FETCH emp_cursor INTO emp_sal;
		SET sum_sal = sum_sal+emp_sal;
		SET emp_count = emp_count + 1;
		UNTIL sum_sal >= limit_total_salary
	END REPEAT;
	SET total_count = emp_count;
	#4.关闭游标
	CLOSE emp_cursor;
END //
DELIMITER ;

#调用
CALL  get_count_by_limit_total_salary(200000,@total_count);
SELECT @total_count;

在这里插入图片描述
扩展
在这里插入图片描述
也就是在变量前加一个persist -> 持续 坚持的关键字即可

触发器

触发器的基本介绍

在这里插入图片描述
在这里插入图片描述

触发器的创建

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

#触发器
#准备工作
CREATE DATABASE dbtest17;
USE dbtest17;
#创建触发器
#①创建数据表

CREATE TABLE test_trigger(
id INT PRIMARY KEY AUTO_INCREMENT,
t_note VARCHAR(20)
);

CREATE TABLE test_trigger_log(
id INT PRIMARY KEY AUTO_INCREMENT,
t_log VARCHAR(30)
);

#②查看表数据
SELECT * FROM test_trigger;

SELECT * FROM test_trigger_log;#表的日志

#创建触发器
#创建名称为before_insert_test_tri的触发器,向test_trigger数据表插入数据之前,
#向test_tirgger_log数据表中插入before_insert的日志信息;
DELIMITER //
CREATE TRIGGER before_insert_test_trigger
BEFORE INSERT ON test_trigger
FOR EACH ROW
BEGIN
	INSERT INTO test_trigger_log(t_log) 
	VALUES('before insert...');
END //
DELIMITER ;

#④测试

INSERT INTO test_trigger(t_note)
VALUES('Tom...');

SELECT * FROM test_trigger;

SELECT * FROM test_trigger_log;#表的日志

完结

触发器的详细使用和MySQL8.0新特性就不写了,因为是企业级路线,这些东西一般用不到,用到了再现场学一下就行了

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/66111.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

生产环境 Nginx后端服务大量TIME-WAIT的解决

netstat -n | awk /^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]} ss -s netstat -nat |awk {print $6}|sort|uniq -c|sort -rn 统计TIME_WAIT 连接的本地地址 netstat -an | grep TIME_WAIT | awk {print $4} | sort | uniq -c | sort -n -k1 尝试抓取 tcp 包 tcpd…

工业CT之三维重建技术

目前&#xff0c;国内现有的工业CT设备绝大多数是基于线阵探测器的断层扫描技术。 该技术主要是通过观察二维图像去发现单层断面上的损伤部位&#xff0c;至于能准确地确定损伤部位的空间位置、大小、几何形状等&#xff0c;仅通过观察二维切片图像是很难实现的。 这个时候就需…

Flink系列之Flink中Checkpoint容错机制

title: Flink系列 三、Flink Checkpoint 容错机制原理概述 ​ Flink 提供了 Exactly once 特性&#xff0c;是依赖于带有 barrier 的分布式快照 可部分重发的数据源功能实现的。而分布式快照中&#xff0c;就保存了 operator 的状态信息。 ​ Flink 的失败恢复依赖于 检查点…

Zabbix技术分享——如何使用zabbix监控华为云RDS

在数字化大背景下&#xff0c;数据是重要的生产资料&#xff0c;这些数据存放在哪里&#xff0c;如何保障数据安全是所有企业都要考虑的事情。华为云RDS凭借安全可靠&#xff0c;可根据业务规模动态扩容的特性&#xff0c;受到越来越多中小企业的青睐&#xff0c;对华为云RDS监…

NR PUSCH power control(一)

这篇看下NR PUSCH power control的相关内容&#xff0c;主要内容集中在38.213 7.1章节&#xff0c;功率计算无非就是一个长公式&#xff0c;根据RRC配置的参数及后续DCI field 的内容作出功率的调整&#xff1b;最初这部分看的就云里雾里的&#xff0c;最近再看&#xff0c;相比…

upload-labs通关

upload-labs通关 shell &#x1f349; 目录upload-labs通关PASS-01、PASS-02PASS-03PASS-04PASS-05PASS-06PASS-07PASS-08PASS-09PASS-10PASS-11PASS-12PASS-13PASS-14PASS-15PASS-16PASS-17PASS-18PASS-19PASS-20PASS-21shell能上传并能解析就算成功 PASS-01、PASS-02 图片…

最近要考pmp,哪个培训机构比较好?

你说的几个都是我着重了解过的&#xff0c;作为过来人&#xff0c;把我做的各大机构的优缺点给你参考吧~ PMP 机构排名的话&#xff0c;没有官方数据&#xff0c;网上数据仅供参考。这篇机构对比的文章&#xff0c;主流机构都有&#xff0c;你可以看看 下面说下我收集的每个机…

【数据库数据恢复】无法启动MongoDB服务的数据恢复案例

关于MongoDB数据库&#xff1a; MongoDB数据库存储方式是将文档存储在集合之中&#xff0c;而不是像Oracle、MySQL一样的关系型数据库。 MongoDB数据库是开源数据库&#xff0c;也提供具有附加功能的商业版本。 MongoDB中的数据是以键值对(key-value pairs)的形式显示的&…

[附源码]Python计算机毕业设计Django校友社交系统

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…

“云办公”如何用任务协同工具搞定项目和团队管理?

导语&#xff1a;远程参加会议、团队协同作业、项目负责人进行任务分配、团队成员多人协同编辑文件及同时推进项目、人力部门在线进行审批报销……&#xff0c;随着“云办公”的加速普及&#xff0c;人们只需一台电脑、一部手机、一根网线&#xff0c;就能随时进入办公状态&…

云服务器及域名到期后,公安联网注销指南

云服务器及域名到期后&#xff0c;公安联网注销指南 公安联网备案及注销的操作流程都写在了官方文档中&#xff0c;可以进入全国互联网安全管理服务平台&#xff0c;在下载中心找到并下载 《互联网站安全服务平台操作指南》&#xff0c;按照操作指南进行备案及撤销。 以下图…

JS实现关闭图片窗口

JS实现关闭图片窗口 有趣的小案例池子&#xff1a; JS实现定时器 JS实现关闭图片窗口 JS实现输入检验 获取焦点后隐藏提示内容的输入框 JS实现获取鼠标在画布中的位置 聊天信息框显示消息 JS点击切换背景图 自动切换背景的登录页面 JS制作跟随鼠标移动的图片 JS实现记住用…

K8S Pod控制器详细讲解

文章目录一、Pod控制器介绍二、ReplicaSet(RS)三、Deployment(Deploy)1.镜像更新&#xff1a;2.版本回退3.金丝雀发布/灰度发布四、Horizontal Pod Autoscaler(HPA)五、DaemonSet(DS)六、Job七、CronJob(CJ)结尾一、Pod控制器介绍 Pod是kubernetes的最小管理单元&#xff0c;在…

ArcGIS矢量化并进行拓扑检查

土地利用数据每年都在发生变化&#xff0c;故每年都要根据去年的数据进行修改。请根据以下要求&#xff0c;修改A区域的数据并对B区域已做好的数据进行拓扑检查。 01 数据说明 1. 地类图斑A.shp&#xff1a;A区域需要编辑修改的图斑数据。 2. 影像.tif&#xff1a;编辑A区域…

Docker数据卷自定义Docker镜像

目录 宿主机与容器之间的文件拷贝 引言&#xff1a;利用MySQL镜像安装MySQL服务 从容器中拷贝文件到宿主机 从宿主机拷贝文件到容器 数据卷 数据卷容器 Dockerfile自定义镜像 自定义tomcat8&#xff08;熟悉几乎所有的Dockerfile命令&#xff09; 宿主机与容器之间的文…

集群配置步骤_java培训

配置步骤 复制3个ZooKeeper zookeeper-3.4.9.tar.gz解压后拷贝到/myzookeeper目录下并重新名为zk01&#xff0c;再复制zk01形成zk02、zk03&#xff0c;共计3份 新增目录 进入zk01/02/03分别新建文件夹&#xff0c;mydata、mylog 新建配置文件 分别进入zk01-zk03各自的conf文件…

perflab 课程设计

初始状态 rotate 版本I 因为本题步步都在寻址而寻址的目标每一步又不同&#xff0c;并且在一个地址的值一次就赋值完毕&#xff0c;不会对同一个地址进行二次寻址&#xff0c;所以我首先想的改进方向就是使得寻址更加快速&#xff0c;于是我使得寻址的地址更加连续。 int i,…

【面试题】 javaScript 进阶之路 --- 《加深理解回调函数》

给大家推荐一个实用面试题库 1、前端面试题库 &#xff08;面试必备&#xff09; 推荐&#xff1a;★★★★★ 地址&#xff1a;前端面试题库 一. 函数 回调函数的基本概念我之前的文章虽然有些过&#xff0c;但是为了引入下文&#xff0c;在这里还是简单再提一嘴…

苹果电脑ntfs如何打开硬盘?mac能读取的硬盘格式

如今&#xff0c;使用Mac电脑的用户越来越多&#xff0c;但是不少用户却面临着一个问题&#xff0c;那就是Mac无法正常读写自己的NTFS格式的外置硬盘&#xff0c;硬盘上面保存着很多重要的文件数据&#xff0c;想要进行复制或者是编辑等&#xff0c;压根没有办法在Mac电脑上进行…

axios和SpringMVC数据交互(一维二维数组,JSON/form形式,@RequestBody/@RequestParam)

目录需求环境准备前端后端成功实现的案例以JSON形式发送double数组以JSON形式发送对象&#xff0c;对象中有数组以JSON形式发送对象&#xff0c;对象中有二维数组以x-www-form-urlencoded形式发送一维数组需求 前端或postman发送数组&#xff0c;后端controller做为入参接收 …