# SQL语句:先编译后执行
存储过程(Stored Procedure):
一组可编程的函数,是为了完成特定功能的SQL语句集,经编译创建并保存在数据库中,用户可通过指定存储过程的名字并给定参数(需要时)来调用执行。
优点(为什么要用存储过程?):
①将重复性很高的一些操作,封装到一个存储过程中,简化了对这些SQL的调用
②批量处理:SQL+循环,减少流量,也就是“跑批”
③统一接口,确保数据的安全
相对于oracle数据库来说,MySQL的存储过程相对功能较弱,使用较少。
一、存储过程的创建和调用
>存储过程就是具有名字的一段代码,用来完成一个特定的功能。
>创建的存储过程保存在数据库的数据字典中。
public DataSet Query(string procName, ref int Total, SqlParameter[] sqlParameters) { DataSet set = new DataSet(); using (SqlConnection conn = new SqlConnection(con)) { conn.Open(); using (SqlCommand comm = new SqlCommand(procName, conn)) { comm.Parameters.AddRange(sqlParameters); comm.CommandType = CommandType.StoredProcedure; SqlDataAdapter adapter = new SqlDataAdapter(comm); adapter.Fill(set); Total = int.Parse(comm.Parameters["@Total"].Value.ToString()); } } return set; }
下面这个例子展示如何在ADO.NET调用存储过程。我只选择了一种方式,说实话不太喜欢多样化的方式去处理问题,这种发散式的做法在编程中似乎没有必要。选择自己喜欢的一种方式。
这里SQL Server有一个样本数据库Northwind.
以Northwind里的Region表为例:
Region表有2个字段:
- RegionID, int 主键
- RegionDescription, nchar(50)
写以下几个存储过程:
RegionUpdate: 多个参数
CREATE PROCEDURE RegionUpdate(@RegionID INTEGER, @RegionDescription NCHAR(50))
AS
SET NOCOUNT OFF
UPDATE Region
SET RegionDescription = @RegionDescription
WHERE RegionID = @RegionID
GO
RegionDelete 一个参数
CREATE PROCEDURE RegionUpdate(@RegionID INTEGER)
AS
SET NOCOUNT OFF
DELETE FROM Region
WHERE RegionID = @RegionID
GO
RegionInsert 带返回参数
CREATE PROCEDURE RegionInsert(@RegionDescription NCHAR(50), @RegionID INTEGER OUTPUT)
AS
SET NOCOUNT OFF
SELECT @RegionID = MAX(RegionID) + 1
FROM Region
INSERT INTO Region VALUES(@RegionID, @RegionDescription)
GO
public DataTable GetPagingDept(ref int Total, int pageSize, int pageNumber, string where, string field) { SqlParameter[] sqlParameters = new SqlParameter[] { new SqlParameter{ ParameterName="@table",Value=" Shopping ",SqlDbType=SqlDbType.NVarChar,Direction= ParameterDirection.Input,Size=200}, new SqlParameter{ ParameterName="@field",Value=field,SqlDbType=SqlDbType.NVarChar,Direction= ParameterDirection.Input,Size=2000}, new SqlParameter{ ParameterName="@where",Value=where,SqlDbType=SqlDbType.NVarChar,Direction= ParameterDirection.Input,Size=2000}, new SqlParameter{ ParameterName="@order",Value=" Id ",SqlDbType=SqlDbType.NVarChar,Direction= ParameterDirection.Input,Size=200}, new SqlParameter{ ParameterName="@pageSize",Value=pageSize,SqlDbType=SqlDbType.Int,Direction= ParameterDirection.Input}, new SqlParameter{ ParameterName="@pageNumber",Value=pageNumber,SqlDbType=SqlDbType.Int,Direction= ParameterDirection.Input}, new SqlParameter{ ParameterName="@Total",SqlDbType=SqlDbType.Int,Direction= ParameterDirection.Output}, }; return db.Query("[SP_UserInfo]", ref Total, sqlParameters).Tables[0]; }
我假设我们已经对IDataParameter对象进行了封装,我想对它简单的封装基本也都能满足日常要求了。一般都是根据当前项目链接数据库的类型字符串进行判断,然后生成相对应如:SqlParameter、OracleParameter、OleDbParameter等等,可能还包括一些开源的数据库扩展框架中的对象,这里的创建工厂可能是抽象工厂,当然方法很多种,实现效果就行。[王清培版权所有,转载请给出署名]
对其简单的封装我们在使用的时候需要使用工厂方法创建IDataParameter数组,如:
一般性的封装基本都这样或者在IDataParameterFactory.CreateDbDataParameter(Entity)中加入根据实体的属性动态的创建IDataParameter[]对象,如果你的创建始终是使用反射的话那么将是不可取的。有兴趣的朋友可以参见本人的另一篇文章“利用抽象、多态实现无反射的绿色环保ORM框架”对实体的使用如果不能摆脱反射,那么在以后的基础库扩展中将面临着很多性能问题,这里需三思。
由于很少存储过程的参数名称都是对应的实体的属性名称,这种对应关系很难做到,或者说是做到的话需要DBA花点时间呢,在命名上也是个约束。
如果存储过程有N个参数的话我们需要对照数据库设计文档来编写IDictionary项,在一般的项目中都将复杂的业务逻辑封装在存储过程中实现,所以存储过程的数量也是不少的。这样一来也算是一个比较浪费时间的工作。
那么如果减少编码量,让存储过程的调用变的简单,而且对用户来说是透明的?
public IHttpActionResult Show(int pageSize, int pageNumber,string name) { int Total = 0; if (!string.IsNullOrEmpty(name)) { Total = 0; name = " Sho_Name like '%" + name + "%'"; } else { name = " 1=1 "; } DataTable tb = bll.GetPagingDept(ref Total, pageSize, pageNumber, name, " * "); PagingModel<DataTable> paging = new PagingModel<DataTable> { tb = tb, Total = Total }; return Ok(paging); }