浅析存储过程

阅读目录

  • 一: 存储过程概述

  • 二:存储过程分类

  • 三:创建存储过程

  • 1.创建无参存储过程

  • 2.修改存储过程

  • 3.删除存储过程

  • 4.重命名存储过程

  • 5.创建带参数的存储过程

简单来说,存储过程就是一条或者多条sql语句的集合,可视为批处理文件,但是其作用不仅限于批处理。本篇主要介绍变量的使用,存储过程和存储函数的创建,调用,查看,修改以及删除操作。

一:存储过程概述

SQL Server中的存储过程是使用T_SQL编写的代码段。它的目的在于能够方便的从系统表中查询信息,或者完成与更新数据库表相关的管理任务和其他的系统管理任务.T_SQL语句是SQL Server数据库与应用程序之间的编程接口。在很多情况下,一些代码会被开发者重复编写多次,如果每次都编写相同功能的代码,不但繁琐,而且容易出错,而且由于SQL Server逐条的执行语句会降低系统的运行效率。

简而言之,存储过程就是SQL Server为了实现特定任务,而将一些需要多次调用的固定操作语句编写成程序段,这些程序段存储在服务器上,有数据库服务器通过程序来调用。

存储过程的优点:

  1. 存储过程加快系统运行速度,存储过程只在创建时编译,以后每次执行时不需要重新编译。

  2. 存储过程可以封装复杂的数据库操作,简化操作流程,例如对多个表的更新,删除等。

  3. 可实现模块化的程序设计,存储过程可以多次调用,提供统一的数据库访问接口,改进应用程序的可维护性。

  4. 存储过程可以增加代码的安全性,对于用户不能直接操作存储过程中引用的对象,SQL Server可以设定用户对指定存储过程的执行权限。

  5. 存储过程可以降低网络流量,存储过程代码直接存储于数据库中,在客户端与服务器的通信过程中,不会产生大量的T_SQL代码流量。

存储过程的缺点:

  1. 数据库移植不方便,存储过程依赖与数据库管理系统, SQL Server 存储过程中封装的操作代码不能直接移植到其他的数据库管理系统中。

  2. 不支持面向对象的设计,无法采用面向对象的方式将逻辑业务进行封装,甚至形成通用的可支持服务的业务逻辑框架.

  3. 代码可读性差,不易维护。不支持集群。

二:存储过程分类

1.系统存储过程

系统存储过程是 SQL Server系统自身提供的存储过程,可以作为命令执行各种操作。

系统存储过程主要用来从系统表中获取信息,使用系统存储过程完成数据库服务器的管理工作,为系统管理员提供帮助,为用户查看数据库对象提供方便,系统存储过程位于数据库服务器中,并且以sp_开头,系统存储过程定义在系统定义和用户定义的数据库中,在调用时不必在存储过程前加数据库限定名。例如:sp_rename系统存储过程可以修改当前数据库中用户创建对象的名称,sp_helptext存储过程可以显示规则,默认值或视图的文本信息,SQL SERVER服务器中许多的管理工作都是通过执行系统存储过程来完成的,许多系统信息也可以通过执行系统存储过程来获得。

系统存储过程创建并存放在与系统数据库master中,一些系统存储过程只能由系统管理员使用,而有些系统存储过程通过授权可以被其它用户所使用。

2.用户存储过程(自定义存储过程)

自定义存储过程即用户使用T_SQL语句编写的、为了实现某一特定业务需求,在用户数据库中编写的T_SQL语句集合,自定义存储过程可以接受输入参数、向客户端返回结果和信息,返回输出参数等。创建自定义存储过程时,存储过程名前加上”##”表示创建了一个全局的临时存储过程;存储过程前面加上”#”时,表示创建的局部临时存储过程。局部临时存储过程只能在创建它的回话中使用,会话结束时,将被删除。这两种存储过程都存储在tempdb数据库中。

用户定义的存储过程分为两类:T_SQLCLR

T_SQL存储过程

是指保存的T_SQL语句集合,可以接受和返回用户提供的参数,存储过程也可能从数据库向客户端应用程序返回数据。

CLR存储过程

是指引用Microsoft.NET Framework公共语言的方法存储过程,可以接受和返回用户提供的参数,它们在.NET Framework程序集是作为类的公共静态方法实现的。

3.扩展存储过程

扩展存储过程是以在SQL SERVER环境外执行的动态连接(DLL文件)来实现的,可以加载到SQL SERVER实例运行的地址空间中执行,扩展存储过程可以用SQL SERVER扩展存储过程API编程,扩展存储过程以前缀”xp_”来标识,对于用户来说,扩展存储过程和普通话存储过程一样,可以用相同的方法来执行。

三:创建存储过程

工欲善其事,必先利其器,准备数据如下:

use sample_db;
--创建测试books表
create table books (
    book_id int identity(1,1) primary key,
    book_name varchar(20),
    book_price float,
    book_auth varchar(10)
);
--插入测试数据
insert into books (book_name,book_price,book_auth)
                    values
                        ('论语',25.6,'孔子'),
                        ('天龙八部',25.6,'金庸'),
                        ('雪山飞狐',32.7,'金庸'),
                        ('平凡的世界',35.8,'路遥'),
                        ('史记',54.8,'司马迁');

1.创建无参存储过程

--1.创建无参存储过程
if (exists (select * from sys.objects where name = 'getAllBooks'))
    drop proc proc_get_student
go
create procedure getAllBooks
as
select * from books;
--调用,执行存储过程
exec getAllBooks;

2.修改存储过程

alter procedure dbo.getAllBooks 
as
select book_auth from books;

3.删除存储过程

drop procedure getAllBooks;

4.重命名存储过程

sp_rename getAllBooks,proc_get_allBooks;

5.创建带参数的存储过程

存储过程的参数分为两种:输入参数和输出参数

输入参数:用于向存储过程传入值,类似java语言或则c中的值传递。

输出参数:用于调用存储过程后,参会结果,类似java语言的按引用传递。

值传递和引用传递区别:

  • 基本数据类型赋值属于值传递;引用类型之间赋值属于引用传递。

  • 值传递传递的是实实在在的变量值;引用传递传递的是对象的引用地址。

  • 值传递后,两个变量改变的是各自的值;引用传递后,两个引用改变的是同一个对象的状态

(1)、带一个参数存储过程

if (exists (select * from sys.objects where name = 'searchBooks'))
    drop proc searchBooks
go
create proc searchBooks(@bookID int)
as
    --要求book_id列与输入参数相等
    select * from books where book_id=@bookID;
--执行searchBooks
exec searchBooks 1;

(2)、带2个参数存储过程

if (exists (select * from sys.objects where name = 'searchBooks1'))
    drop proc searchBooks1
go
create proc searchBooks1(
    @bookID int,
    @bookAuth varchar(20)
)
as
    --要求book_id和book_Auth列与输入参数相等
    select * from books where book_id=@bookID and book_auth=@bookAuth;
exec searchBooks1 1,'金庸';

(3)、创建有返回值的存储过程

if (exists (select * from sys.objects where name = 'getBookId'))
    drop proc getBookId
go
create proc getBookId(
    @bookAuth varchar(20),--输入参数,无默认值
    @bookId int output --输入/输出参数 无默认值
)
as
    select @bookId=book_id from books where book_auth=@bookAuth
--执行getBookId这个带返回值的存储过程
declare @id int --声明一个变量用来接收执行存储过程后的返回值
exec getBookId '孔子',@id output
select @id as bookId;--as是给返回的列值起一个名字

(4)、创建带通配符的存储过程

if (exists (select * from sys.objects where name = 'charBooks'))
    drop proc charBooks
go
create proc charBooks(
    @bookAuth varchar(20)='金%',
    @bookName varchar(20)='%'
)
as 
    select * from books where book_auth like @bookAuth and book_name like @bookName;
--执行存储过程charBooks
exec  charBooks    '孔%','论%';

(5)、加密存储过程

with encryption子句对用户隐藏存储过程的文本.下例创建加密过程,使用 sp_helptext 系统存储过程获取关于加密过程的信息,然后尝试直接从 syscomments 表中获取关于该过程的信息.

if (object_id('books_encryption', 'P') is not null)
    drop proc books_encryption
go
create proc books_encryption 
with encryption
as 
    select * from books;
--执行此过程books_encryption
exec books_encryption;
exec sp_helptext 'books_encryption';--控制台会显示"对象 'books_encryption' 的文本已加密。"

(6)、不缓存存储过程

--with  recompile不缓存
if (object_id('book_temp', 'P') is not null)
    drop proc book_temp
go
create proc book_temp
with recompile
as
    select * from books;
go

exec book_temp;
exec sp_helptext 'book_temp';

(7)、创建带游标参数的存储过程

if (object_id('book_cursor', 'P') is not null)
    drop proc book_cursor
go
create proc book_cursor
    @bookCursor cursor varying output
as
    set @bookCursor=cursor forward_only static for
    select book_id,book_name,book_auth from books
    open @bookCursor;
go
--调用book_cursor存储过程
declare @cur cursor,
        @bookID int,
        @bookName varchar(20),
        @bookAuth varchar(20);
exec book_cursor @bookCursor=@cur output;
fetch next from @cur into @bookID,@bookName,@bookAuth;
while(@@FETCH_STATUS=0)
begin 
    fetch next from @cur into @bookID,@bookName,@bookAuth;
    print 'bookID:'+convert(varchar,@bookID)+' , bookName: '+ @bookName
            +' ,bookAuth: '+@bookAuth;
end
close @cur    --关闭游标
DEALLOCATE @cur; --释放游标

(8)、创建分页存储过程

if (object_id('book_page', 'P') is not null)
    drop proc book_page
go
create proc book_page(
    @TableName varchar(50),            --表名
    @ReFieldsStr varchar(200) = '*',   --字段名(全部字段为*)
    @OrderString varchar(200),         --排序字段(必须!支持多字段不用加order by)
    @WhereString varchar(500) =N'',  --条件语句(不用加where)
    @PageSize int,                     --每页多少条记录
    @PageIndex int = 1 ,               --指定当前为第几页
    @TotalRecord int output            --返回总记录数
)
as
begin
     --处理开始点和结束点
    Declare @StartRecord int;
    Declare @EndRecord int; 
    Declare @TotalCountSql nvarchar(500); 
    Declare @SqlString nvarchar(2000);    
    set @StartRecord = (@PageIndex-1)*@PageSize + 1
    set @EndRecord = @StartRecord + @PageSize - 1 
    SET @TotalCountSql= N'select @TotalRecord = count(*) from ' + @TableName;--总记录数语句
    SET @SqlString = N'(select row_number() over (order by '+ @OrderString +') as rowId,'+@ReFieldsStr+' from '+ @TableName;--查询语句
    --
    IF (@WhereString! = '' or @WhereString!=null)
        BEGIN
            SET @TotalCountSql=@TotalCountSql + '  where '+ @WhereString;
            SET @SqlString =@SqlString+ '  where '+ @WhereString;            
        END
    --第一次执行得到
    --IF(@TotalRecord is null)
    --   BEGIN
           EXEC sp_executesql @totalCountSql,N'@TotalRecord int out',@TotalRecord output;--返回总记录数
    --  END
    ----执行主语句
    set @SqlString ='select * from ' + @SqlString + ') as t where rowId between ' + ltrim(str(@StartRecord)) + ' and ' +  ltrim(str(@EndRecord));
    Exec(@SqlString)    
END
--调用分页存储过程book_page
exec book_page 'books','*','book_id','',3,1,0;

--
declare @totalCount int
exec book_page 'books','*','book_id','',3,1,@totalCount output; 
select @totalCount as totalCount;--总记录数。

参考博客:

https://www.cnblogs.com/selene/p/4483612.html

评论列表

1楼 2016-01-21 09:57 柚子茶丶

exec searchBooks 1; 这句话报错 说超出 最大嵌套层数 ,本人小白


2楼 2016-01-21 10:01 柚子茶丶

if (exists (select * from sys.objects where name = 'searchBooks')) drop proc searchBooks go

这句话在建立存储时 ,不执行,直接建立存储过程,然后运行,exec searchBooks 1; 就不会报错。


3楼 2016-09-19 15:00 willxiang

创建无参存储过程:

这里创建存储过程的时候后边直接跟了一句执行查询的操作,如果没注意直接粘贴复制后执行存储过程会报错,
建议在exec getAllBooks 后边加一句 go


4楼 2017-06-29 12:27 xs~ing

创建带通配符的存储过程那里不是很明白,参数上面有赋值了,后面执行的时候为什么还要再赋值?是不是再赋值的时候会覆盖掉原来参数的值?我执行的时候报错“超出了存储过程、函数、触发器或视图的最大嵌套层数(最大层数为 32)。”


5楼 2017-10-07 20:58 hello_wind

@ happy萍儿
这里我尝试回复一下:
1)带通配符的存储过程,创建存储过程时给变量的赋值为默认值,执行存储过程时没有另行赋值时这里会起作用,比如本例中,执行exec charBooks则等同于执行exec charBooks '金%','%'
2)报错问题参见12/13/15楼的回答。


6楼 2019-03-25 10:24 KevinZhyp

–1.创建无参存储过程

if (exists (select * from sys.objects where name = 'getAllBooks')) drop proc proc_get_student go create procedure getAllBooks as
select * from books;

–调用,执行存储过程

exec getAllBooks;

这个里面为什么判断了‘getAllBooks’,然后drop的是‘proc_get_student’?


7楼 2019-05-10 16:46 熊猫大侠FB

@ KevinZhyp
作者这里是写错了, 应该是 drop getAllBooks 才对~其他代码都可完美运行


8楼 2021-06-18 17:56 思复诵

if (exists (select * from sys.objects where name = 'getAllBooks')) drop proc proc_get_student go create procedure getAllBooks as select * from books;

这个有错误 drop proc 后面应该是getAllBooks


使用案例

在数据库创建存储过程

USE [gspcrm_MSCRM]
GO
/****** Object:  StoredProcedure [dbo].[Update_deliveryline]    Script Date: 2022/7/6 16:43:42 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER procedure [dbo].[Update_deliveryline] 
---定义传入参数 中间表id
@tempid uniqueidentifier,
@ownerid uniqueidentifier,
@owneribu uniqueidentifier
as
--定义循环总条数
DECLARE @count INT
SET @count=0
SELECT @count = COUNT(1) FROM deliverline_temp where  tempId =@tempid
--定义循环下标
DECLARE @i INT
SET @i=1

-- 循环
WHILE @i<= @count

BEGIN
--定义发货主档id
DECLARE @deliveryid uniqueidentifier
SELECT @deliveryid = (select top 1 new_ord_deliveryplan_id from deliverline_temp where  tempId =@tempid)
print @deliveryid
--定义发货明细id
DECLARE @deliverylineid uniqueidentifier
SELECT @deliverylineid = (
  select top 1 new_ord_deliveryplanlineid from new_ord_deliveryplanline a  
  inner join deliverline_temp b on  
  a.new_ord_deliveryplan_id = b.new_ord_deliveryplan_id 
  and a.new_ord_saleorder_id = b.new_ord_saleorder_id
  and a.new_ord_saleorderline_id = b.new_ord_saleorderline_id
  where seq = @i and b.tempId =@tempid)

  --定义当前发货数量
  DECLARE @new_quantity decimal(10,2)
  select @new_quantity = (select top 1 new_quantity from deliverline_temp where  seq = @i and tempId =@tempid)
  print @new_quantity
  --定义当前订单明细id
  DECLARE @orderlineid  uniqueidentifier
  select @orderlineid = (select top 1 new_ord_saleorderline_id from deliverline_temp where  seq = @i and tempId =@tempid)
  print @orderlineid
  --定义订单id
  DECLARE @orderid  uniqueidentifier
  select @orderid = (select top 1 new_ord_saleorder_id from deliverline_temp where  seq = @i and tempId =@tempid)
  ---判断定义发货明细id 是否存在
  if  (@deliverylineid is not null)
  begin
  print '更新'
  ---1.存在发货明细,进行更新处理
  --1.1 更新本次发货数量和承诺数量 做加法
  update new_ord_deliveryplanlinebase set new_quantity =  new_committedquantity+@new_quantity,new_committedquantity = new_committedquantity+@new_quantity where @deliverylineid = new_ord_deliveryplanlineid
  --1.2更新订单明细已添加发货数量
  update new_ord_saleorderlinebase set new_shipmentplanadded =  new_shipmentplanadded+@new_quantity  where @orderlineid = new_ord_saleorderlineid
  --1.3 更新可发货数量 = 订单明细当前数量 - 已添加发货数量
  update new_ord_saleorderlinebase set new_confirm_count =  new_qty- new_shipmentplanadded  where @orderlineid = new_ord_saleorderlineid
  --1.4判断可发货数量 更新 订单发货状态
  update  new_ord_saleorderlinebase set  new_deliveryplanstatus =  (
             case
      when   new_confirm_count = 0    then 3
      when   new_confirm_count >0 and new_confirm_count < new_qty    then 2
      else    1 end ) where @orderlineid = new_ord_saleorderlineid
      --1.5更新折前金额 和折后金额
      update new_ord_deliveryplanlinebase set new_undiscounted = new_quantity * (select top 1 new_actualprice from new_ord_saleorderlinebase where  new_ord_saleorderlineid = @orderlineid),
      new_discountamount = new_quantity * (select isnull(new_actualprice,0) + isnull(new_discountrate,0) from new_ord_saleorderlinebase where  new_ord_saleorderlineid = @orderlineid),
      new_discountamountrmb = new_quantity * (select (isnull(new_actualprice,0) + isnull(new_discountrate,0)) * isnull(exchangerate,0) from new_ord_saleorderlinebase where  new_ord_saleorderlineid = @orderlineid)
      where @deliverylineid = new_ord_deliveryplanlineid
      end
      else
      begin
      ---2.不存在发货明细,进行创建处理
      print '创建'
      insert into new_ord_deliveryplanlinebase (
        new_ord_deliveryplanlineid,
        new_name,
        new_ord_deliveryplan_id,
        new_quantity,
        new_committedquantity,
        new_businessmemo,
        new_approvalstatus,
        new_actualprice,
        new_approval_deliverydate,
        new_newest_deliverydate,
        new_product_id,
        new_qty,
        new_productnumber,
        OwnerId,
        new_discountrate,
        new_ord_saleorder_id,
        new_ord_saleorderline_id,
        new_undiscounted,
        new_discountamount,
        new_discountamountrmb,
        statecode,
        statuscode,
        CreatedOn,
        ModifiedOn,
        CreatedBy,
        ModifiedBy,
        OwningBusinessUnit,
        new_mark
      ) values(
        NEWID(),
        convert(varchar(100),1000+@i -1),
        @deliveryid,
        @new_quantity,
        @new_quantity,
        (select top 1 new_businessmemo from deliverline_temp where  seq = @i and tempId =@tempid),
        1,
        (select top 1  new_actualprice from new_ord_saleorderlinebase where  new_ord_saleorderlineid = @orderlineid),
        (select top 1 new_approval_deliverydate from new_ord_saleorderlinebase where  new_ord_saleorderlineid = @orderlineid),
        (select top 1 new_approval_deliverydate from new_ord_saleorderlinebase where  new_ord_saleorderlineid = @orderlineid),
        (select top 1 new_product_id from new_ord_saleorderlinebase where  new_ord_saleorderlineid = @orderlineid),
        (select top 1 new_qty from new_ord_saleorderlinebase where  new_ord_saleorderlineid = @orderlineid),
        (select top 1 new_name from new_ord_saleorderlinebase where  new_ord_saleorderlineid = @orderlineid),
        @ownerid,
        (select top 1 new_discountrate from new_ord_saleorderlinebase where  new_ord_saleorderlineid = @orderlineid),
        @orderid,
        @orderlineid,
        @new_quantity * (select top 1 new_actualprice from new_ord_saleorderlinebase where  new_ord_saleorderlineid = @orderlineid),
        @new_quantity * (select isnull(new_actualprice,0) + isnull(new_discountrate,0) from new_ord_saleorderlinebase where  new_ord_saleorderlineid = @orderlineid),
        @new_quantity * (select (isnull(new_actualprice,0) + isnull(new_discountrate,0)) * isnull(exchangerate,0) from new_ord_saleorderlinebase where  new_ord_saleorderlineid = @orderlineid),
        0,
        1,
        DATEADD(HOUR,-8,GETDATE()),
        DATEADD(HOUR,-8,GETDATE()),
        @ownerid,
        @ownerid,
        @owneribu,
        (select top 1 new_mark 
         from new_saleorder2memo A
         inner join new_producttype B on A.new_producttype_id2=B.new_producttypeId
         where A.statecode=0 and B.statecode=0
         and B.new_code='506' and A.new_ord_saleorder_id=@orderid))
         --1.2更新订单明细已添加发货数量
         update new_ord_saleorderlinebase set new_shipmentplanadded =  new_shipmentplanadded+@new_quantity  where @orderlineid = new_ord_saleorderlineid
         --1.3 更新可发货数量 = 订单明细当前数量 - 已添加发货数量
         update new_ord_saleorderlinebase set new_confirm_count =  new_qty- new_shipmentplanadded  where @orderlineid = new_ord_saleorderlineid
         --1.3判断可发货数量 更新 订单发货状态
         update  new_ord_saleorderlinebase set  new_deliveryplanstatus =  (case
         when   new_confirm_count = 0    then 3
         when   new_confirm_count >0 and new_confirm_count < new_qty    then 2
         else    1 end ) where @orderlineid = new_ord_saleorderlineid
         end
         --循环一次后下标加1
         SET @i = @i +1

         END
         --循环结束后,处理订单单头信息
         --1.汇总 已添加发货数量
         update  new_ord_saleorderbase
         set new_shipmentplanadded = (select  sum(isnull(new_shipmentplanadded,0)) from new_ord_saleorderlinebase where new_ord_salesorder_id =new_ord_saleorderid)
         where new_ord_saleorderid in (select distinct new_ord_saleorder_id from deliverline_temp  where   tempId =@tempid)
         --1.2 比较已添加发货数量和总数量 赋值发货计划状态
         update  new_ord_saleorderbase 
         set new_deliveryplanstatus = (
             case
             when   new_shipmentplanadded = new_totalqty    then 3
             when   new_shipmentplanadded >0 and new_shipmentplanadded < new_totalqty    then 2
          else    1 end ) 
where new_ord_saleorderid in (select distinct new_ord_saleorder_id from deliverline_temp  where   tempId =@tempid)

后台代码调用

// 存储过程名称
string strProcName = @"exec Update_deliveryline @tempid,@ownerid,@owneribu";

var paramList = new Dictionary<string, object>();
paramList.Add("@tempid", tempId);
paramList.Add("@ownerid", ownerid);
paramList.Add("@owneribu", businessunitid);
Log.InfoMsg("ownerid:" + ownerid);
Log.InfoMsg("owneribu:" + businessunitid);
Broker.ExecuteNonQuery(strProcName, paramList);

   转载规则


《浅析存储过程》 LoveBin 采用 知识共享署名 4.0 国际许可协议 进行许可。
  目录