关于存储过程我只能说请看下图,这是阿里巴巴发布的《阿里巴巴Java开发手册(终极版)v1.3版本》
在 MySQL 第七条中强制指出禁止使用存储过程
所以对于存储过程不必深究,做到会写能看懂即可
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hUsQx6Tf-1680617792594)(./assets/%E6%90%9C%E7%8B%97%E6%88%AA%E5%9B%BE20230329084802.png)]
Stored Procedure
优点
缺点
SQL 最大的缺点还是 SQL 语言本身的局限性 SQL 本身是一种结构化查询语言,我们不应该用存储过程处理复杂的业务逻辑让 SQL 回归它
结构化查询语言
的功用。复杂的业务逻辑,还是交给代码去处理吧
CREATE
[DEFINER = { user | CURRENT_USER }]
# 定义DEFINER默认为当前用户
PROCEDURE 存储过程名
[SQL SECURITY { DEFINER | INVOKER } | …]
# 指定DEFINER或INVOKER权限
BEGIN
…
END
特性 | 说明 |
---|---|
LANGUAGE SQL | 表示存储过程语言,默认SQL |
{CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA} | 表示存储过程要做的工作类别默认值为CONTAINS SQL |
SQL SECURITY { DEFINER | INVOKER } | 指定存储过程的执行权限默认值是DEFINERDEFINDER:使用创建者的权限INVOKER:用执行者的权限 |
COMMENT ‘string’ | 存储过程的注释信息 |
如果省略 SQL SECURITY 特性,则使用 DEFINER 属性指定调用者,且调用者必须具有 EXECUTE 权限,必须在 mysql.user 表中如果将 SQL SECURITY 特性指定为 INVOKER,则 DEFINER 属性无效
IN:指输入参数
OUT:指输出参数
INOUT:指输入输出参数
如果需要定义多个参数,需要使用
,
进行分隔
CALL 存储过程名([参数1,参数2, …]);
# 根据存储过程的定义包含相应的参数
存储过程调用类似于Java中的方法调用
SHOW PROCEDURE STATUS
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xI3lnwUE-1680617792596)(./assets/image-20230403133422488.png)]
SHOW CREATE PROCEDURE 存储过程名
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bCB1Meir-1680617792597)(./assets/image-20230403133612810.png)]
ALTER PROCEDURE 存储过程名[特性………]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HpV3zvNd-1680617792597)(./assets/image-20230403133809483.png)]
DROP PROCEDURE 存储过程名
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kefN8vXn-1680617792597)(./assets/image-20230403133956314.png)]
与Java语言类似,定义存储过程时可以使用变量
DECLARE 变量名[,变量名...] 数据类型 [DEFAULT 值];
给变量进行赋值
SET 变量名 = 表达式值[,变量名=表达式...] ;
定义存储过程时,所有局部变量的声明一定要放在存储过程体的开始;否则,会提示语法错误
系统变量
@@
开头,形式为@@变量名
用户自定义变量
@
开头,形式为@变量名
演示案例
CREATE DEFINER=`root`@`localhost` PROCEDURE `proc_exam_GetLastExamDateByPatientNameAndDepID`(IN patient_name VARCHAR(50), IN dep_id INT,OUT last_exam_date DATETIME)
BEGIN
#Routine body goes here...
DECLARE patient_id INT; #声明局部变量
SELECT patientID INTO patient_id FROM patient WHERE patientName= patient_name;
SELECT patient_id; #输出病人的ID
SELECT MAX(examDate) INTO last_exam_date FROM prescription WHERE patientID = patient_id AND depID = dep_id;
END
SET @patient_name='夏颖';
SET @dep_id =1;
CALL proc_exam_GetLastExamDateByPatientNameAndDepID(@patient_name, @dep_id, @last);
SELECT @last;
与Java语言的流程控制语句类似,MySQL提供的控制语句
IF 条件 THEN 语句列表
[ELSEIF 条件 THEN 语句列表]
[ELSE 语句列表]
END IF;
根据病人的家庭收入,返还补贴不同比例的医疗费用
- 家庭年收入在5000元以下的返还当年总医疗费用的20%
- 家庭年收入在10000以下的返还当年总医疗费用的15%
- 家庭年收入在30000以下的返还总医疗费用的5%
- 30000元以上或未登记的不享受医疗费用返还
- 输入病人编号和年份,计算该患者当年的应返还的医疗费用
CREATE DEFINER=`root`@`localhost` PROCEDURE `Proc_CH05_4`(IN patient_ID INT ,IN in_year VARCHAR(50),OUT ou_subsidy FLOAT )
BEGIN
DECLARE tital_Cost FLOAT;
DECLARE totial_income FLOAT;
SELECT incomeMoney INTO totial_income FROM income WHERE patientID =patient_ID;
SELECT sum(checkItemCost) INTO tital_Cost FROM prescription
INNER JOIN checkitem ON prescription.checkItemID=checkitem.checkItemID
WHERE patientID=patient_ID AND examDate >= CONCAT(in_year,'-01-01')
AND examDate <= CONCAT(in_year,'-12-31');
IF totial_income>=0 AND totial_income<5000 THEN
SET ou_subsidy =tital_Cost*0.2;
ELSEIF totial_income>=5000 AND totial_income<10000 THEN
SET ou_subsidy =tital_Cost*0.15;
ELSEIF totial_income>=10000 AND totial_income<30000 THEN
SET ou_subsidy =tital_Cost*0.05;
ELSE
SET ou_subsidy =0;
END IF;
END
CASE
WHEN 条件 THEN 语句列表
[WHEN 条件 THEN 语句列表]
[ELSE 语句列表]
END CASE;
CASE 列名
WHEN 条件值 THEN 语句列表
[WHEN 条件值 THEN 语句列表]
[ELSE 语句列表]
END CASE;
使用CASE语句实现返还补贴不同比例的医疗费用
CREATE DEFINER=`root`@`localhost` PROCEDURE `Proc_CH05_5`(IN patient_ID INT ,IN in_year VARCHAR(50),OUT ou_subsidy FLOAT )
BEGIN
DECLARE tital_Cost FLOAT;
DECLARE totial_income FLOAT;
SELECT incomeMoney INTO totial_income FROM income WHERE patientID =patient_ID;
SELECT sum(checkItemCost) INTO tital_Cost FROM prescription
INNER JOIN checkitem ON prescription.checkItemID=checkitem.checkItemID
WHERE patientID=patient_ID AND examDate >= CONCAT(in_year,'-01-01')
AND examDate <= CONCAT(in_year,'-12-31');
CASE
WHEN totial_income>=0 AND totial_income<5000 THEN
SET ou_subsidy =tital_Cost*0.2;
WHEN totial_income>=5000 AND totial_income<10000 THEN
SET ou_subsidy =tital_Cost*0.15;
WHEN totial_income>=10000 AND totial_income<30000 THEN
SET ou_subsidy =tital_Cost*0.05;
WHEN totial_income>=30000 AND totial_income<0 THEN
SET ou_subsidy =0;
END CASE;
END
在某种情况下(例如,做等值判断),使用第二种写法更加简洁但是,因为CASE后面有列名,功能上会有一些限制
[label:] WHILE 条件 DO
语句列表
END WHILE [label]
- 首先判断条件是否成立。如果成立,则执行循环体
- label为标号,用于区分不同的循环,可省略
- 用在begin、repeat、while 或者loop 语句前
假设有测试表test,有Id字段、Val字段
- 根据输入的行数要求,批量插入测试数据
DECLARE rand_val FLOAT;
WHILE rows > 0 DO
SELECT RAND() INTO rand_val;
INSERT INTO test VALUES(NULL, rand_val);
SET rows = rows - 1;
END WHILE;
[label:] LOOP
语句列表
END LOOP [label] ;
不需判断初始条件,直接执行循环体
LEAVE label ;
遇到 LEAVE 语句,退出循环
批量插3个新的检查项目,检查项目名称为胃镜、肠镜和支气管纤维镜,各项检查的价格均为70元
CREATE DEFINER=`root`@`localhost` PROCEDURE `proc_checkitem_insert`( IN checkitems VARCHAR(100))
BEGIN
DECLARE comma_pos INT;
DECLARE current_checkitem VARCHAR(20);
loop_label: LOOP
SET comma_pos = LOCATE(',', checkitems);
SET current_checkitem = SUBSTR(checkitems, 1, comma_pos-1);
IF current_checkitem <> '' THEN
SET checkitems = SUBSTR(checkitems, comma_pos+1);
ELSE
SET current_checkitem = checkitems;
END IF;
INSERT INTO checkitem(checkItemName,checkItemCost) VALUES(current_checkitem,70);
IF comma_pos=0 OR current_checkitem='' THEN
LEAVE loop_label;
# 退出loop_label标识的程序块
END IF;
END LOOP loop_label;
# LOOP循环结束
END
[label:] REPEAT
语句列表
UNTIL 条件
END REPEAT [label]
- 先执行循环操作再判断循环条件
- 与 LOOP 循环语句相比较相同点
- 不需要初始条件直接进入循环体
- 不同点:REPEAT 语句可以设置退出条件
使用REPEAT循环语句编码实现,根据输入的行数要求,向测试表test中批量插入测试数据
CREATE DEFINER=`root`@`localhost` PROCEDURE `Proc_CH05_7`(IN rows INT )
BEGIN
DECLARE rand FLOAT;
REPEAT
SELECT RAND() INTO rand;
INSERT INTO test (val)VALUES(rand);
SET rows = rows -1 ;
UNTIL rows <= 0 END REPEAT;
END
ITERATE label;
- 从当前代码处返回到程序块开始位置,重新执行
- ITERATE关键字可以嵌入到LOOP、WHILE和REPEAT程序块中
输入需增加数据行数,随机产生的测试数据必须大于0.5
CREATE DEFINER=`root`@`localhost` PROCEDURE `Proc_CH05_8`(IN rows INT)
BEGIN
DECLARE rand FLOAT;
random_lbl : REPEAT
SELECT RAND() INTO rand;
IF rand< 0.5 THEN
ITERATE random_lbl;
END IF;
INSERT INTO test (val) VALUES (rand);
SET rows=rows-1;
UNTIL rows<=0 END REPEAT;
END
TRANSACTION
事务必须具备的属性,简称 ACID 属性
ACID | 属性描述 |
---|---|
原子性:Atomicity | 事务是一个完整的操作,事务的各步操作是不可分的(原子的),要么都执行,要么都不执行 |
一致性:Consistency | 当事务完成时,数据必须处于一致状态 |
隔离性:Isolation | 并发事务之间彼此隔离、独立,不应以任何方式依赖于或影响其他事务 |
持久性:Durability | 事务完成后,它对数据库的修改被永久保持 |
MySQL中支持事务的存储引擎
InnoDB支持事务操作
通过 UNDO 日志和 REDO 日志实现对事务的支持
UNDO 日志
REDO 日志
MyISAM不支持事务操作
实现事务的方式
在执行命令SET autocommit=0,禁止当前会话的自动提交后,后面的SQL语句将作为事务中的语句一同提交
开始事务
BEGIN ;
或
START TRANSACTION;
提交事务
COMMIT ;
回滚(撤销)事务
ROLLBACK ;
使用事务实现小王和小张之间的转账操作
USE paycorp;
BEGIN;
UPDATE account SET balance=balance-2000 WHERE accountName='小王';
UPDATE account SET balance=balance+2000 WHERE accountName='小张';
COMMIT;
小王和小张的总账户余额和转账前保持一致,数据库中数据从一个一致性状态更新到另一个一致性状态
默认情况下,每条单独的SQL语句视为一个事务
关闭默认提交状态后,可手动开启、关闭事务
关闭/开启自动提交
SET autocommit = 0|1;
关闭自动提交后,从下一条SQL语句开始将会开启新事务,需使用COMMIT或ROLLBACK语句结束该事务
事务尽可能简短
事务中访问的数据量尽量最少
查询数据时尽量不要使用事务
在事务处理过程中尽量不要出现等待用户输入的操作
不同的人员关注不同的数据
保证信息的安全性
视图是一张虚拟表
视图中不存放数据
一个原始表,根据不同用户的不同需求,可以创建不同的视图
开发人员
最终用户
使用SQL语句创建视图
CREATE VIEW view_name
AS
使用SQL语句删除视图
DROP VIEW [IF EXISTS] view_name;
-- 删除前先判断 视图是否存在
使用SQL语句查看视图
SELECT 字段1, 字段2, …… FROM view_name;
视图中可以使用多个表
一个视图可以嵌套另一个视图,但最好不要超过3层
对视图数据进行添加、更新和删除操作会直接影响所引用表中的数据
当视图数据来自多个表时,不允许添加和删除数据
查看所有视图
USE information_schema;
SELECT * FROM views;
使用视图修改数据会有许多限制,一般在实际开发中视图仅用作查询
CREATE VIEW VIEW_1
AS
SELECT prescription.*,patient.patientName FROM prescription INNER JOIN patient ON prescription.patientID=patient.patientID;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RJVIdn1h-1680617792598)(./assets/image-20230403144209697.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mwSY2G3n-1680617792598)(./assets/image-20230403144259240.png)]
是对数据库表中一列或多列值进行排列的一种结构
作用
大大提高数据库的检索速度
改善数据库性能
MySQL索引按存储类型分类
B-树索引(BTREE)
哈希索引(HASH)
数据存储在数据表中,而索引是创建在数据库表对象上
由表中的一个字段或多个字段生成的键组成
Indexes Use Key Values to Locate Data
(根据索引键查找定位数据行)
普通索引
唯一索引
主键索引
复合索引
全文索引
创建索引
CREATE [UNIQUE|FULLTEXT|SPATIAL] INDEX index_name ON table_name (column_name [length] …);
-- 唯一索引、全文索引或空间索引,可选
如果创建索引是未指定创建索引的类型,则创建的索引为普通索引通过CREATE INDEX语句无法创建主键索引,主键索引的创建语句
删除索引
ALTER TABLE tablename ADD PRIMARY KEY(column)
删除表时,该表的所有索引同时会被删除
按照下列标准选择建立索引的列
不要使用下面的列创建索引
查询时减少使用*
返回表的全部列,不要返回不需要的列
索引应该尽量小,在字节数小的列上建立索引
WHERE 子句中有多个条件表达式时,包含索引列的表达式应置于其他条件表达式之前
避免在 ORDER BY 子句中使用表达式
根据业务数据发生频率,定期重新生成或重新组织索引,进行碎片整理
查看已创建的索引信息
SHOW INDEX FROM table_name;
删除索引
DROP INDEX index_name ON table_name;
删除表时,该表的所有索引将同时被删
除删除表中的列时,如果要删除的列为索引的组成部分,则该列也会从索引中删除
如果组成索引的所有列都被删除,则整个索引将被删除
可能导致数据丢失的意外状况
数据备份
数据恢复
mysqldump 命令——MySQL的客户端常用逻辑备份工具
mysqldump [options] database [table1,[table2]…] > [path]/filename.sql
mysqldump是DOS系统下的命令
在使用时,无须进入mysql命令行;否则,将无法执行
示例
以root账户登录到MySQL服务器,使用mysqldump命令备份hospital数据库,将SQL脚本保存到e:\hospital.sql中
mysqldump -uroot -proot hospital > e:\hospital.sql
导出的SQL脚本中两种注释
--
开头:关于SQL语句的注释信息/*!
开头, */
结尾:是关于MySQL服务器相关的注释为保证账户密码安全,命令中可不写密码
但是,参数
-p
必须有,回车后根据提示写密码
常用参数选项
参数 | 描述 |
---|---|
-add-drop-table | 在每个CREATE TABLE语句前添加DROP TABLE语句,默认是打开的,可以用-skip-add-drop-table取消 |
–add-locks | 该选项会在INSERT 语句中捆绑一个LOCK TABLE 和UNLOCK TABLE 语句 |
好处:防止记录被再次导入时,其他用户对表进行的操作 | |
-t或-no-create-info | 只导出数据,而不添加CREATE TABLE语句 |
-c或–complete-insert | 在每个INSERT语句的列上加上列名,在数据导入另一个数据库时有用 |
-d或–no-data | 不写表的任何行信息,只转储表的结构 |
备份文件包含的主要信息
Navicat也可以用于导出数据库的备份脚本
在需要恢复数据库数据时,对导出的SQL备份脚本执行导入操作
mysql为DOS命令
mysql –u username –p [dbname] < filename.sql
-- –u 用户名
-- –p 数据库名
-- filename.sql 备份文件名
在执行该语句之前,必须在MySQL服务器中创建新数据库
因为导出的备份文件中只包含表的备份,而不包含创建的库的语句
因此执行导入操作时必须指定数据库名,且该数据库必须存在
除了在命令行中导入数据以外,还可以在数据库已连接状态下导入数据
source filename;
登录MySQL服务后使用
执行该命令前,先创建并选择恢复后的目标数据库
CREATE DATABASE hospitalDB; #创建数据库
USE hospitalDB; #选择要导入数据库的数据库
source e:\hospital.sql #导入数据
# SQL脚本文件后面不要加字符 ;
Navicat中导入数据的操作步骤
运行SQL文件…
快捷菜单项开始
按钮开始导入数据