Mysql 存储过程以及在.net中的应用示例

一、存储过程概述

MySQL 5.0 版本开始支持存储过程。存储过程(Stored Procedure)是一种在数据库中存储复杂程序,以便外部程序调用的一种数据库对象。存储过程是为了完成特定功能的SQL语句集,经编译创建并保存在数据库中,用户可通过指定存储过程的名字并给定参数(需要时)来调用执行。存储过程思想上很简单,就是数据库 SQL 语言层面的代码封装与重用。

优点 

  • 存储过程可封装,并隐藏复杂的商业逻辑。 
  • 存储过程可以回传值,并可以接受参数。 
  • 运行速度:对于复杂的业务逻辑,使用存储过程比较快,因为在存储过程创建的时候,数据库已经对其进行了编译,已经通过语法检查和性能优化,以后每次执行存储过程都不需要再重新编译。 对于很简单的sql,存储过程没有什么优势。 
  •  可降低网络的通信量:不需要通过网络来传送很多的sql语句到数据库服务器。 

缺点

  • 移植性差:存储过程往往定制化于特定的数据库上,因为支持的编程语言不同,当切换到其他厂商的数据库系统时,需要重写原有的存储过程。

  • SQL是一种结构化查询语言,本质上是过程化的语言。面对复杂的业务逻辑,过程化的处理会很吃力。同时SQL擅长的是数据查询而非业务逻辑的处理。

  •  维护复杂:如果需要对输入存储过程的参数进行更改,或者要更改由其返回的数据,则您仍需要更新程序集中的代码以添加参数、更新调用等等,这时候估计会比较繁琐了。

  •  开发调试复杂:由于IDE的问题,存储过程的开发调试要比一般程序困难。   

总结

  • 适当的使用存储过程,能够提高我们SQL查询的性能,但不应该大规模使用、滥用。

  • 随着众多ORM 的出现,存储过程很多优势已经不明显。

二、存储过程的创建

  • 存储过程就是具有名字的一段代码,用来完成一个特定的功能。

  • 创建的存储过程保存在数据库的数据字典中。

存储过程的创建语法

CREATE
    [DEFINER = { user | CURRENT_USER }]
 PROCEDURE sp_name ([proc_parameter[,...]])
    [characteristic ...] routine_body
 
proc_parameter:
    [ IN | OUT | INOUT ] param_name type
 
characteristic:
    COMMENT 'string'
  | LANGUAGE SQL
  | [NOT] DETERMINISTIC
  | { CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA }
  | SQL SECURITY { DEFINER | INVOKER }
 
routine_body:
  Valid SQL routine statement
 
[begin_label:] BEGIN
  [statement_list]
    ……
END [end_label]

先来创建一个数据库School和数据表user

mysql> create database school;
Query OK, 1 row affected

mysql> use school;
Database changed
mysql> create table user(
    -> Id int not null auto_increment,
    -> Name varchar(100) not null,
    -> Email varchar(100) not null,
    -> primary key (Id)
    -> )engine=innodb default charset=utf8;
Query OK, 0 rows affected
mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| school             |
| sys                |
+--------------------+
5 rows in set

mysql> show tables;
+------------------+
| Tables_in_school |
+------------------+
| user             |
+------------------+
1 row in set

创建一个存储过程,往user表中插入数据

mysql> use school;
Database changed
mysql> delimiter $$ #将语句的结束符号从分号;临时改为$$(可自定义)
mysql> create procedure insert_user(in name varchar(100),in email varchar(100))
    -> begin
    -> insert into user(Name,Email) values(name,email);
    -> end$$
Query OK, 0 rows affected
mysql> delimiter ; #将语句的结束符号恢复为分号

调用存储过程

mysql> call insert_user('qxh','qxh@qq.com')
    -> ;
Query OK, 1 row affected
mysql> 

查看效果:

存储过程概念解疑

  • DELIMITER的作用:在mysql执行过程中,遇到分号就执行了。存储过程是一个代码段,里面可能会加入分号,但是该分号不是结束符号。使用DELIMITER $$ 命令将语句的结束符号从分号 ; 临时改为两个$$,使得过程体中使用的分号被直接传递到服务器,而不会被客户端(如mysql)解释,即不会被执行。当我们创建完存储过程后,在通过命令 “delimiter ;”将语句的结束符号恢复为分号。
  • BEGIN .... END:是存储过程开始和结束符号,可嵌套使用,如下:
BEGIN
  BEGIN
    BEGIN
      statements; 
    END
  END
END
  • 为语句块贴标签:增强代码的可读性
label1: BEGIN
  label2: BEGIN
    label3: BEGIN
      statements; 
    END label3 ;
  END label2;
END label1
  • 使用call sp_name(参数)调用存储过程
mysql> call insert_user('qxh','qxh@qq.com') ;
Query OK, 1 row affected
mysql> 

三、存储过程的参数

 MySQL存储过程的参数用在存储过程的定义,共有三种参数类型,IN,OUT,INOUT,形式如:

CREATEPROCEDURE 存储过程名([[IN |OUT |INOUT ] 参数名 数据类形...])
  • IN 输入参数:表示调用者向过程传入值(传入值可以是字面量或变量)
  • OUT 输出参数:表示过程向调用者传出值(可以返回多个值)(传出值只能是变量)
  • INOUT 输入输出参数:既表示调用者向过程传入值,又表示过程向调用者传出值(值只能是变量),尽量少用。

1、in 输入参数

mysql> use school;#指定数据库
Database changed
mysql> delimiter $$ #将语句的结束符号从分号;临时改为$$(可自定义)
mysql> create procedure in_param(in p_in int) #创建存储过程,可输入参数
    -> begin #存储过程开始
    ->     select p_in;
    ->     set p_in=2; #设置局部参数
    ->     select P_in;
    -> end$$ #存储过程结束
Query OK, 0 rows affected

mysql> delimiter ; #将语句的结束符号恢复为分号
mysql> set @p_in=1; #设置全局变量
Query OK, 0 rows affected

mysql> call in_param(@p_in);#调用存储过程
+------+
| p_in |
+------+
|    1 |
+------+
1 row in set
#接收传入的参数,所以输出1
+------+
| P_in |
+------+
|    2 |
+------+
1 row in set
#设置局部变量为2,所以输出2
Query OK, 0 rows affected

mysql> select @p_in;
+-------+
| @p_in |
+-------+
| 1 |
+-------+
#p_in 在存储过程中被修改,但并不影响 @p_in 的值,因为前者为局部变量、后者为全局变量。

2、out输出参数

mysql> use school;#指定数据库
Database changed
mysql> delimiter //   #将语句的结束符号从分号;临时改为//(可自定义)
mysql> create procedure out_param(out p_out int) #创建存储过程,可输出参数
    -> begin #存储过程开始
    ->     select p_out;
    ->     set p_out=2; #局部变量
    ->     select p_out;
    -> end// #存储过程结束
Query OK, 0 rows affected

mysql> delimiter ; #将语句的结束符号恢复为分号
mysql> set @p_out=1; #设置全局变量
Query OK, 0 rows affected

mysql> call out_param(@p_out); #调用存储过程
+-------+
| p_out |
+-------+
| NULL  |
+-------+
1 row in set
#因为out是向调用者输出参数,不接收输入的参数,所以存储过程里的p_out为null
+-------+
| p_out |
+-------+
|     2 |
+-------+
1 row in set
#调用了out_param存储过程,输出参数,改变了p_out变量的值
Query OK, 0 rows affected

mysql> 

3、inout输入参数

mysql> use school;#指定数据库
Database changed

mysql> delimiter $$
mysql> create procedure inout_param(inout p_inout int)
    ->   begin
    ->     select p_inout;
    ->     set p_inout=2;
    ->     select p_inout;
    ->   end
    -> $$
mysql> delimiter ;
 
mysql> set @p_inout=1;
 
mysql> call inout_param(@p_inout);
+---------+
| p_inout |
+---------+
|       1 |
+---------+
 
+---------+
| p_inout |
+---------+
|       2 |
+---------+
 
mysql> select @p_inout;
+----------+
| @p_inout |
+----------+
|        2 |
+----------+
#调用了inout_param存储过程,接受了输入的参数,也输出参数,改变了变量

4、注意:

  • 如果过程没有参数,也必须在过程名后面写上小括号

  • 确保参数的名字不等于列的名字,否则在过程体中,参数名被当做列名来处理

四、变量

变量定义

局部变量声明一定要放在存储过程体的开始:

DECLAREvariable_name [,variable_name...] datatype [DEFAULT value];

其中,datatype 为 MySQL 的数据类型,如: int, float, date,varchar(length),例如:

DECLARE l_int int unsigned default 4000000;  
DECLARE l_numeric number(8,2) DEFAULT 9.95;  
DECLARE l_date date DEFAULT '1999-12-31';  
DECLARE l_datetime datetime DEFAULT '1999-12-31 23:59:59';  
DECLARE l_varchar varchar(255) DEFAULT 'This will not be padded';

变量赋值

SET 变量名 = 表达式值 [,variable_name = expression ...]

用户变量

在MySQL客户端使用用户变量:

mysql > SELECT 'Hello World' into @x;  
mysql > SELECT @x;  
+-------------+  
|   @x        |  
+-------------+  
| Hello World |  
+-------------+  
mysql > SET @y='Goodbye Cruel World';  
mysql > SELECT @y;  
+---------------------+  
|     @y              |  
+---------------------+  
| Goodbye Cruel World |  
+---------------------+  
 
mysql > SET @z=1+2+3;  
mysql > SELECT @z;  
+------+  
| @z   |  
+------+  
|  6   |  
+------+
  • 用户变量名一般以@开头
  • 滥用用户变量会导致程序难以理解及管理

五、注释

MySQL 存储过程可使用两种风格的注释

  • 两个横杆--:该风格一般用于单行注释。
  • c 风格:/* 注释内容 */  一般用于多行注释。

六、存储过程中常用的命令

MySQL存储过程的调用

用call和你过程名以及一个括号,括号里面根据需要,加入参数,参数包括输入参数、输出参数、输入输出参数。

MySQL存储过程的查询

show databases;--展示所有的数据库
mysql> use school;
Database changed
mysql> show tables;--展示某库中所有数据表
mysql> SHOW PROCEDURE STATUS WHERE db='school';--school数据库中的所有存储过程

MySQL存储过程的修改

ALTER PROCEDURE 存储过程名 [ 特征 ... ]

MySQL存储过程的删除

删除一个存储过程比较简单,和删除表一样:

DROP PROCEDURE  IF EXISTS insert_user;

从 MySQL 的表格中删除一个或多个存储过程。

MySQL存储过程的控制语句

变量作用域

内部的变量在其作用域范围内享有更高的优先权,当执行到 end。变量时,内部变量消失,此时已经在其作用域外,变量不再可见了,应为在存储过程外再也不能找到这个申明的变量,但是你可以通过 out 参数或者将其值指派给会话变量来保存其值。

条件语句

  • if-then-else 语句

  • case语句

循环语句

  • while ···· end while

  • repeat···· end repeat

  • loop ·····endloop

LABLES 标号

标号可以用在 begin repeat while 或者 loop 语句前,语句标号只能在合法的语句前面使用。可以跳出循环,使运行指令达到复合语句的最后一步。

ITERATE迭代

ITERATE 通过引用复合语句的标号,来从新开始复合语句

七、NET中使用存储过程

 先定义一个数据表base_user,如下:

 创建一个存储过程procBaseUserInsert,用于往base_user中插入数据。创建存储过程的SQL如下:

CREATE PROCEDURE procBaseUserInsert(
    _guid varchar(200),
    _userName varchar(200),
    _email varchar(200),
    _password varchar(1020),
    _createTime datetime,
    _createUserName varchar(75),
    _updateTime datetime,
    _updateUserName varchar(75)
)
begin
    INSERT INTO base_user (
            Guid,
            UserName,
            Email,
            Password,
            CreateTime,
            CreateUserName,
            UpdateTime,
            UpdateUserName
    ) VALUES (
        _guid,
        _userName,
        _email,
        _password,
        _createTime,
        _createUserName,
        _updateTime,
        _updateUserName
);
END

接下来,在ADO.NET中使用:

数据表对应的实体如下:

//===================================================================  
// FileName: base_user.cs 
// Author  : qiuxianhu 
// Description: 实体(Entity) 
// CreateTime: 2020/11/18 16:57:36
//===================================================================
using System;
namespace Qxh.DB.MySql.Entity
{
    /// <summary>
    /// 描述:base_user实体信息   
    /// </summary>
    [Serializable] 
    public partial class Base_user
    {    
        #region 常量
        public const string _BASE_USER_="base_user";
        public const string _GUID_="Guid";
        public const string _USERNAME_="UserName";
        public const string _EMAIL_="Email";
        public const string _PASSWORD_="Password";
        public const string _CREATETIME_="CreateTime";
        public const string _CREATEUSERNAME_="CreateUserName";
        public const string _UPDATETIME_="UpdateTime";
        public const string _UPDATEUSERNAME_="UpdateUserName";
        #endregion
        
        #region 构造函数
        /// <summary>
        /// base_user
        /// </summary>
        public Base_user()
        {
        }        
        #endregion
        
        #region 公共属性
        
        ///<summary>
        ///Guid 分布式
        ///</summary>
        public string Guid{get;set;}
        ///<summary>
        ///用户名
        ///</summary>
        public string UserName{get;set;}
        ///<summary>
        ///邮件
        ///</summary>
        public string Email{get;set;}
        ///<summary>
        ///密码
        ///</summary>
        public string Password{get;set;}
        ///<summary>
        ///创建时间
        ///</summary>
        public DateTime? CreateTime{get;set;}
        ///<summary>
        ///创建人
        ///</summary>
        public string CreateUserName{get;set;}
        ///<summary>
        ///更新时间
        ///</summary>
        public DateTime? UpdateTime{get;set;}
        ///<summary>
        ///更新人
        ///</summary>
        public string UpdateUserName{get;set;}
        #endregion
    }
}
View Code

逻辑层代码如下:

var result = Base_userBLL.Insert(new Base_user
{
  Guid = "1232342431234",
  UserName = "qxh",
  Password = "12323",
  Email = "q@q",
  CreateTime = DateTime.Now,
  CreateUserName = "qxh",
  UpdateTime = DateTime.Now,
  UpdateUserName = "qxh"
});

数据层代码如下:

/// <summary>
/// 向数据库中插入一条新记录
/// </summary>
/// <parameterseterseters name="baseUser">Base_user实体对象</parameterseterseters>
/// <returns>-1表示没有执行数据库操作;>0表示执行数据库操作成功;=0表示执行数据库操作失败</returns>
public int Insert(Base_user baseUser)
{
  int errorCode = -1;
  const string SQL_COMMAND = "procBaseUserInsert";
  MySqlParameter[] parameters = {
    new MySqlParameter("_guid", MySqlDbType.VarChar,200),
    new MySqlParameter("_userName", MySqlDbType.VarChar,200),
    new MySqlParameter("_email", MySqlDbType.VarChar,200),
    new MySqlParameter("_password", MySqlDbType.VarChar,1020),
    new MySqlParameter("_createTime", MySqlDbType.DateTime,0),
    new MySqlParameter("_createUserName", MySqlDbType.VarChar,75),
    new MySqlParameter("_updateTime", MySqlDbType.DateTime,0),
    new MySqlParameter("_updateUserName", MySqlDbType.VarChar,75)
  };
  parameters[0].Value = baseUser.Guid;
  parameters[1].Value = baseUser.UserName;
  parameters[2].Value = baseUser.Email;
  parameters[3].Value = baseUser.Password;
  parameters[4].Value = baseUser.CreateTime;
  parameters[5].Value = baseUser.CreateUserName;
  parameters[6].Value = baseUser.UpdateTime;
  parameters[7].Value = baseUser.UpdateUserName;
  errorCode = MySqlHelper.ExecuteNonQuery(Conn.SqlConn, CommandType.StoredProcedure, SQL_COMMAND, parameters);
}


如果你有现成的MySqlHelper文件,就可以不用看下面的代码了。MySqlHelper中ExecuteNonQuery方法体:

public static int ExecuteNonQuery(string connectionString, CommandType commandType, string commandText, params DbParameter[] commandParameters)
{
  //create & open a MySqlConnection, and dispose of it after we are done.
  using (MySqlConnection cn = new MySqlConnection(connectionString))
  {
    cn.Open();       

     MySqlCommand cmd = new MySqlCommand();
     PrepareCommand(cmd, connection, (DbTransaction)null, commandType, commandText, commandParameters);    

     int retval = cmd.ExecuteNonQuery();   

     cmd.Parameters.Clear();
     return retval;

  }
}

然后再看一下PrepareCommand方法体:

private static void PrepareCommand(MySqlCommand command, DbConnection connection, DbTransaction transaction, CommandType commandType, string commandText, DbParameter[] commandParameters)
{
  //if the provided connection is not open, we will open it
   if (connection.State != ConnectionState.Open)
   {
     connection.Open();
   }

   //associate the connection with the command
   command.Connection = connection as MySqlConnection;

   //set the command text (stored procedure name or SQL statement)
   command.CommandText = commandText;

   //command.CommandTimeout = connection.ConnectionTimeout;

   //if we were provided a transaction, assign it.
   if (transaction is MySqlTransaction)
   {
     command.Transaction = transaction as MySqlTransaction;
   }

   //set the command type
   command.CommandType = commandType;

   //attach the command parameters if they are provided
   if (commandParameters != null)
   {
      AttachParameters(command, commandParameters);
   }

      return;
}
原文地址:https://www.cnblogs.com/qtiger/p/14001132.html