首页 > 数据库 >mysql -- 存储过程

mysql -- 存储过程

时间:2023-05-01 19:22:06浏览次数:29  
标签:存储 name no -- dept emp mysql date

存储过程

如果需要在MySQL中执行一系列语句,可以将所有语句封装在单个程序中,并在需要的时候调用这个程 序,而不是每次发送所有SQL语句。存储过程处理的是一组SQL语句,且没有返回值。

除了SQL语句,还可以使用变量来存储结果并在存储过程中执行程序化的内容。例如可以使用if,case语句、逻辑操作和while循环。

  • 存储的函数(function)和过程(procedure [prəˈsiːdʒər])都称为存储例程(routine [ruːˈtiːn] )

  • 要创建存储过程,应该具有CREATE ROUTINE [ruːˈtiːn] 权限

  • 存储函数具有返回值

  • 存储过程没有返回值

  • 所有代码写在BEGIN和END会之间

  • 存储函数可以直接在SELECT语句中调用

  • 可以使用CALL语句调用存储过程 由于存储过程中的语句应以分隔符(;)结尾,因此必须要更改MySQL的分隔 符,以便MySQL会用正常语句解释存储例程中的SQL语句。创建过程结束后,可以将分隔符更改回默值。

操作过程

​ 假设想要添加新员工,你需要更新3个表,分别是employees[ɛmˈplɔɪiz]表、salaries [ˈsæləriz] 表和titles[ˈtaɪtlz]表。可以开发一个 存储过程并调用它来创建新的employee,而不是执行三条语句。

# 查询三表结构: hire  [ˈhaɪər] 雇用
> desc employees;
+------------+---------------+------+-----+---------+-------+
| Field      | Type          | Null | Key | Default | Extra |
+------------+---------------+------+-----+---------+-------+
| emp_no     | int(11)       | NO   | PRI | NULL    |       |
| birth_date | date          | NO   |     | NULL    |       |
| first_name | varchar(14)   | NO   |     | NULL    |       |
| last_name  | varchar(16)   | NO   |     | NULL    |       |
| gender     | enum('M','F') | NO   |     | NULL    |       |
| hire_date  | date          | NO   |     | NULL    |       |
+------------+---------------+------+-----+---------+-------+

> desc titles;
+-----------+-------------+------+-----+---------+-------+
| Field     | Type        | Null | Key | Default | Extra |
+-----------+-------------+------+-----+---------+-------+
| emp_no    | int(11)     | NO   | PRI | NULL    |       |
| title     | varchar(50) | NO   | PRI | NULL    |       |
| from_date | date        | NO   | PRI | NULL    |       |
| to_date   | date        | YES  |     | NULL    |       |
+-----------+-------------+------+-----+---------+-------+

> desc salaries;
+-----------+---------+------+-----+---------+-------+
| Field     | Type    | Null | Key | Default | Extra |
+-----------+---------+------+-----+---------+-------+
| emp_no    | int(11) | NO   | PRI | NULL    |       |
| salary    | int(11) | NO   |     | NULL    |       |
| from_date | date    | NO   | PRI | NULL    |       |
| to_date   | date    | NO   |     | NULL    |       |
+-----------+---------+------+-----+---------+-------+

​ 必须传递的信息包括员工的first_name、last_name、gender和birth_date以及员工加入部门 department。可以使用输入变量来传递这些变量,并且应该将员工编号作为输出。存储过程不返回 值,但它可以更新一个变量并使用它。

​ 实例:实现的是创建新的employee并更新salaries表和departments表:

# 在创建之前,如果存在任何相同名字的存储过程,则删除已经存在的存储过程 [prəˈsiːdʒər]
> DROP PROCEDURE IF EXISTS create_employee; 

# 修改分隔符为“$$” delimiter 分隔符
> DELIMITER $$

# "IN" 指定作为参数的变量, "OUT" 指定输出的变量
> CREATE PROCEDURE create_employee (
    -> OUT new_emp_no INT,
    -> IN first_name varchar(20),
    -> IN last_name varchar(20),
    -> IN gender e num('M','F'),
    -> IN birth_date date,
    -> IN emp_dept_name varchar(40),
    -> IN title varchar(50))
    -> begin
    
	# 为emp_dept_no和salary声明变量 declare [dɪˈkler] 声明
    -> DECLARE emp_dept_no char(4);
    -> DECLARE salary int DEFAULT 60000;
    
	# 查询employees表的emp_no的最大值,赋值给变量new_emp_no
    -> SELECT max(emp_no) INTO new_emp_no FROM employees;
    
    # 增加new_emp_no
    -> SET new_emp_no = new_emp_no + 1;
    
    # 插入数据到employees表中,CURDATE()函数给出当前日期
    -> INSERT INTO employees VALUES(
    -> new_emp_no,
    -> birth_date,
    -> first_name,
    -> last_name,
    -> gender,
    -> CURDATE());
    
    # 找到dept_name对应的dept_no
    -> SELECT emp_dept_name;
    -> SELECT dept_no INTO emp_dept_no FROM departments WHERE dept_name=emp_dept_name; 
    -> SELECT emp_dept_no;
    
    # 插入dept_emp
    -> INSERT INTO dept_emp VALUES(new_emp_no,emp_dept_no,CURDATE(),'9999-01-01');
    
    #  插入titles 
    -> INSERT INTO titles VALUES(new_emp_no,title,CURDATE(),'9999-01-01');
    
    # 以title为条件查询的薪水
    -> IF title = 'Staff'
    -> 		THEN SET salary = 100000;
    -> ELSEIF title = 'Senior Staff'
    -> 		THEN SET salary = 120000;
    -> END IF;
    
    # 插入salaries
    -> INSERT INTO salaries VALUES(new_emp_no,salary,CURDATE(),'9999-01-01');
    -> END
    -> $$
    
# 改回分隔符
> DELIMITER ;
    

要创建存储过程,可以有以下方法:

# 1. 将上述代码粘贴到命令行客户端中 
# 2. 将以上代码保存成文件
# 并使用mysql -u {user} -p employees < stored_procedure.sql将其导入到 MySQL中 
$ mysql employees < stored_procedure.sql
# 3. 使用SOURCE从文件加载:source ./stored_procedure.sql:
> source stored_procedure.sql; 
# 查询新建立的存储过程是否存在:
> show procedure status like '%emp%'\G
*************************** 1. row ***************************
                  Db: employees
                Name: create_employee
                Type: PROCEDURE
             Definer: root@localhost
            Modified: 2020-03-02 16:32:16
             Created: 2020-03-02 16:32:16
       Security_type: DEFINER
             Comment:
character_set_client: utf8
collation_connection: utf8_general_ci
  Database Collation: latin1_swedish_ci

# 把你要传递的输出值存储在@new_emp_no中,并传递所需的输入值
> CALL create_employee(@new_emp_no,'John','Smith','M','1984-06-19','Research','Staff'); 
+---------------+ 
| emp_dept_name |
+---------------+ 
| Research      |
+---------------+
1 row in set (0.01 sec)
+-------------+
| emp_dept_no |
+-------------+
| d008        |
+-------------+ 
1 row in set (0.01 sec)

# 查询存储在@new_emp_no变量中的emp_no的值:
> select @new_emp_no;
+-------------+ 
| @new_emp_no | 
+-------------+
|      500000 | 
+-------------+

# 检查是否在employees表、salaries表和titles表中创建了行:
> select * from employees where emp_no=500000;
+--------+------------+------------+-----------+--------+------------+
| emp_no | birth_date | first_name | last_name | gender | hire_date  |
+--------+------------+------------+-----------+--------+------------+
| 500000 | 1984-06-19 | John       | Smith     | M      | 2020-03-04 |
+--------+------------+------------+-----------+--------+------------+

> select * from salaries where emp_no=500000;
+--------+--------+------------+------------+
| emp_no | salary | from_date  | to_date    |
+--------+--------+------------+------------+
| 500000 | 100000 | 2020-03-04 | 9999-01-01 |
+--------+--------+------------+------------+

>  select * from titles where emp_no=500000;
+--------+-------+------------+------------+
| emp_no | title | from_date  | to_date    |
+--------+-------+------------+------------+
| 500000 | Staff | 2020-03-04 | 9999-01-01 |
+--------+-------+------------+------------+

可以通过 show create procedure create_employee \G;命令查看存储过程定义的语句

# 查看存储过程定义的语句
> show create procedure create_employee \G
*************************** 1. row ***************************
           Procedure: create_employee
            sql_mode: ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION
    Create Procedure: CREATE DEFINER=`root`@`localhost` PROCEDURE `create_employee`(
OUT new_emp_no INT,
IN first_name varchar(20),
IN last_name varchar(20),
IN gender enum('M','F'),
IN birth_date date,
IN emp_dept_name varchar(40),
IN title varchar(50))
begin
DECLARE emp_dept_no char(4);
DECLARE salary int DEFAULT 60000;
SELECT max(emp_no) INTO new_emp_no FROM employees;
SET new_emp_no = new_emp_no + 1;
INSERT INTO employees VALUES(
new_emp_no,
birth_date,
first_name,
last_name,
gender,
CURDATE());
SELECT emp_dept_name;
SELECT dept_no INTO emp_dept_no FROM departments WHERE dept_name=emp_dept_name;
SELECT emp_dept_no;

INSERT INTO dept_emp VALUES(new_emp_no,emp_dept_no,CURDATE(),'9999-01-01');

INSERT INTO titles VALUES(new_emp_no,title,CURDATE(),'9999-01-01');
IF title = 'Staff'
THEN SET salary = 100000;
ELSEIF title = 'Senior Staff'
THEN SET salary = 120000;
END IF;
INSERT INTO salaries VALUES(new_emp_no,salary,CURDATE(),'9999-01-01');
END
character_set_client: utf8mb4
collation_connection: utf8mb4_0900_ai_ci
  Database Collation: utf8mb4_0900_ai_ci
1 row in set (0.00 sec)

存储过程安全性

用户需要拥有针对存储过程的EXECUTE权限才能执行它。

> GRANT EXECUTE ON employees.* TO 'emp_read_only'@'%'; 

即使emp_read_only对表没有写访问权限,也可以通过调用存储过程来写入,这就产生了安全问题。

为了避免这种问题,如果存储过程的SQL SECURITY创建为INVOKER,则只读用户不能修改数据。

根据存储例程的定义:

DEFINER ( //dɪˈfaɪnr// 定义者) 子句指定存储例程的创建者。 如果没有指定,则获取当前用户。
SQL SECURITY [sɪˈkjʊrəti] 子句指定存储例程的执行上下文。它可以是DEFINERINVOKER ( //ɪnˈvoʊkr// 调用程序)。
DEFINER:即使只有EXECUTE[ˈeksɪkjuːt] 权限的用户也可以调用并获取存储例程的输出,而不管该用户是否具有对基础表的操作权限。 如果DEFINER具有权限,那就足够了。
INVOKER(推荐):安全上下文被切换到调用存储例程的用户。在这种情况下,调用者应具有读写基础表权限才能执行成功。

CREATE [DEFINER = '{用户名}'@'{主机地址}'] PROCEDURE schema.proc_name()
# 此处指定优先级高
SQL SECURITY [DEFINER | INVOKER]
BEGIN
/* 计算逻辑 */
END

标签:存储,name,no,--,dept,emp,mysql,date
From: https://www.cnblogs.com/paopaoT/p/17366858.html

相关文章

  • 【Nginx】配置俩前端,指定路径的时候报错的原因
    #NGINX配置文件listen80;server_nameXX.XX.XX.XX;#配置前台的前端location/{indexindex.phpindex.htmlindex.htmdefault.phpdefault.htmdefault.html;root/www/wwwroot/uniapp/h5/;#root指令会在根目录查找index......
  • CentOS 7的下载安装配置教程
    CentOS7的下载安装配置教程  CentOS主流版本有7和8,我们这里选择CentOS7进行安装,因为现在8已经不更新了,CentOS8是一个介于实验性版本和稳定商业版本之间的产物,是最后一个版本,一般情况下,我们更追求稳定性,所以我们选择CentOS7。相关下载:  CentOS7镜像:http://mirrors.......
  • 微信公众号网页登录,获取用户信息
    0、参考wechat登录前端代码.zip下载后端java代码下载1、接口信息配置请填写接口配置信息,此信息需要你有自己的服务器资源,填写的URL需要正确响应微信发送的Token验证URL=http://自己的域名/rest/WeChat/verifyToken=123456792、JS接口安全域名域名=自己的域名3、网页......
  • [oeasy]python0145_版本控制_git_备份还原
    git版本控制回忆上次内容上次我们了解了try的完全体try尝试运行 except发现异常时运行的代码块 else没有发现异常时运行的代码块 finally无论是否发现异常最终都要运行的代码块  ​ 添加图......
  • 跟随移动
    distance=Vector2.Distance(transform.position,player.transform.position);Vector2direction=player.transform.position-transform.position;direction.Normalize();floatangle=Mathf.Atan2(direction.y,direction.x)*Mathf.Rad2Deg;transform.position......
  • Why WA?
    Acwing292炮兵阵地状压dp#include<bits/stdc++.h>#defineintlonglong#defineitnintusingnamespacestd;intread(){intx=1,a=0;charch=getchar();while(ch>'9'||ch<'0')x=(ch=='-')?-1:x,ch=getchar......
  • 持续集成-Jenkins
    day19持续集成-jenkinsdocker篇:基础篇|各平台搭建docker环境|快速上手(容器、镜像、网络、数据卷)|docker-compose搭建SeleniumGridhub分布式UI测试框架|docker安装MySQL|docker安装redis|docker配置httpbin项目|docker配置Tomcat并添加Javaweb项目|docke......
  • Typora免费使用方法
    中文网:https://typoraio.cn/官网:https://typora.io/众所周知,typora在更新到新版本之后会变成收费版本,89元三台设备,并且如果是以前的,并且如果是以前的老版本,也会弹出提示让你去升级到最新的版本,关闭提示就会退出软件,是无法正常使用的。安装不废话,直入主题:要正常使用还是得装旧......
  • 使用曼哈顿距离画菱形
    输入样例:5输出样例:*************importjava.util.Scanner;publicclassMain{publicstaticvoidmain(String[]args){Scannersc=newScanner(System.in);intn=sc.nextInt();intcx=n/2,cy=n/2......
  • 当前标识(IIS APPPOOL\XX)没有对“C:\Windows\Microsoft.NET\Framework64\4.0.30
    当前标识(IISAPPPOOL\WMS.APP)没有对“C:\Windows\Microsoft.NET\Framework64\v4.0.30319\TemporaryASP.NETFiles”的写访问权限。解决此问题为在使用Windows的IIS搭建服务器时,遇到的问题。在网上尝试了各种解决方法后,终于找到了一个可以解决问题的方法,以管理员身份运行命令......