存储过程

存储过程优点

运行T-SQL语句进行编程有两种办法,一种是把T-SQL语句全部写在应用程序中,并存储在本地;另一种是把部分T-SQL语句编写的程序作为存储过程存储在SQL Server中,只有本地的应用程序调用存储过程。大多数程序员偏向使用后者,原因在于存储过程具有以下优点:

  • 一次编译,多次执行。第一次执行某个过程时,将编译该过程以确定检索数据的最优访问计划。 如果已经生成的计划仍保留在数据库引擎计划缓存中,则该过程随后执行的操作可能重新使用该计划。
  • 可在应用程序中多次调用;修改存储过程不会影响应用程序源代码。
  • 存储过程存储在服务中,能够减少网络流量。比如一个需要数百行T-SQL代码的操作可以通过一条执行存储过程代码的语句来调用,而不需要在网络中发送数百行代码。
  • 存储过程可被作为一种安全机制来充分利用。可以只授予用户执行存储过程的权限,而不授予用户直接访问存储过程中涉及的表的权限。这样,用户只能通过存储过程来访问表,并进行有限的操作,从而保证了表中数据的安全。使用授权操作设置各个用户的权限

存储过程分类

(1)系统存储过程
  SQL Server提供的存储过程,用于执行与系统相关的任务,主要存储在master数据库并以sp_为前缀,例如sp_addtype、sp_rename等。

(2)扩展存储过程
  扩展存储过程是以在SQL Server环境之外执行的动态链接库(Dymatic-Link)Libraries,DDL)来实现的,执行系统存储过程不能胜任的任务,如发邮件、文件处理等,通常以前缀xp_开头。执行扩展存储过程的方式与存储过程的相似。

(3)临时存储过程
  临时存储过程首先是本地存储过程。SQL Server支持两种临时存储过程:局部临时过程和全局临时过程。
  如果存储过程的前面有一个符号“#”,那么它就是局部临时过程,只能在一个用户会话中使用,在当前会话结束时就会被除去。
  如果存储过程的前面有两个符号“##”,那么把该存储过程称为全局临时存储过程,可以在所有用户会话中使用,在使用该过程的最后一个会话结束时除去。

(4)用户定义的存储过程
  用户自定义的存储过程由用户创建的一组T-SQL语句集合组成,可以接收和返回用户提供的参数,完成某些特定功能。
  存储过程创建好且语法正确后,系统将存储过程的名称存储在当前数据库的系统表sysobject中;将存储过程的文本存储在当前数据库的系统表syscomments中。

创建存储过程

存储过程语法如下:

CREATE PROCEDURE|PROC [schema_name.] procedure_name
    -- Add the parameters for the stored procedure here
    [ { @parameter [ type_schema_name. ] data_type }  
        [ VARYING ] [ = default ] [ OUT | OUTPUT | [READONLY]  
    ] [ ,...n ]   
    [WITH <procedure_option> [ ,...n ]]
    [FOR REPLICATION]
AS { [ BEGIN ] sql_statement [;] [ ...n ] [ END ] }

<procedure_option> ::=   
    [ ENCRYPTION ]  
    [ RECOMPILE ]  
    [ EXECUTE AS Clause ]  
  • schema_name:该过程所属的架构的名称。如果在创建过程时未指定架构名称,则自动分配正在创建过程的用户的默认架构。
  • 可以通过使用一个#符号在procedure_name之前创建本地临时过程(#procedure_name)或两个#符号创建全局临时过程(## procedure_name) 。局部临时程序仅对创建了它的连接可见,并且在关闭该连接后将被删除。 全局临时程序可用于所有连接,并且在使用该过程的最后一个会话结束时将被删除。
  • @parameter:指定过程中的参数,是局部的,可以声明一个或多个。
  • 如果指定了FOR REPLICATION,则无法声明参数。
  • parameter可以是输入参数or输出参数,若为输入参数IN可以不写,系统默认;若为输出参数则要加上OUTPUT。
  • 表值参数只能是 INPUT 参数,并且这些参数必须带有 READONLY 关键字。
  • 光标数据类型只能是输出参数和必须附带由 VARYING 关键字。
  • OUT | OUTPUT指示参数是输出参数,使用 OUTPUT 参数将值返回给过程的调用方。
  • [ =default ]:参数的默认值。 如果默认定义值,该函数可以执行而无需指定该参数的值。
  • WITH ENCRYPTION:SQL Server加密syscomments表中包含CREATE PROCEDURE语句文本的条目,即对用户隐藏存储过程的文本,不能从syscomments表中获取该存储过程的信息。
  • WITH RECOMPILE:指示数据库引擎不缓存该过程的计划,该过程将在每次运行时重新编译。如果指定了FOR REPLICATION,则不能使用此选项。
  • EXECUTE AS子句:指定在其中执行过程的安全上下文。

关于参数

  • 存储过程参数也可以带有默认值,如:
create procedure pun_info @pubname varchar(20)='ALGOdata'
  • 存储过程参数可以带有通配符,如:
create procedure pun_info 
   @name varchar(20)='D%'
as
  select name from authors where name like @name

关于输出
①OUTPUT参数
  如果在过程定义中为参数指定 OUTPUT 关键字,则存储过程在退出时可将该参数的当前值返回至调用程序。若要用变量保存参数值以便在调用程序中使用,则调用程序必须在执行存储过程时使用 OUTPUT 关键字。
  也可以在执行过程时为 OUTPUT 参数指定输入值。 这将允许过程从调用程序接收值,使用该值更改或执行操作,然后将新值返回给调用程序。
②使用返回代码返回数据
  过程可以返回一个整数值(称为“返回代码”),以指示过程的执行状态。 使用 RETURN 语句指定过程的返回代码。 与 OUTPUT 参数一样,执行过程时必须将返回代码保存到变量中,才能在调用程序中使用返回代码值。
  RETURN是从查询或过程中无条件退出,不执行位于 RETURN 之后的语句。RETURN返回的不能是空值,如果过程试图返回空值,将生成警告信息并返回 0 值。用输出参数OUTPUT可以输出任意类型的结果(不包括表类型),而RETURN只能返回整型并且总能返回一个整型值。一般的RETURN用来返回返回代码(如0表示执行成功,1表示未指定所需参数值)。
  RETURN和OUTPUT还可以出现在同一存储过程中,详见示例(3)。

局限与限制

①在单个批处理中,CREATE PROCEDURE 语句不能与其他 Transact-SQL 语句组合使用。
②以下语句不能用于存储过程主体中的任何地方。



③过程可以引用尚不存在的表。 在创建时,只进行语法检查。 直到第一次执行该过程时才对其进行编译。 只有在编译过程中才解析过程中引用的所有对象。 因此,如果语法正确的过程引用了不存在的表,则仍可以成功创建;但如果被引用的表不存在,则过程将在执行时将失败。
④不能将某一函数名称指定为参数默认值或者在执行过程时传递给参数的值。 但是,您可以将函数作为变量传递,如以下示例中所示:

-- Passing the function value as a variable.  
DECLARE @CheckDate datetime = GETDATE();  
EXEC dbo.uspGetWhereUsedProductID 819, @CheckDate;   

⑤如果该过程对 SQL Server 的远程实例进行更改,将无法回滚这些更改。 远程过程不参与事务。

执行存储过程

调用存储过程使用Execute|Exec关键字,不能省略。

Execute|Exec
{
  [@整形变量=]
  存储过程名[,n]|@存储过程变量名
  [[@过程参数=]参数值|@可变参数名 [OUTPUT]|[DEFAULT]]
  [,..,n]
  [WITH RECOMPILE]
}
  • @整形变量:可选,代表存储过程的返回状态。
  • n:可选,用于对同名的过程分组。
  • @过程参数:为存储过程的参数赋值。

SQL Server提供了两种传递参数的办法:
(1)按位置传递参数,即传递的参数和定义时的参数顺序一致,如:
execute au_info 'Dull','Ann'
(2)通过参数名传递,采用“参数=值”的形式,此时各个参数可以任意排序,如:
execute au_info @firstName='Dull',@lastName='Ann' 或
execute au_info @lastName='Ann',@firstName='Dull'

  • OUTPUT:指定该参数为输出参数。
  • DEFAULT:指明该参数使用默认值。如果该参数定义时没有指定默认值,则不能使用DEFAULT选项。
  • WITH RECOMPILE:强制在执行存储过程时重新对其进行编译。

【示例】
(1)带OUTPUT参数的存储过程——最后的返回值存储在调用程序声明的OUTPUT变量中

create procedure Query_Relationer
   @QueryCID int,                   -- 输入的形参
   @QueryRName varchar(20) OUTPUT   -- 输出的形参
as
begin
  if exists(select rid from Customer where cid = @QueryCID)
    select @QueryRName = RName from Relationer
    where rid = (select rid from Customer where cid = @QueryCID and cStatus = 1)
  else
    set @QueryRName = '不存在'
end
go

调用过程如下:

declare @Relationer_name varchar(20),@Cust_ID int
execute Query_Relationer @Cust_ID=20103530,@Relationer_name OUTPUT
print '客户ID为'+convert(char(8),@Cust_ID)+'的联系人是:'+@Relationer_name

(2)带Return参数的存储过程

create proc up_user
as
delcare @age int
begin
  select @age=uage from user
  return @age
end

(3)同时带Return和output参数的存储过程

create proc up_user
@id int,
@name varchar(20) output
as
 declare @age int
 begin
  select @age=stuage,@name=stuname from stuinfo where uid=@id
  return @age
 end

调用过程如下:

declare @age int
declare @name varchar(20)
exec @age=up_user 2,@name output
-- 输出age和name
select @age,@name

存储过程传递集合参数以及返回、接收结果集

(1)传递集合参数

A、传递多个形参

B、使用表值参数
  使用表值参数类型将多个行插入表中。 一下示例将创建参数类型,声明表变量来引用它,填充参数列表,然后将值传递给存储过程。 存储过程使用这些值将多个行插入表中。

/* Create a table type. */  
CREATE TYPE LocationTableType AS TABLE   
( LocationName VARCHAR(50)  
, CostRate INT );  
GO  
  
/* Create a procedure to receive data for the table-valued parameter. */  
CREATE PROCEDURE usp_InsertProductionLocation  
    @TVP LocationTableType READONLY  
    AS   
    SET NOCOUNT ON  
    INSERT INTO [AdventureWorks2012].[Production].[Location]  
           ([Name]  
           ,[CostRate]  
           ,[Availability]  
           ,[ModifiedDate])  
        SELECT *, 0, GETDATE()  
        FROM  @TVP;  
GO  
  
/* Declare a variable that references the type. */  
DECLARE @LocationTVP   
AS LocationTableType;  
  
/* Add data to the table variable. */  
INSERT INTO @LocationTVP (LocationName, CostRate)  
    SELECT [Name], 0.00  
    FROM   
    [AdventureWorks2012].[Person].[StateProvince];  
  
/* Pass the table variable data to a stored procedure. */  
EXEC usp_InsertProductionLocation @LocationTVP;  
GO  

(2)返回结果集

A、使用 OUTPUT 游标参数
  以下示例使用 OUTPUT 游标参数将过程的局部游标传递回执行调用的批处理、过程或触发器。
  首先,创建在 Currency表上声明并打开一个游标的过程:

IF OBJECT_ID ( 'dbo.uspCurrencyCursor', 'P' ) IS NOT NULL  
    DROP PROCEDURE dbo.uspCurrencyCursor;  
GO  
CREATE PROCEDURE dbo.uspCurrencyCursor   
    @CurrencyCursor CURSOR VARYING OUTPUT  
AS  
    SET NOCOUNT ON;  
    SET @CurrencyCursor = CURSOR  
    FORWARD_ONLY STATIC FOR  
      SELECT CurrencyCode, Name  
      FROM Sales.Currency;  
    OPEN @CurrencyCursor;  
GO  

接下来,运行以下批处理:声明一个局部游标变量,执行上述过程以将游标赋值给局部变量,然后从该游标提取行。

DECLARE @MyCursor CURSOR;  
EXEC dbo.uspCurrencyCursor @CurrencyCursor = @MyCursor OUTPUT;  
WHILE (@@FETCH_STATUS = 0)  
BEGIN;  
     FETCH NEXT FROM @MyCursor;  
END;  
CLOSE @MyCursor;  
DEALLOCATE @MyCursor;  
GO  

B、使用OUTPUT返回多个输出参数
  这种方法缺点在于如果结果集中几百个元素,那么在存储过程就要声明几百个变量,十分麻烦。

CREATE PROCEDURE Student.singS
      @id int,
      @name varchar(20) OUTPUT,
      @age int OUTPUT
AS
   select name,age from Student where id=@id
GO

调用段:

DECLARE @name varchar(20),@age int
EXECUTE Student.singS 28, @name OUTPUT,@age OUTPUT
print '学生的姓名为:'+@name+',年龄为:'+@age

C、SELECT返回结果集
  在存储过程中写一段返回一个结果集的SELECT语句,如果在调用段中仅仅EXEC procedure_name [parameter1...parametern],那么该SELECT语句的结果仅仅只会输出到屏幕上,而不能用这个结果集做后续处理。如果要保存此结果集,只有一种方法,即通过使用 INSERT/EXEC 将其存储到永久表、临时表或表变量中,从而将结果流式处理到磁盘。

①把结果集存储在临时表
创建存储过程:

CREATE PROCEDURE Proc1
 @a varchar(50)
AS
 SELECT id,name FROM Table1 WHERE name=@a

调用段:

-- 创建一个临时表,和存储过程的结果集结构一致
CREATE TABLE #t1
(
  id int,
  name varchar(50)
)

-- 把结果集插入临时表中
INSERT INTO #t1 EXEC Proc1 'Ada'
-- do something with results
--用完之后要把临时表清空
DROP TABLE #t1

②把结果集存储在表变量
  但这种方法在查询的数据量较大的情况下比较影响性能,查询速度较慢,在数据量较小的情况下这种差异并不明显。

create proc proc1 as
   select col1 from dbo.table1;

create proc proc2 as
   declare @t table(col1 int);
   insert @t (col1) exec proc1;
   -- do something with results

管理存储过程

①查看存储过程信息


②修改存储过程

ALTER PROCEDURE|PROC [schema_name.] procedure_name
    -- Add the parameters for the stored procedure here
    [ { @parameter [ type_schema_name. ] data_type }  
        [ VARYING ] [ = default ] [ OUT | OUTPUT | [READONLY]  
    ] [ ,...n ]   
    [WITH <procedure_option> [ ,...n ]]
    [FOR REPLICATION]
AS { [ BEGIN ] sql_statement [;] [ ...n ] [ END ] }

<procedure_option> ::=   
    [ ENCRYPTION ]  
    [ RECOMPILE ]  
    [ EXECUTE AS Clause ]

③删除存储过程

DROP { PROC | PROCEDURE } [ IF EXISTS ] { [ schema_name. ] procedure } [ ,...n ]  
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350

推荐阅读更多精彩内容

  • oracle存储过程常用技巧 我们在进行pl/sql编程时打交道最多的就是存储过程了。存储过程的结构是非常的简单的...
    dertch阅读 3,491评论 1 12
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,633评论 18 139
  • 当一个大型系统在建立时,会发现,很多的SQL操作是有重叠的,个别计算是相同的,比如:业务系统中,计算一张工单的计算...
    JackFrost_fuzhu阅读 3,374评论 0 27
  • 任务需求:定时执行的任务,调用存储过程,进行数据迁移。 存储过程相关总结:(存储过程的创建 不能伴随有if exi...
    时待吾阅读 3,071评论 0 4
  • 存储过程是一组预先编辑好的SQL语句组成,编译后存储在数据库中。 存储过程可包含程序流、逻辑及对数据库的查询。它们...
    肉肉要次肉阅读 4,423评论 0 1