SQL Server存储过程在C#中调用的简单实现方法
0. 简介
【定义】:存储过程(stored procedure) 是在大型数据库系统中,一组为了完成特定功能的sql 语句集,它存储在数据库中,一次编译后永久有效,用户通过指定存储过程的名字并给出参数(如果该存储过程带有参数)来执行它。
【优缺点】:存储过程优缺点都非常的明显!几乎每一篇讨论存储过程的文字,都是会说其他优点是balabala,缺点是balabala,然而最后作者的结论都是:“我不推荐使用存储过程”。
具体的存储过程的优缺点这里就不详述了!
公司旧项目使用存储过程实现业务逻辑,没办法只能研究了一下 ????!
闲言碎语不要讲,书归正传,下面就开始存储过程!
1. 语法细节
变量与变量之间使用逗号隔开,语句结尾无标点符号
声明变量:declare @variate_name variate_type
,例如声明并赋值:declare @name nvarchar(50) ='shanzm'
变量赋值:set @variate_name =value
打印变量:print @variate_name
begin……end 之间的sql语句称之为一个代码块
可以使用if……else实现逻辑判断
创建存储过程:create procedure pro_name
执行存储过程:execute pro_name
输出参数:存储过程返回的是sql语句查阅结果,在定义参数后,添加output,设置为一个输出参数(和c#中输出参数类似),相当于多了一个返回值!
创建存储过程的基本形式:
create procedure pro_name_tablename @param1 param1_type, @param2 param2_type, as begin --sql语句 end go
2. 示例1:模拟转账
①示例背景:使用存储过程,模拟在一张存款表中实现用户与用户之间的转账
②准备工作1:在数据库中创建表szmbank
create table [dbo].[szmbank]( [id] [bigint] identity(1,1) not null, [balance] [decimal](18, 0) not null
添加一些测试数据:
id balance
--------------- ----------------
1 1000
2 2000
3 3000
③准备工作2:封装c#代码中的sql辅助类sqlhelper
注意封装的时候要有一个commandtype参数,决定是执行sql语句还是存储过程,
commandtype是一个枚举类型,其中text值为执行sql语句,storeprocedure为执行存储过程
具体封装细节这里就不详述了。
找到了2年前我封装的一个sqlhelper.cs,常规使用没有任何问题,仅供参考:
#region // =============================================================================== // project name : // project description : // =============================================================================== // class name : sqlhelper // class version : v1.0.0.0 // class description : sql语句辅助类 // clr : 4.0.30319.18408 // author : shanzm // create time : 2018-8-14 18:22:59 // update time : 2018-8-14 18:22:59 // =============================================================================== // copyright © shanzm-pc 2018 . all rights reserved. // =============================================================================== #endregion using system.configuration; using system.data; using system.data.sqlclient; namespace _16storeprocedure { public class sqlhelper { private static readonly string connstr = configurationmanager.connectionstrings["connstr"].connectionstring; /// <summary> /// 返回查询结果的的表 /// </summary> /// <param name="sql">sql语句或存储过程</param> /// <param name="type">执行类型</param> /// <param name="param">参数</param> /// <returns></returns> public static datatable getdatatable(string sql, commandtype type, params sqlparameter[] param) { using (sqlconnection conn = new sqlconnection(connstr)) { using (sqldataadapter adapter = new sqldataadapter(sql, conn)) { if (param != null) { adapter.selectcommand.parameters.addrange(param); } adapter.selectcommand.commandtype = type; datatable da = new datatable(); adapter.fill(da); return da; } } } /// <summary> /// 返回影响行数 /// </summary> /// <param name="sql">sql语句或存储过程</param> /// <param name="type">执行类型</param> /// <param name="param">参数</param> /// <returns></returns> public static int executenonquery(string sql, commandtype type, params sqlparameter[] param) { using (sqlconnection conn = new sqlconnection(connstr)) { using (sqlcommand cmd = new sqlcommand(sql, conn)) { if (param != null) { cmd.parameters.addrange(param); } cmd.commandtype = type; conn.open(); return cmd.executenonquery(); } } } /// <summary> /// 返回查询结果的第一行第一个单元格的数据 /// </summary> /// <param name="sql">sql语句或存储过程</param> /// <param name="type">执行类型</param> /// <param name="param">参数</param> /// <returns></returns> public static object executescalar(string sql, commandtype type, params sqlparameter[] param) { using (sqlconnection conn=new sqlconnection (connstr )) { using (sqlcommand cmd=new sqlcommand (sql,conn)) { if (param !=null ) { cmd.parameters.addrange(param); } cmd.commandtype = type ; conn.open(); return cmd.executescalar(); } } } } }
④编写存储过程:
在数据库中:指定数据库-->可编程性-->存储过程-->右键:新建-->存储过程:
sql server中编写的sql语句没有默认的格式化,所有代码排版按照我自己习惯进行tab缩进
建议放到编辑器中查看下面的存储过程,会好看一些!
sql大小写不敏感,我习惯小写,方便阅读!
-- ============================================= -- author: shanzm -- create date: 2020年5月2日 19:56:51 -- description: 模拟账户之间转账 -- ============================================= create procedure pro_transfer_szmbank @from bigint, @to bigint, @balance decimal(18,0), @returnnum int output--(1表示转账成功,2表示失败,3表示余额不足) as begin --判断转出账户是否有足够的金额 declare @money decimal(18,0) select @money=balance from dbo.szmbank where id=@from; if @money-@balance>=0.1 --开始转账 begin begin transaction declare @sum int =0 --转出账户扣钱 update szmbank set balance=balance-@balance where id=@from set @sum=@sum+@@error --转入账户加钱 update szmbank set balance=balance+@balance where id=@to set @sum=@sum+@@error --判断是否成功 if @sum<>0 begin set @returnnum=2--转账失败 rollback end else begin set @returnnum=1--转账成功 commit end end else begin set @returnnum=3--余额不足 end end go
在数据库中执行测试(f5):
--执行测试: declare @ret int execute pro_transfer_szmbank @from='1', @to='2', @balance='10', @returnnum=@ret output--注意输出参数在执行语句中也是要表明"output" print @ret --结果是打印:1,即存储过程实现成功
【注意】:
- 我们需要查看某个存储过程,则可以使用数据中自带的存储过程查看:
- sp_helptext pro_transfer_szmbank
- 修改现有的存储过程,右键存储过程-->修改:显示的存储过程只是把创建存储过程中的create变为了alert
- 可以在sql server的sql窗口选中某些sql语句,点击执行,即执行选中的sql语句
⑤控制台中测试
新建一个控制台项目,在配置文件中添加连接字符串
因为封装的sqlhelper中需要从配置文件中读取数据库连接字符串,所以添加引用:system.configuration
static void main(string[] args) { //转出账户的id int from = 1; //转入账户的id int to = 2; //转账金额 decimal balance = 10; sqlparameter[] param = { new sqlparameter ("@from",from), new sqlparameter("@to",to), new sqlparameter ("@balance",balance), //-------------------------------注意:这里设置为输出参数 new sqlparameter ("@returnnum",system.data.sqldbtype.int{direction=system.data.parameterdirection.output } }; //------------------------设置commontype为storprocedure类型 sqlhelper.executenonquery("pro_transfer_szmbank",system.data.commandtype.storedprocedure, param); //------------------------获取输出参数 //根据输出参数判断转账结果 int outputparam = convert.toint16(param[3].value); switch (outputparam) { case 1: console.writeline($"success:从id:{from}转账{balance}元到id:{to}");break; case 2: console.writeline("error"); break; case 3: console.writeline("余额不足"); break; } console.readkey(); }
测试结果:
success:从id:1转账10元到id:2
3. 示例2:测试返回datatable
①存储过程:
create procedure [dbo].[pro_returndatatable] as begin select id as 用户id ,balance as 余额 from szmbank; end go
②数据库中测试:
execute pro_returndatatable
测试结果:即显示szmbank中的所有数据
③控制台中测试:
static void main(string[] args) { datatable dt = sqlhelper.getdatatable("pro_returndatatable", commandtype.storedprocedure); foreach (datarow row in dt.rows) { console.writeline(row["用户id"].tostring() + ":" + row["余额"].tostring()); } console.readkey(); //transferaccounts(); returndatatable(); }
测试结果:即打印szmbank中的所有数据
4. 源代码下载
所需要的数据库表在示例中已说明,可以直接使用建表语句创建!
存储过程的sql语句在示例中完整的展示了,可以直接复制!
总结
到此这篇关于sql server存储过程在c#中调用的文章就介绍到这了,更多相关sql server存储过程在c#调用内容请搜索以前的文章或继续浏览下面的相关文章希望大家以后多多支持!
上一篇: nginx worker进程循环的实现
下一篇: Unity实现截屏以及根据相机画面截图