T-SQL—— C#中调用存储过程的简单示例

shanzm-2020年5月3日 23:23:44

0. 简介

【定义】:存储过程(Stored Procedure) 是在大型数据库系统中,一组为了完成特定功能的SQL 语句集,它存储在数据库中,一次编译后永久有效,用户通过指定存储过程的名字并给出参数(如果该存储过程带有参数)来执行它。

【优缺点】:存储过程优缺点都非常的明显!几乎每一篇讨论存储过程的文字,都是会说其他优点是balabala,缺点是balabala,然而最后作者的结论都是:“我不推荐使用存储过程”
具体的存储过程的优缺点这里就不详述了!



1. 语法细节

  1. 变量与变量之间使用逗号隔开,语句结尾无标点符号

  2. 声明变量:declare @variate_name variate_type,例如声明并赋值:declare @name nvarchar(50) ='shanzm'
    变量赋值:set @variate_name =value
    打印变量:print @variate_name

  3. begin……end 之间的SQL语句称之为一个代码块

  4. 可以使用if……else实现逻辑判断

  5. 创建存储过程:create procedure pro_name

  6. 执行存储过程:execute pro_name

  7. 输出参数:存储过程返回的是SQL语句查阅结果,在定义参数后,添加output,设置为一个输出参数(和C#中输出参数类似),相当于多了一个返回值!

  8. 创建存储过程的基本形式:

create procedure pro_name_tableName
@param1 param1_type =defaultValue,--声明参数的时候可以设置默认参数
@param2 param2_type,
as
begin 
SET NOCOUNT ON;--关闭返回受影响行数
    --sql语句
end
go


2. 示例1:模拟转账

①示例背景:使用存储过程,模拟在一张存款表中实现用户与用户之间的转账

②准备工作1:在数据库中创建表szmBank

CREATE TABLE [dbo].[szmBank]
(
	[Id] [bigint] IDENTITY(1,1) NOT NULL,
	[Balance] [decimal](18, 4) NOT NULL
)

添加一些测试数据:

Id                   Balance
---------------      ----------------
1                    1000
2                    2000
3                    3000

③准备工作2:封装C#代码中的SQL辅助类SqlHelper

注意封装的时候要有一个CommandType参数,决定是执行SQL语句还是存储过程,
CommandType是一个枚举类型,其中Text值为执行SQL语句,StoreProcedure为执行存储过程
具体封装细节这里就不详述了。

④编写存储过程
在数据库中:指定数据库-->可编程性-->存储过程-->右键:新建-->存储过程:

-- =============================================
-- Author:		shanzm
-- Create date: 2020年5月2日 19:56:51
-- Description:	模拟账户之间转账
-- =============================================
CREATE PROCEDURE pro_transfer_szmbank
    @from BIGINT,
    @to BIGINT,
    @balance DECIMAL(18, 4),
    @returnNum INT OUTPUT --(1表示转账成功,2表示失败,3表示余额不足)
AS
BEGIN
    --判断转出账户是否有足够的金额
    DECLARE @money DECIMAL(18, 4);
    SELECT @money = Balance
    FROM dbo.szmBank
    WHERE Id = @from;
    IF @money - @balance >= 0.1
    --开始转账
    BEGIN
        BEGIN TRANSACTION;
        DECLARE @sum INT = 0;

        --转出账户扣钱
        UPDATE dbo.szmBank
        SET balance = balance - @balance
        WHERE id = @from;
        SET @sum = @sum + @@error;

        --转入账户加钱
        UPDATE dbo.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;
--注意1.输出参数在执行语句中也是要表明"output"
--注意2.输入输出参数也是可以不赋值的,这里就没有赋值,但是需要定义一个变量用于存储输出值,这里就是@ret(和使用输出参数out的方式一样)
--注意3.是@returnNum=@ret,而不@ret=@returnNum,这里就和函数中给参数赋值是一样的,参数=value
PRINT @ret; --结果是打印:1,即存储过程实现成功

【注意】:

  • @@ERROR:当前一个语句遇到错误,则返回错误码(比如除零错误的错误码是:8134),否则返回0。

    需要注意的是@@ERROR只对其前一条sql语句有效,在每一条语句执行后会被立刻重置,因此应该在要验证的语句执行后检查数值或者是将它保存到局部变量中以备将来使用。
    这里可以同时使用@@ROWCOUNT来判断更新记录的受影响行数,若是受影响行数是0则返回转账失败的代码。
    参考:将@@ERROR与@@ROWCOUNT一起使用

  • 我们需要查看某个存储过程,则可以使用数据中自带的存储过程查看:
    sp_helptext pro_transfer_szmBank

  • 修改现有的存储过程,右键存储过程-->修改:显示的存储过程只是把创建存储过程中的create变为了alter

  • 定义存成过程的时候参数类型默认的输入参数,还有输出参数(out) 和输入输出参数(output)
    输出参数和输入输出参数的区别就是:输入参数在执行存储过程的时候是不需要赋值的,输入输出参数在执行存储过程的时候需要赋值

⑤控制台中测试

新建一个控制台项目,在配置文件中添加连接字符串

因为封装的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. 源代码下载

  • C#中使用存储过程-源代码下载

  • 所需要的数据库表在示例中已说明,可以直接使用建表语句创建!

  • 存储过程的SQL语句在示例中完整的展示了,可以直接复制!

posted @ 2020-05-03 23:25  shanzm  阅读(1736)  评论(4编辑  收藏  举报
TOP