Browse Source

'message'

machinecat520 2 years ago
parent
commit
f510bec82c

+ 1 - 0
第五章、排序与分页.sql

@@ -11,6 +11,7 @@
 #降序:DESC(descend)
 #降序:DESC(descend)
 #如果在 ORDER BY 后没有显式指明排序的方式的话,则默认按照升序排列。
 #如果在 ORDER BY 后没有显式指明排序的方式的话,则默认按照升序排列。
 
 
+#例:按照salary从高到低的顺序显示员工信息
 SELECT employee_id,last_name,salary
 SELECT employee_id,last_name,salary
 FROM employees
 FROM employees
 ORDER BY salary DESC;
 ORDER BY salary DESC;

+ 273 - 0
第十五章、存储过程与函数.sql

@@ -0,0 +1,273 @@
+#第十五章、存储过程与函数
+
+#0、准备工作
+CREATE DATABASE DBTEST15;
+
+USE dbtest15;
+
+#复制员工表
+CREATE TABLE employees
+AS
+SELECT *
+FROM atguigudb.employees;
+
+#复制部门表
+CREATE TABLE departments
+AS
+SELECT *
+FROM atguigudb.departments;
+
+SELECT * FROM employees;
+
+SELECT * FROM departments;
+
+#1、创建存储过程
+
+#类型1:无参数无返回值
+
+#举例1:创建存储过程select_all_data(),查看employees表的所有数据
+
+DELIMITER $
+
+CREATE PROCEDURE select_all_data()
+BEGIN
+	SELECT * FROM employees;
+END $
+
+DELIMITER ;
+
+#2、存储过程的调用
+
+CALL select_all_data();
+
+#例2:创建存储过程avg_employee_salary(),返回所有员工的平均工资
+
+DELIMITER //
+
+CREATE PROCEDURE avg_employee_salary()
+BEGIN
+		SELECT AVG(salary)
+		FROM employees;
+END //
+
+DELIMITER ;
+
+#调用
+CALL avg_employee_salary();
+
+#举例3:创建存储过程show_max_salary(),用来查看“employees”表的最高薪资值。
+
+DELIMITER $
+
+CREATE PROCEDURE show_max_salary()
+BEGIN
+		SELECT MAX(salary)
+		FROM employees;
+END $
+
+DELIMITER ;
+
+#调用
+CALL show_max_salary();
+
+#类型2:带OUT
+#举例4:创建存储过程show_min_salary(),查看“employees”表的最低薪资值。并将最低薪资通过OUT参数“ms”输出
+
+DESC employees;
+
+DELIMITER //
+
+CREATE PROCEDURE show_min_salary(OUT ms DOUBLE)
+BEGIN
+		SELECT MIN(salary) INTO ms
+		FROM employees;
+END //
+
+DELIMITER ;
+
+#调用
+CALL show_min_salary(@ms);
+
+#查看变量值
+SELECT @ms;
+
+#类型3:带IN
+#举例5:创建存储过程show_someone_salary(),查看“employees”表的某个员工的薪资,并用IN参数empname输入员工姓名。
+
+DELIMITER //
+
+CREATE PROCEDURE show_someone_salary(IN empname VARCHAR(25))
+BEGIN
+		SELECT salary FROM employees
+		WHERE last_name = empname;
+END //
+
+DELIMITER ;
+
+#调用方式1
+CALL show_someone_salary('Abel');
+
+#调用方式2
+SET @empname := 'Abel'; #写成SET @empname = 'Abel';也可以
+CALL show_someone_salary(@empname);
+
+#类型4:带IN和OUT
+#举例6:创建存储过程show_someone_salary2(),查看“employees”表的某个员工的薪资,并用IN参数empname输入员工姓名,用OUT参数empsalary输出员工薪资。
+
+DELIMITER //
+
+CREATE PROCEDURE show_someone_salary2(IN empname VARCHAR(25),OUT empsalary DOUBLE)
+BEGIN
+		SELECT salary INTO empsalary
+		FROM employees
+		WHERE last_name = empname;
+END //
+
+DELIMITER ;
+
+#调用
+SET @empname = 'Abel';
+CALL show_someone_salary2(@empname,@empsalary)
+
+SELECT @empsalary;
+
+#类型5:带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;
+
+#2、存储函数
+#举例1:创建存储函数,名称为email_by_name(),参数定义为空,该函数查询Abel的email,并返回,数据类型为字符串型。
+
+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 ;
+
+#调用
+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 ;
+
+#调用
+SET @dept_id := 50;
+SELECT count_by_id(@dept_id);
+
+#3、存储过程、存储函数的查看
+
+#方式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';
+
+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';
+
+#4、存储过程、函数的修改
+ALTER PROCEDURE show_max_salary
+SQL SECURITY INVOKER
+COMMENT '查询最高工资';
+
+#5、存储过程、函数的删除
+
+DROP FUNCTION IF EXISTS count_by_id;
+
+DROP PROCEDURE IF EXISTS show_min_salary;
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+ 297 - 0
第十五章、练习题.sql

@@ -0,0 +1,297 @@
+#第十五章、练习题
+
+#存储过程练习
+#0、准备工作
+CREATE DATABASE test15_pro_func;
+
+USE test15_pro_func;
+
+#1、创建存储过程insert_user(),实现传入用户名和密码,插入到admin表中
+CREATE TABLE admin(
+id INT PRIMARY KEY AUTO_INCREMENT,
+user_name VARCHAR(15) NOT NULL,
+pwd VARCHAR(25) NOT NULL
+);
+
+DESC admin;
+
+#创建存储过程insert_user()
+DELIMITER //
+CREATE PROCEDURE insert_user(IN uname VARCHAR(15),IN passwd VARCHAR(25))
+CONTAINS SQL
+BEGIN
+		INSERT INTO admin(user_name,pwd)
+		VALUES(uname,passwd);
+END //
+
+DELIMITER ;
+
+#调用
+SET @user_name := 'Tom';
+SET @pwd := 'abc123';
+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;
+
+#创建存储过程get_phone():我做的
+DELIMITER //
+CREATE PROCEDURE get_phone(INOUT uid INT)
+BEGIN
+		SELECT name,phone FROM beauty WHERE id = uid;
+END //
+
+DELIMITER ;
+#调用
+SET @uid := 1;
+CALL get_phone(@uid);
+
+#老师讲的
+DELIMITER //
+CREATE PROCEDURE teach_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 teach_get_phone(2,@name,@phone);
+SELECT @name,@phone;
+
+#3、创建存储过程date_diff(),实现传入两个女神生日,返回日期间隔大小
+DELIMITER //
+CREATE PROCEDURE date_diff(IN birth1 DATE,IN birth2 DATE,OUT result INT)
+BEGIN
+		SELECT DATEDIFF(birth1,birth2) INTO result;
+END //
+
+DELIMITER ;
+#调用
+SET @birth1 := '1991-12-11';
+SET @birth2 := '1991-11-12';
+CALL date_diff(@birth1,@birth2,@result);
+SELECT @result;
+
+#4、创建存储过程format_date(),实现传入一个日期,格式化成xx年xx月xx日并返回
+#我做的:不对
+DELIMITER //
+CREATE PROCEDURE format_date(IN udate DATE,OUT result DATE)
+BEGIN
+		SELECT DATE_FORMAT(udate,'%y%m%d') INTO result;
+END //
+
+DELIMITER ;
+#调用
+SET @udate := '1989-05-18';
+CALL format_date(@udate,@result);
+SELECT @result;
+
+#老师讲的
+DELIMITER //
+CREATE PROCEDURE teach_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 teach_format_date(CURDATE(),@str);
+SELECT @str;
+
+#5、创建存储过程beauty_limit(),根据传入的起始索引和条目数,查询女神表的记录
+#我做的:正确
+DELIMITER //
+CREATE PROCEDURE beauty_limit(IN uid INT,IN num INT)
+BEGIN
+		SELECT * FROM beauty LIMIT uid,num; 
+END //
+
+DELIMITER ;
+#调用
+SET @uid := 1;
+SET @num := 3;
+CALL beauty_limit(@uid,@num);
+
+#老师讲的
+DELIMITER //
+CREATE PROCEDURE teach_beauty_limit(IN start_index INT,IN size INT)
+BEGIN
+		SELECT * FROM beauty LIMIT start_index,size; 
+END //
+
+DELIMITER ;
+#调用
+CALL teach_beauty_limit(1,3);
+
+#创建带inout模式参数的存储过程
+
+#6、传入a和b两个值,最终a和b都翻倍并返回
+#我做的
+DELIMITER //
+CREATE PROCEDURE multi(INOUT a INT,INOUT b INT)
+BEGIN
+		SELECT a*2,b*2 FROM DUAL;
+END //
+
+DELIMITER ;
+#调用
+SET @a := 2;
+SET @b := 5;
+CALL multi(@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 PROCEDURE STATUS LIKE 'multi';
+#老师讲的add_double
+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();
+
+DESC employees;
+
+#有参有返回
+#2、创建函数ename_salary(),根据员工姓名,返回它的工资
+DELIMITER //
+CREATE FUNCTION ename_salary(lname VARCHAR(25))
+RETURNS DOUBLE
+BEGIN
+		RETURN (SELECT salary FROM employees WHERE last_name = lname);
+END //
+
+DELIMITER ;
+#调用
+SET @lname = 'Abel';
+SELECT ename_salary(@lname);
+
+#3、创建函数dept_sal(),根据部门名,返回该部门的平均工资
+DESC departments;
+SELECT * FROM departments;
+
+#我做的
+DELIMITER //
+CREATE FUNCTION dept_sal(dept_name VARCHAR(30))
+RETURNS DOUBLE
+BEGIN
+		RETURN (SELECT AVG(salary)
+						FROM employees
+						WHERE department_id = (SELECT department_id
+																	FROM departments
+																	WHERE department_name = dept_name)
+						GROUP BY department_id);
+END //
+
+DELIMITER ;
+#调用
+SET @dept_name = 'IT';
+SELECT dept_sal(@dept_name);
+
+#老师讲的
+DELIMITER //
+CREATE FUNCTION teach_dept_sal(dept_name VARCHAR(30))
+RETURNS DOUBLE
+BEGIN
+		RETURN (SELECT AVG(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 ;
+
+#调用
+SET @v1 = 12.2;
+SET @v2 = 2.3;
+SELECT add_float(@v1,@v2);
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+ 1101 - 0
第十六章、变量、流程控制与游标.sql

@@ -0,0 +1,1101 @@
+#第十六章、变量、流程控制与游标
+
+#1、变量
+#1.1、变量:系统变量(全局系统变量、会话系统变量) vs 用户自定义变量
+
+#1.2、查看系统变量
+#查询全局系统变量
+SHOW GLOBAL VARIABLES;	#617
+
+#查询会话系统变量
+SHOW SESSION VARIABLES; #640
+
+SHOW VARIABLES;
+
+#查看部分系统变量
+
+SHOW GLOBAL VARIABLES LIKE 'admin_%';
+
+SHOW VARIABLES LIKE 'character_%';
+
+#1.3、查看指定的系统变量
+
+SELECT @@global.max_connections;
+SELECT @@global.character_set_client;
+
+#错误
+SELECT @@global.pseudo_thread_id;
+
+#错误
+SELECT @@session.max_connections;
+
+SELECT @@session.character_set_client;
+
+SELECT @@session.pseudo_thread_id;
+
+SELECT @@character_set_client; #先查询会话系统变量,再查询全局系统变量
+
+#1.4、修改系统变量的值
+#全局系统变量
+#方式1
+SET @@global.max_connections = 161;
+#方式2
+SET GLOBAL max_connections = 171;
+
+#针对于当前的数据库实例是有效的,一旦重启mysql服务,就失效了
+
+#会话系统变量
+#方式1
+SET @@session.character_set_client = 'gbk';
+
+#方式2
+SET SESSION character_set_client = 'utf8mb4';
+
+#针对于当前会话是有效的,一旦结束会话,重新建立起新的会话,就失效了。
+
+#1.5、用户变量
+/*
+	(1)、用户变量:会话用户变量 vs 局部变量
+	
+	(2)、会话用户变量:使用“@”开头,作用域为当前会话。
+	
+	(3)、局部变量:只能使用在存储过程和存储函数中的。	
+*/
+
+#1.6、会话用户变量
+/*
+	(1)、变量的声明和赋值:
+		#方式1:“=”或“:=”
+		SET @用户变量 = 值;
+		SET @用户变量 := 值;
+		
+		#方式2:“:=” 或 INTO关键字
+		SELECT @用户变量 := 表达式 [FROM 等子句];
+		SELECT 表达式 INTO @用户变量  [FROM 等子句];
+		
+		(2)、使用
+		SELECT @变量名
+	
+*/
+#准备工作
+CREATE DATABASE dbtest16;
+
+USE dbtest16;
+
+CREATE TABLE employees
+AS
+SELECT * FROM atguigudb.employees;
+
+CREATE TABLE departments
+AS
+SELECT * FROM atguigudb.departments;
+
+SELECT * FROM employees;
+SELECT * FROM departments;
+
+#测试:
+#方式一:
+SET @m1 = 1;
+SET @m2 = 2;
+SET @sum = @m1 + @m2;
+
+SELECT @sum;
+
+#方式2:
+SELECT @count := COUNT(*) FROM employees;
+
+SELECT @count;
+
+SELECT AVG(salary) INTO @avg_sal FROM employees;
+
+SELECT @avg_sal;
+
+#1.7、局部变量
+/*
+	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
+			#1、声明局部变量
+			DECLARE a INT DEFAULT 0;
+			DECLARE b INT;
+			#如果两个变量类型相同,默认值也相同,可以合并成下面的写法
+			#DECLARE a,b INT DEFAULT 0;
+			DECLARE emp_name VARCHAR(25);
+			
+			#2、赋值
+			SET a = 1;
+			SET b := 2;
+			
+			SELECT last_name INTO emp_name FROM employees WHERE employee_id = 101;
+			
+			#3、使用
+			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:声明两个变量,求和并打印 (分别使用会话用户变量、局部变量的方式实现)
+
+#方式一:使用会话用户变量
+SET @v1 = 10;
+SET @v2 := 20;
+SET @result := @v1 + @v2;
+
+#查看
+SELECT @result;
+
+#方式二:使用局部变量
+DELIMITER //
+CREATE PROCEDURE add_value()
+BEGIN
+		#声明
+		DECLARE value1,value2,sum_val INT DEFAULT 0;
+		
+		#赋值
+		SET value1 = 10;
+		SET value2 = 20;
+		SET sum_val = value1 + value2;
+		#使用
+		SELECT sum_val;
+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;
+
+#2、定义条件和处理程序
+#2.1、错误演示
+
+#错误代码:1364
+#> 1364 - Field 'email' doesn't have a default value
+INSERT INTO employees(last_name)
+VALUES('Tom');
+
+DESC employees;
+
+#错误演示:
+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 ;
+
+#调用存储过程
+#错误代码:1048
+#> 1048 - Column 'email' cannot be null
+CALL UpdateDataNoCondition();
+
+SELECT @x;
+
+#2.2、定义条件
+#格式:DECLARE 错误名称 CONDITION FOR 错误码(或错误条件)
+#举例1:定义“Field_Not_Be_NULL”错误名与MySQL中违反非空约束的错误类型是“ERROR 1048 (23000)”对应。
+#方式一:使用MySQL_error_code
+DECLARE Field_Not_Be_NULL CONDITION FOR 1048;
+
+#方式二:使用sqlstate_value
+DECLARE Field_Not_Be_NULL CONDITION FOR SQLSTATE '23000';
+#使用sqlstate_value时,需要在字符串前面添加SQLSTATE,避免隐式将字符串(尤其是纯数字的字符串)转换成数值类型。
+
+#举例2:定义"ERROR 1148(42000)"错误,名称为command_not_allowed。
+#方式一:使用MySQL_error_code
+DECLARE command_not_allowed CONDITION FOR 1148;
+
+#方式二:使用sqlstate_value
+DECLARE command_not_allowed CONDITION FOR SQLSTATE '42000';
+
+#2.3、定义处理程序
+#格式:DECLARE 处理方式 HANDLER FOR 错误类型 处理语句
+
+#举例
+#方法1:捕获sqlstate_value
+DECLARE CONTINUE HANDLER FOR SQLSTATE '42S02' 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';
+
+#2.4、案例的处理
+#先删除原来创建的存储过程
+DROP PROCEDURE UpdateDataNoCondition;
+
+#重新定义存储过程,体现错误的处理程序
+DELIMITER //
+
+CREATE PROCEDURE UpdateDataNoCondition()
+	BEGIN
+		#声明处理程序
+		#处理方式一:
+		DECLARE CONTINUE HANDLER FOR 1048 SET @prc_value = -1;
+		#处理方式二:
+		#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;
+
+#2.5、再举一个例子
+#创建一个名称为“InsertDataWithCondition”的存储过程。
+#1、准备工作
+CREATE TABLE departments
+AS
+SELECT * FROM atguigudb.departments;
+
+DESC departments;
+
+ALTER TABLE departments
+ADD CONSTRAINT uk_dept_name UNIQUE(department_id);
+
+#2、定义存储过程:
+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 ;
+
+#3、调用
+CALL InsertDataWithCondition();
+#查看变量的值
+SELECT @x; #2
+
+#4、删除此存储过程
+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;
+
+#3、流程控制
+#3.1、分支结构之IF
+
+#举例
+#情况一
+
+DELIMITER //
+
+CREATE PROCEDURE test_if()
+BEGIN
+		#声明局部变量
+		DECLARE stu_name VARCHAR(15);
+		
+		IF stu_name IS NULL
+				THEN SELECT 'stu_name is null';
+		END IF;
+
+END //
+
+DELIMITER ;
+
+#调用
+CALL test_if();
+
+#情况二:二选一
+DELIMITER //
+
+CREATE PROCEDURE test_if2()
+BEGIN
+		#声明局部变量
+		DECLARE email VARCHAR(25) DEFAULT 'aaa';
+		
+		IF email IS NULL
+				THEN SELECT 'email is null';
+		ELSE
+				SELECT 'emial is not null';
+		END IF;
+
+END //
+
+DELIMITER ;
+
+#调用
+CALL test_if2();
+
+#情况三:多选一
+DELIMITER //
+
+CREATE PROCEDURE test_if3()
+BEGIN
+		#声明局部变量
+		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_if3();
+
+#例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(10,2);	#记录员工的工资
+		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 DATEDIFF(CURDATE(),hire_date)/365,employee_id,salary
+FROM employees
+WHERE DATEDIFF(CURDATE(),hire_date)/365 >=5 AND salary <= 8000;
+
+DROP PROCEDURE update_salary_by_eid1;
+
+#例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) 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 employee_id,last_name,hire_date,salary
+FROM employees
+WHERE employee_id IN (103,104);
+
+DROP PROCEDURE update_salary_by_eid2;
+
+#例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);
+CALL update_salary_by_eid3(103);
+CALL update_salary_by_eid3(104);
+
+SELECT employee_id,last_name,salary,commission_pct
+FROM employees
+WHERE employee_id IN (102,103,104);
+
+#3.2、分支结构之 CASE
+
+#举例1:基本使用
+#演示:case …… when …… then ……
+DELIMITER //
+
+CREATE PROCEDURE test_case1()
+BEGIN
+			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;		
+END //
+
+DELIMITER ;
+
+#调用
+CALL test_case1();
+
+#演示:case when …… then ……
+DELIMITER //
+
+CREATE PROCEDURE test_case2()
+BEGIN
+			DECLARE var1 INT DEFAULT 10;
+			
+			CASE
+			WHEN var1 >= 100 THEN SELECT '三位数';
+			WHEN var1 >= 10 THEN SELECT '两位数';
+			ELSE SELECT '个位数';
+			END CASE;		
+END //
+
+DELIMITER ;
+
+#调用
+CALL test_case2();
+
+#举例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);
+CALL update_salary_by_eid4(104);
+CALL update_salary_by_eid4(105);
+
+SELECT employee_id,last_name,salary,commission_pct
+FROM employees
+WHERE employee_id IN (103,104,105);
+
+#举例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) 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 employee_id,salary
+FROM employees
+WHERE employee_id = 101;
+
+#4.1、循环结构之LOOP
+/*
+	[loop_label:] LOOP
+	循环执行的语句
+	END LOOP [loop_label]
+*/
+
+#举例1:定义一个变量,使用LOOP语句进行循环操作(变量值+1),当变量值大于等于10的时候退出循环。
+
+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;
+		
+		#查看
+		SELECT num;
+END //
+
+DELIMITER ;
+
+#调用
+CALL test_loop();
+
+#举例2:当市场环境变好时,公司为了奖励大家,决定给大家涨工资。声明存储过程“update_salary_loop()”,声明OUT参数num,输出循环次数。存储过程中实现循环给大家涨薪,薪资涨为原来的1.1倍。直到全公司的平均薪资达到12000结束。并统计循环次数。
+
+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_lab:LOOP
+						#② 循环条件
+						#结束循环的条件
+						IF avg_sal >= 12000
+								THEN LEAVE loop_lab;
+						END IF;
+						
+						#③ 循环体
+						#如果低于12000,更新员工的工资
+						UPDATE employees SET salary = salary * 1.1;
+						
+						#④ 迭代条件
+						#更新avg_sal变量的值
+						SELECT AVG(salary) INTO avg_sal FROM employees;
+						
+						#记录循环次数
+						SET loop_count = loop_count + 1;
+		END LOOP loop_lab;
+		
+		#给num赋值
+		SET num = loop_count;
+
+END //
+
+DELIMITER ;
+
+#调用
+CALL update_salary_loop(@num);
+SELECT @num;
+#查询平均工资
+SELECT AVG(salary) FROM employees;
+
+#4.2、循环结构之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;
+			
+			SELECT AVG(salary) INTO avg_sal FROM employees;
+			SET while_count = while_count + 1;
+		END WHILE;
+		
+		#给num赋值
+		SET num = while_count;
+END //
+
+DELIMITER ;
+
+#调用
+CALL update_salary_while(@num)
+SELECT @num;
+#查询平均工资
+SELECT AVG(salary) FROM employees;
+
+#4.3、循环结构之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);
+SELECT @num;
+#查询平均工资
+SELECT AVG(salary) FROM employees;
+
+/*
+	凡是循环结构,一定具备四个要素:(其它语言也是一样)
+	1、初始化条件
+	2、循环条件:通常都是针对初始化条件去做判断的
+	3、循环体
+	4、迭代条件:针对初始化条件进行更改的
+*/
+
+#5.1、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 ;
+
+#调用
+CALL leave_begin(1);
+
+#举例2:当市场环境不好时,公司为了渡过难关,决定暂时降低大家的薪资。声明存储过程“leave_while()”,声明OUT参数num,输出循环次数,存储过程中使用WHILE循环给大家降低薪资为原来薪资的90%,直到全公司的平均薪资小于等于10000,并统计循环次数。
+
+DELIMITER //
+
+CREATE PROCEDURE leave_while(OUT num INT)
+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;
+				SET while_count = while_count + 1;
+				#④迭代条件
+				SELECT AVG(salary) INTO avg_sal FROM employees;				
+		END while;
+		
+		#赋值
+		SET num = while_count;
+
+END //
+
+DELIMITER ;
+
+#调用
+CALL leave_while(@num);
+SELECT @num;
+
+SELECT AVG(salary) FROM employees;
+
+#5.2、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 num;
+		
+		END LOOP loop_label;
+
+END //
+
+DELIMITER ;
+
+#调用
+CALL test_iterate();
+
+#6、游标的使用
+/*
+	游标使用的步骤:
+	①、声明游标
+	②、打开游标
+	③、使用游标(从游标中获取数据)
+	④、关闭游标
+*/
+
+#举例:创建存储过程“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;
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+ 305 - 0
第十六章、练习题.sql

@@ -0,0 +1,305 @@
+#第十六章、练习题
+
+#一、变量
+#0、准备工作
+CREATE DATABASE test16_var_cur;
+use test16_var_cur;
+
+CREATE TABLE employees
+AS
+SELECT * FROM atguigudb.`employees`;
+
+CREATE TABLE departments
+AS
+SELECT * FROM atguigudb.`departments`;
+
+############################################################################
+
+#无参有返回
+#1. 创建函数get_count(),返回公司的员工个数
+
+SET GLOBAL log_bin_trust_function_creators = 1;
+
+DELIMITER //
+
+CREATE FUNCTION get_count()
+RETURNS INT
+BEGIN
+		#声明变量
+		DECLARE emp_count INT;
+		#赋值
+		SELECT COUNT(*) INTO emp_count FROM employees;
+		RETURN emp_count;
+END //
+
+DELIMITER ;
+
+#调用
+SELECT get_count();
+
+############################################################################
+
+#有参有返回
+#2. 创建函数ename_salary(),根据员工姓名,返回它的工资
+DELIMITER //
+
+CREATE FUNCTION ename_salary(emp_name VARCHAR(15))
+RETURNS DOUBLE
+BEGIN
+		#声明变量
+		SET @sal = 0;	#相当于定义了会话用户变量
+		#赋值
+		SELECT salary INTO @sal FROM employees WHERE last_name = emp_name;
+		RETURN @sal;	
+END //
+
+DELIMITER ;
+
+#调用
+SET @emp_name := 'Abel';
+SELECT ename_salary(@emp_name);
+#或
+SELECT ename_salary('Abel');
+
+SELECT @sal;	#由于定义的是会话用户变量,所以这条语句也可以执行
+
+############################################################################
+
+#3. 创建函数dept_sal() ,根据部门名,返回该部门的平均工资
+DELIMITER //
+
+CREATE FUNCTION dept_sal(dept_name VARCHAR(25))
+RETURNS DOUBLE
+BEGIN
+		#声明变量
+		DECLARE avg_sal DOUBLE;
+		
+		SELECT AVG(salary) INTO avg_sal
+		FROM employees e JOIN departments d
+		ON e.department_id = d.department_id
+		WHERE d.department_name = dept_name;
+		
+		RETURN avg_sal;
+END //
+
+DELIMITER ;
+
+#调用
+SELECT dept_sal('Marketing');
+
+############################################################################
+
+#4. 创建函数add_float(),实现传入两个float,返回二者之和
+DELIMITER //
+
+CREATE FUNCTION add_float(value1 FLOAT,value2 FLOAT)
+RETURNS FLOAT
+BEGIN
+		#声明变量
+		DECLARE sum_val FLOAT;
+		
+		SET sum_val = value1 + value2;
+		
+		RETURN sum_val;		
+
+END //
+
+DELIMITER ;
+
+#调用
+SELECT add_float(1.2,2.3);
+
+############################################################################
+
+#二. 流程控制
+
+#1. 创建函数test_if_case(),实现传入成绩,如果成绩>90,返回A,如果成绩>80,返回B,如果成绩>60,返回C,否则返回D
+#要求:分别使用if结构和case结构实现
+#方式一:使用IF
+DELIMITER //
+
+CREATE FUNCTION test_if_case1(score DOUBLE)
+RETURNS CHAR
+BEGIN
+		#声明变量
+		DECLARE score_level CHAR;
+		
+		IF score > 90 THEN SET score_level = 'A';
+		ELSEIF score > 80 THEN SET score_level = 'B';
+		ELSEIF score > 60 THEN SET score_level = 'C';
+		ELSE SET score_level = 'D';
+		END IF;
+		
+		RETURN score_level;
+END //
+
+DELIMITER ;
+
+#调用
+SELECT test_if_case1(85);
+
+#方式二:使用CASE
+DELIMITER //
+
+CREATE FUNCTION test_if_case2(score INT)
+RETURNS CHAR
+BEGIN
+		#定义变量
+		DECLARE score_level CHAR;
+		
+		CASE WHEN score > 90 THEN SET score_level = 'A';
+				 WHEN score > 80 THEN SET score_level = 'B';
+				 WHEN score > 60 THEN SET score_level = 'C';
+				 ELSE SET score_level = 'D';
+		 END CASE;
+		
+		RETURN score_level;
+END //
+
+DELIMITER ;
+
+#调用
+SELECT test_if_case2(96);
+
+############################################################################
+
+#2. 创建存储过程test_if_pro(),传入工资值,如果工资值<3000,则删除工资为此值的员工,如果3000 <= 工资值 <= 5000,则修改此工资值的员工薪资涨1000,否则涨工资500
+DELIMITER $
+
+CREATE PROCEDURE test_if_pro(IN emp_sal DOUBLE)
+BEGIN
+		
+		IF emp_sal < 3000 
+				THEN DELETE FROM employees WHERE salary = emp_sal;
+		ELSEIF emp_sal <= 5000 
+				THEN UPDATE employees SET salary = salary + 1000 WHERE salary = emp_sal;
+		ELSE 
+				UPDATE employees SET salary = salary + 500 WHERE salary = emp_sal;
+		END IF;
+
+END $
+
+DELIMITER ;
+
+#调用
+CALL test_if_pro(2900);
+
+SELECT *FROM employees;
+
+############################################################################
+
+#3. 创建存储过程insert_data(),传入参数为 IN 的 INT 类型变量 insert_count,实现向admin表中批量插入insert_count条记录
+
+CREATE TABLE admin(
+id INT PRIMARY KEY AUTO_INCREMENT,
+user_name VARCHAR(25) NOT NULL,
+user_pwd VARCHAR(35) NOT NULL
+);
+SELECT * FROM admin;
+
+DELIMITER //
+
+CREATE PROCEDURE insert_data(IN insert_count INT)
+BEGIN
+		#声明变量
+		DECLARE init_count INT DEFAULT 1;	#①初始化条件
+		
+		WHILE init_count <= insert_count DO	#②循环条件
+				#③循环体
+				INSERT INTO admin(user_name,user_pwd)
+				VALUES(CONCAT('atguigu-',init_count),ROUND(RAND()*1000000));
+				#④迭代条件
+				SET init_count = init_count + 1;
+		END WHILE;
+
+END //
+
+DELIMITER ;
+
+#调用
+CALL insert_data(100);
+
+SELECT * FROM admin;
+
+############################################################################
+
+#三. 游标的使用
+/*创建存储过程update_salary(),参数1为 IN 的INT型变量dept_id,表示部门id;参数2为 IN的INT型变量change_sal_count,表示要调整薪资的员工个数。查询指定id部门的员工信息,按照salary升序排列,根据hire_date的情况,调整前change_sal_count个员工的薪资,详情如下。
+
+		hire_date																	salary
+		hire_date < 1995													salary = salary * 1.2
+		hire_date >=1995 AND hire_date <= 1998		salary = salary * 1.15
+		hire_date >1998 AND hire_date <= 2001			salary = salary * 1.10
+		hire_date > 2001													salary = salary * 1.05
+*/
+
+DELIMITER $
+
+CREATE PROCEDURE update_salary(IN dept_id INT,IN change_sal_count INT)
+BEGIN
+		#声明变量
+		DECLARE emp_id INT;	#记录员工id
+		DECLARE emp_hire_date DATE;	#记录员工入职时间
+		DECLARE init_count INT DEFAULT 1;	#用于表示循环结构的初始化条件
+		DECLARE add_sal_rate DOUBLE;	#记录涨薪的比例
+		
+		#声明游标
+		DECLARE emp_cursor CURSOR FOR SELECT employee_id,hire_date FROM employees
+		WHERE department_id = dept_id ORDER BY salary ASC;
+		
+		#打开游标
+		OPEN emp_cursor;
+		
+		WHILE init_count <= change_sal_count DO
+				#使用游标
+				FETCH emp_cursor INTO emp_id,emp_hire_date;
+				
+				#获取涨薪的比例
+				IF (YEAR(emp_hire_date) < 1995)
+						THEN SET add_sal_rate = 1.2;
+				ELSEIF (YEAR(emp_hire_date) <= 1998)
+						THEN SET add_sal_rate = 1.15;
+				ELSEIF (YEAR(emp_hire_date) <= 2001)
+						THEN SET add_sal_rate = 1.10;
+				ELSE
+						SET add_sal_rate = 1.05;
+				END IF;
+				
+				#涨薪
+				UPDATE employees
+				SET salary = salary * add_sal_rate
+				WHERE employee_id = emp_id;
+				
+				#迭代条件的更新
+				SET init_count = init_count + 1;
+		
+		END WHILE;
+		#关闭游标
+		CLOSE emp_cursor;
+
+END $
+
+DELIMITER ;
+
+#调用
+CALL update_salary(50,3);
+
+SELECT employee_id,hire_date,salary
+FROM employees
+WHERE department_id = 50
+ORDER BY salary ASC;
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+ 106 - 0
第十四章、练习.sql

@@ -0,0 +1,106 @@
+#第十四章、练习
+
+#练习1
+#1、使用数据库dbtest14中的表emps创建视图employee_vu,其中包括姓名(LAST_NAME),员工号(EMPLOYEE_ID),部门号(DEPARTMENT_ID)
+USE dbtest14;
+
+#我做的
+CREATE VIEW employee_vu
+AS
+SELECT LAST_NAME,EMPLOYEE_ID,DEPARTMENT_ID
+FROM emps;
+
+#老师讲的
+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;
+
+#练习2
+#1、创建视图emp_v1,要求查询电话号码以‘011’开头的员工姓名和工资、邮箱
+SELECT * FROM emps;
+
+CREATE OR REPLACE VIEW emp_v1
+AS
+SELECT last_name,salary,email
+FROM emps
+WHERE phone_number LIKE '011%';
+
+#使用正则查询
+SELECT last_name,salary,email
+FROM emps
+WHERE phone_number REGEXP '^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;
+
+#实测:失败了
+INSERT INTO emp_v1
+VALUES('Tom','tom@126.com','01012345');
+
+#4、修改emp_v1中员工的工资,每人涨薪1000
+UPDATE emp_v1
+SET salary = salary+1000
+
+#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中插入一条记录,是否可以?
+#不可以
+#错误:The target table emp_v2 of the INSERT is not insertable-into
+INSERT INTO emp_v2
+VALUES(4000,20000);
+
+#8、删除刚才的emp_v2和emp_v1
+DROP VIEW IF EXISTS emp_v1,emp_v2;
+
+SHOW TABLES;
+
+
+
+
+
+
+
+
+
+
+
+
+

+ 205 - 0
第十四章、视图.sql

@@ -0,0 +1,205 @@
+#第十四章、视图
+
+/*
+1、视图的理解
+(1)、视图,可以看做一个虚拟表,本身是不存储数据的。
+				视图的本质,就可以看做是存储起来的SELECT语句。
+(2)、视图中SELECT语句中涉及到的表,称为基表
+(3)、针对视图做DML操作,会影响到对应的基表中的数据。反之亦然。
+(4)、视图本身的删除,不会导致基表中数据的删除。
+(5)、视图的应用场景:针对于小型项目,不推荐使用视图。
+											 针对于大型项目,可以考虑使用视图。
+(6)、视图的优点:简化查询;控制数据的访问
+
+*/
+
+#2、如何创建视图
+#准备工作
+#创建数据库
+CREATE DATABASE dbtest14;
+
+USE dbtest14;
+
+#准备员工信息表
+CREATE TABLE emps
+AS
+SELECT *
+FROM atguigudb.`employees`;
+
+#准备部门信息表
+CREATE TABLE depts
+AS
+SELECT *
+FROM atguigudb.`departments`;
+
+SELECT * FROM emps;
+
+SELECT * FROM depts;
+
+DESC emps;
+
+DESC atguigudb.employees;
+
+#2.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,lname,monthly_sal)	#小括号内字段个数与SELECT中字段个数相同
+AS
+SELECT employee_id,last_name,salary
+FROM emps
+WHERE salary > 8000;
+
+SELECT * FROM vu_emp3;
+
+#情况二:视图中的字段在基表中可能没有对应的字段
+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;
+
+#2.2、针对于多表
+
+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;
+
+#2.3、基于视图创建视图
+
+CREATE VIEW vu_emp4
+AS
+SELECT employee_id,last_name
+FROM vu_emp1;
+
+SELECT * FROM vu_emp4;
+
+CREATE VIEW vu_emp5
+AS
+SELECT last_name,department_name,salary*12 year_sal
+FROM vu_emp1 e JOIN vu_emp_dept d
+WHERE e.employee_id = d.employee_id
+
+SELECT * FROM vu_emp5;
+
+#3、查看视图
+#语法1:查看数据库的表对象、视图对象
+SHOW TABLES;
+
+#语法2:查看视图的结构
+DESCRIBE vu_emp1;
+
+#语法3:查看视图的属性信息
+SHOW TABLE STATUS LIKE 'vu_emp1';
+
+#语法4:查看视图的详细定义信息
+SHOW CREATE VIEW vu_emp1;
+
+#4、“更新”视图中的数据
+#4.1、一般情况,可以更新视图的数据
+SELECT * FROM vu_emp1;
+
+SELECT employee_id,last_name,salary
+FROM emps;
+
+#更新视图的数据,会导致基表中数据的修改
+UPDATE vu_emp1
+SET salary = 20000
+WHERE employee_id = 101;
+
+#同理,更新表中的数据,也会导致视图中的数据的修改
+UPDATE emps
+SET salary = 10000
+WHERE employee_id = 102;
+
+#删除视图中的数据,也会导致表中的数据的删除
+DELETE FROM vu_emp1
+WHERE employee_id = 103;
+
+SELECT employee_id,last_name,salary
+FROM emps;
+
+#4.2、不能更新视图中的数据
+
+SELECT * FROM vu_emp_sal;
+
+#更新失败
+UPDATE vu_emp_sal
+SET avg_sal = 5000
+WHERE department_id = 30;
+
+#删除失败
+DELETE FROM vu_emp_sal
+WHERE department_id = 30;
+
+#5、修改视图
+
+DESC vu_emp1;
+
+#方式一
+CREATE OR REPLACE VIEW vu_emp1
+AS SELECT employee_id,last_name,salary,email
+FROM emps
+WHERE salary > 7000;
+
+#方式二
+ALTER VIEW vu_emp1
+AS
+SELECT employee_id,last_name,salary,email,hire_date
+FROM emps;
+
+#6、删除视图
+SHOW TABLES;
+
+DROP VIEW vu_emp4;
+
+DROP VIEW IF EXISTS vu_emp2,vu_emp3;
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+ 86 - 1
第四章、运算符.sql

@@ -37,7 +37,7 @@ WHERE employee_id % 2 = 0;
 SELECT 1 = 2,1 != 2,1 = '1',1 = 'a',0 = 'a'	#字符串存在隐式转换。如果转换数值不成功,则看做0
 SELECT 1 = 2,1 != 2,1 = '1',1 = 'a',0 = 'a'	#字符串存在隐式转换。如果转换数值不成功,则看做0
 FROM DUAL;
 FROM DUAL;
 
 
-SELECT 'a' = 'a','ab' = 'ab','a' = 'b'	#两边都是字符串的话,则按照ANSI的比较贵重进行比较
+SELECT 'a' = 'a','ab' = 'ab','a' = 'b'	#两边都是字符串的话,则按照ANSI的比较规则进行比较
 FROM DUAL;
 FROM DUAL;
 
 
 SELECT 1 = NULL,NULL = NULL	#只要有null参与判断,结果就为null
 SELECT 1 = NULL,NULL = NULL	#只要有null参与判断,结果就为null
@@ -204,6 +204,91 @@ SELECT 10 & ~1 FROM DUAL;
 SELECT 4 << 1,8 >>1
 SELECT 4 << 1,8 >>1
 FROM DUAL;
 FROM DUAL;
 
 
+#拓展:使用正则表达式查询
+#1、查询以特定字符或字符串开头的记录
+#字符“^”匹配以特定字符或者字符串开头的文本。要查询的字符或者字符串必须放在“^”的后面。
+#例:查询employees表中last_name字段值以字母“b”开头的所有信息。
+SELECT *
+FROM employees
+WHERE last_name REGEXP '^b';
+
+#2、查询以特定字符或字符串结尾的记录
+#字符“$”匹配以特定字符或者字符串结尾的文本。要查询的字符或者字符串必须放在“$”的前面。
+#例:查询employees表中last_name字段值以字母“y”结尾的所有信息。
+SELECT *
+FROM employees 
+WHERE last_name REGEXP 'y$';
+
+#3、用符号“.”来替代字符串中的任意一个字符
+#例:查询employees表中last_name字段值包含字母“a”与“k”且两个字母之间只有一个字母的记录。
+SELECT *
+FROM employees
+WHERE last_name REGEXP 'a.k';
+
+#4、使用“*”和“+”来匹配多个字符
+#星号“*”匹配前面的字符任意多次,包括0次。加号“+”匹配前面的字符至少一次。
+#例1:查询employees表中last_name字段值以字母“b”开头且“b”后面出现字母“a”的记录。
+SELECT *
+FROM employees
+WHERE last_name REGEXP '^ba*';
+
+#例2:查询employees表中last_name字段值以字母“b”开头且“b”后面出现字母“a”至少一次的记录。
+SELECT *
+FROM employees
+WHERE last_name REGEXP '^ba+';
+
+#5、匹配指定字符串
+#正则表达式可以匹配指定字符串,只要这个字符串在查询文本中即可,如要匹配多个字符串,多个字符串直接使用分隔符“|”隔开。
+#例1:查询employees表中last_name字段值包含字符串“on”的记录。
+SELECT *
+FROM employees
+WHERE last_name REGEXP 'on';
+
+#例2:查询employees表中last_name字段值包含字符串“on”或者“no”的记录。
+SELECT *
+FROM employees
+WHERE last_name REGEXP 'on|no';
+
+SELECT *
+FROM employees
+WHERE last_name LIKE 'on';
+
+#6、匹配指定字符中的任意一个
+#方括号“[]”指定一个字符集合,只匹配其中任何一个字符,即为所查找的文本。
+#例:查询employees表中last_name字段值包含字母“o”或者“t”的记录。
+SELECT *
+FROM employees
+WHERE last_name REGEXP '[ot]';
+
+#7、匹配指定字符以外的字符
+#“[^字符集合]”匹配不在指定集合中的任何字符。
+#例1:查询employees表中last_name字段值包含字母“a~e”以外的记录。
+SELECT *
+FROM employees
+WHERE last_name REGEXP '[^a-e]';
+
+#例2:查询employees表中last_name字段值包含字母“a~z”以外的记录。
+
+SELECT *
+FROM employees
+WHERE last_name REGEXP '[^a-z]';
+
+#8、使用{n,}或者{n,m}来指定字符串连续出现的次数
+/*
+	“字符串{n,}”表示至少匹配n次前面的字符串;“字符串{n,m}”表示匹配前面的字符串不少于n此,不多于m次。例如,“a{2,}”表示字母a练习出现至少2次,也可以大于2次;“a{2,4}”表示字符a连续出现最少2次,最多不能超过4次。
+	
+*/
+#例1:查询employees表中last_name字段值出现字母“a”至少2次的记录。
+SELECT *
+FROM employees
+WHERE last_name REGEXP 'a{2,}';
+
+#例2:查询employees表中last_name字段值出现字母“a”最少1次、最多3次的记录。
+SELECT *
+FROM employees
+WHERE last_name REGEXP 'a{1,3}';
+
+