存儲(chǔ)過(guò)程

存儲(chǔ)過(guò)程優(yōu)點(diǎn)

運(yùn)行T-SQL語(yǔ)句進(jìn)行編程有兩種辦法,一種是把T-SQL語(yǔ)句全部寫(xiě)在應(yīng)用程序中,并存儲(chǔ)在本地;另一種是把部分T-SQL語(yǔ)句編寫(xiě)的程序作為存儲(chǔ)過(guò)程存儲(chǔ)在SQL Server中,只有本地的應(yīng)用程序調(diào)用存儲(chǔ)過(guò)程。大多數(shù)程序員偏向使用后者,原因在于存儲(chǔ)過(guò)程具有以下優(yōu)點(diǎn):

  • 一次編譯,多次執(zhí)行。第一次執(zhí)行某個(gè)過(guò)程時(shí),將編譯該過(guò)程以確定檢索數(shù)據(jù)的最優(yōu)訪問(wèn)計(jì)劃。 如果已經(jīng)生成的計(jì)劃仍保留在數(shù)據(jù)庫(kù)引擎計(jì)劃緩存中,則該過(guò)程隨后執(zhí)行的操作可能重新使用該計(jì)劃。
  • 可在應(yīng)用程序中多次調(diào)用;修改存儲(chǔ)過(guò)程不會(huì)影響應(yīng)用程序源代碼。
  • 存儲(chǔ)過(guò)程存儲(chǔ)在服務(wù)中,能夠減少網(wǎng)絡(luò)流量。比如一個(gè)需要數(shù)百行T-SQL代碼的操作可以通過(guò)一條執(zhí)行存儲(chǔ)過(guò)程代碼的語(yǔ)句來(lái)調(diào)用,而不需要在網(wǎng)絡(luò)中發(fā)送數(shù)百行代碼。
  • 存儲(chǔ)過(guò)程可被作為一種安全機(jī)制來(lái)充分利用??梢灾皇谟栌脩魣?zhí)行存儲(chǔ)過(guò)程的權(quán)限,而不授予用戶直接訪問(wèn)存儲(chǔ)過(guò)程中涉及的表的權(quán)限。這樣,用戶只能通過(guò)存儲(chǔ)過(guò)程來(lái)訪問(wèn)表,并進(jìn)行有限的操作,從而保證了表中數(shù)據(jù)的安全。使用授權(quán)操作設(shè)置各個(gè)用戶的權(quán)限。

存儲(chǔ)過(guò)程分類

(1)系統(tǒng)存儲(chǔ)過(guò)程
??SQL Server提供的存儲(chǔ)過(guò)程,用于執(zhí)行與系統(tǒng)相關(guān)的任務(wù),主要存儲(chǔ)在master數(shù)據(jù)庫(kù)并以sp_為前綴,例如sp_addtype、sp_rename等。

(2)擴(kuò)展存儲(chǔ)過(guò)程
??擴(kuò)展存儲(chǔ)過(guò)程是以在SQL Server環(huán)境之外執(zhí)行的動(dòng)態(tài)鏈接庫(kù)(Dymatic-Link)Libraries,DDL)來(lái)實(shí)現(xiàn)的,執(zhí)行系統(tǒng)存儲(chǔ)過(guò)程不能勝任的任務(wù),如發(fā)郵件、文件處理等,通常以前綴xp_開(kāi)頭。執(zhí)行擴(kuò)展存儲(chǔ)過(guò)程的方式與存儲(chǔ)過(guò)程的相似。

(3)臨時(shí)存儲(chǔ)過(guò)程
??臨時(shí)存儲(chǔ)過(guò)程首先是本地存儲(chǔ)過(guò)程。SQL Server支持兩種臨時(shí)存儲(chǔ)過(guò)程:局部臨時(shí)過(guò)程和全局臨時(shí)過(guò)程。
??如果存儲(chǔ)過(guò)程的前面有一個(gè)符號(hào)“#”,那么它就是局部臨時(shí)過(guò)程,只能在一個(gè)用戶會(huì)話中使用,在當(dāng)前會(huì)話結(jié)束時(shí)就會(huì)被除去。
??如果存儲(chǔ)過(guò)程的前面有兩個(gè)符號(hào)“##”,那么把該存儲(chǔ)過(guò)程稱為全局臨時(shí)存儲(chǔ)過(guò)程,可以在所有用戶會(huì)話中使用,在使用該過(guò)程的最后一個(gè)會(huì)話結(jié)束時(shí)除去。

(4)用戶定義的存儲(chǔ)過(guò)程
??用戶自定義的存儲(chǔ)過(guò)程由用戶創(chuàng)建的一組T-SQL語(yǔ)句集合組成,可以接收和返回用戶提供的參數(shù),完成某些特定功能。
??存儲(chǔ)過(guò)程創(chuàng)建好且語(yǔ)法正確后,系統(tǒng)將存儲(chǔ)過(guò)程的名稱存儲(chǔ)在當(dāng)前數(shù)據(jù)庫(kù)的系統(tǒng)表sysobject中;將存儲(chǔ)過(guò)程的文本存儲(chǔ)在當(dāng)前數(shù)據(jù)庫(kù)的系統(tǒng)表syscomments中。

創(chuàng)建存儲(chǔ)過(guò)程

存儲(chǔ)過(guò)程語(yǔ)法如下:

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:該過(guò)程所屬的架構(gòu)的名稱。如果在創(chuàng)建過(guò)程時(shí)未指定架構(gòu)名稱,則自動(dòng)分配正在創(chuàng)建過(guò)程的用戶的默認(rèn)架構(gòu)。
  • 可以通過(guò)使用一個(gè)#符號(hào)在procedure_name之前創(chuàng)建本地臨時(shí)過(guò)程(#procedure_name)或兩個(gè)#符號(hào)創(chuàng)建全局臨時(shí)過(guò)程(## procedure_name) 。局部臨時(shí)程序僅對(duì)創(chuàng)建了它的連接可見(jiàn),并且在關(guān)閉該連接后將被刪除。 全局臨時(shí)程序可用于所有連接,并且在使用該過(guò)程的最后一個(gè)會(huì)話結(jié)束時(shí)將被刪除。
  • @parameter:指定過(guò)程中的參數(shù),是局部的,可以聲明一個(gè)或多個(gè)。
  • 如果指定了FOR REPLICATION,則無(wú)法聲明參數(shù)。
  • parameter可以是輸入?yún)?shù)or輸出參數(shù),若為輸入?yún)?shù)IN可以不寫(xiě),系統(tǒng)默認(rèn);若為輸出參數(shù)則要加上OUTPUT。
  • 表值參數(shù)只能是 INPUT 參數(shù),并且這些參數(shù)必須帶有 READONLY 關(guān)鍵字。
  • 光標(biāo)數(shù)據(jù)類型只能是輸出參數(shù)和必須附帶由 VARYING 關(guān)鍵字。
  • OUT | OUTPUT指示參數(shù)是輸出參數(shù),使用 OUTPUT 參數(shù)將值返回給過(guò)程的調(diào)用方。
  • [ =default ]:參數(shù)的默認(rèn)值。 如果默認(rèn)定義值,該函數(shù)可以執(zhí)行而無(wú)需指定該參數(shù)的值。
  • WITH ENCRYPTION:SQL Server加密syscomments表中包含CREATE PROCEDURE語(yǔ)句文本的條目,即對(duì)用戶隱藏存儲(chǔ)過(guò)程的文本,不能從syscomments表中獲取該存儲(chǔ)過(guò)程的信息。
  • WITH RECOMPILE:指示數(shù)據(jù)庫(kù)引擎不緩存該過(guò)程的計(jì)劃,該過(guò)程將在每次運(yùn)行時(shí)重新編譯。如果指定了FOR REPLICATION,則不能使用此選項(xiàng)。
  • EXECUTE AS子句:指定在其中執(zhí)行過(guò)程的安全上下文。

關(guān)于參數(shù)

  • 存儲(chǔ)過(guò)程參數(shù)也可以帶有默認(rèn)值,如:
create procedure pun_info @pubname varchar(20)='ALGOdata'
  • 存儲(chǔ)過(guò)程參數(shù)可以帶有通配符,如:
create procedure pun_info 
   @name varchar(20)='D%'
as
  select name from authors where name like @name

關(guān)于輸出
①OUTPUT參數(shù)
??如果在過(guò)程定義中為參數(shù)指定 OUTPUT 關(guān)鍵字,則存儲(chǔ)過(guò)程在退出時(shí)可將該參數(shù)的當(dāng)前值返回至調(diào)用程序。若要用變量保存參數(shù)值以便在調(diào)用程序中使用,則調(diào)用程序必須在執(zhí)行存儲(chǔ)過(guò)程時(shí)使用 OUTPUT 關(guān)鍵字。
??也可以在執(zhí)行過(guò)程時(shí)為 OUTPUT 參數(shù)指定輸入值。 這將允許過(guò)程從調(diào)用程序接收值,使用該值更改或執(zhí)行操作,然后將新值返回給調(diào)用程序。
②使用返回代碼返回?cái)?shù)據(jù)
??過(guò)程可以返回一個(gè)整數(shù)值(稱為“返回代碼”),以指示過(guò)程的執(zhí)行狀態(tài)。 使用 RETURN 語(yǔ)句指定過(guò)程的返回代碼。 與 OUTPUT 參數(shù)一樣,執(zhí)行過(guò)程時(shí)必須將返回代碼保存到變量中,才能在調(diào)用程序中使用返回代碼值。
??RETURN是從查詢或過(guò)程中無(wú)條件退出,不執(zhí)行位于 RETURN 之后的語(yǔ)句。RETURN返回的不能是空值,如果過(guò)程試圖返回空值,將生成警告信息并返回 0 值。用輸出參數(shù)OUTPUT可以輸出任意類型的結(jié)果(不包括表類型),而RETURN只能返回整型并且總能返回一個(gè)整型值。一般的RETURN用來(lái)返回返回代碼(如0表示執(zhí)行成功,1表示未指定所需參數(shù)值)。
??RETURN和OUTPUT還可以出現(xiàn)在同一存儲(chǔ)過(guò)程中,詳見(jiàn)示例(3)。

局限與限制

①在單個(gè)批處理中,CREATE PROCEDURE 語(yǔ)句不能與其他 Transact-SQL 語(yǔ)句組合使用。
②以下語(yǔ)句不能用于存儲(chǔ)過(guò)程主體中的任何地方。



③過(guò)程可以引用尚不存在的表。 在創(chuàng)建時(shí),只進(jìn)行語(yǔ)法檢查。 直到第一次執(zhí)行該過(guò)程時(shí)才對(duì)其進(jìn)行編譯。 只有在編譯過(guò)程中才解析過(guò)程中引用的所有對(duì)象。 因此,如果語(yǔ)法正確的過(guò)程引用了不存在的表,則仍可以成功創(chuàng)建;但如果被引用的表不存在,則過(guò)程將在執(zhí)行時(shí)將失敗。
④不能將某一函數(shù)名稱指定為參數(shù)默認(rèn)值或者在執(zhí)行過(guò)程時(shí)傳遞給參數(shù)的值。 但是,您可以將函數(shù)作為變量傳遞,如以下示例中所示:

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

⑤如果該過(guò)程對(duì) SQL Server 的遠(yuǎn)程實(shí)例進(jìn)行更改,將無(wú)法回滾這些更改。 遠(yuǎn)程過(guò)程不參與事務(wù)。

執(zhí)行存儲(chǔ)過(guò)程

調(diào)用存儲(chǔ)過(guò)程使用Execute|Exec關(guān)鍵字,不能省略。

Execute|Exec
{
  [@整形變量=]
  存儲(chǔ)過(guò)程名[,n]|@存儲(chǔ)過(guò)程變量名
  [[@過(guò)程參數(shù)=]參數(shù)值|@可變參數(shù)名 [OUTPUT]|[DEFAULT]]
  [,..,n]
  [WITH RECOMPILE]
}
  • @整形變量:可選,代表存儲(chǔ)過(guò)程的返回狀態(tài)。
  • n:可選,用于對(duì)同名的過(guò)程分組。
  • @過(guò)程參數(shù):為存儲(chǔ)過(guò)程的參數(shù)賦值。

SQL Server提供了兩種傳遞參數(shù)的辦法:
(1)按位置傳遞參數(shù),即傳遞的參數(shù)和定義時(shí)的參數(shù)順序一致,如:
execute au_info 'Dull','Ann'
(2)通過(guò)參數(shù)名傳遞,采用“參數(shù)=值”的形式,此時(shí)各個(gè)參數(shù)可以任意排序,如:
execute au_info @firstName='Dull',@lastName='Ann' 或
execute au_info @lastName='Ann',@firstName='Dull'

  • OUTPUT:指定該參數(shù)為輸出參數(shù)。
  • DEFAULT:指明該參數(shù)使用默認(rèn)值。如果該參數(shù)定義時(shí)沒(méi)有指定默認(rèn)值,則不能使用DEFAULT選項(xiàng)。
  • WITH RECOMPILE:強(qiáng)制在執(zhí)行存儲(chǔ)過(guò)程時(shí)重新對(duì)其進(jìn)行編譯。

【示例】
(1)帶OUTPUT參數(shù)的存儲(chǔ)過(guò)程——最后的返回值存儲(chǔ)在調(diào)用程序聲明的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

調(diào)用過(guò)程如下:

declare @Relationer_name varchar(20),@Cust_ID int
execute Query_Relationer @Cust_ID=20103530,@Relationer_name OUTPUT
print '客戶ID為'+convert(char(8),@Cust_ID)+'的聯(lián)系人是:'+@Relationer_name

(2)帶Return參數(shù)的存儲(chǔ)過(guò)程

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

(3)同時(shí)帶Return和output參數(shù)的存儲(chǔ)過(guò)程

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

調(diào)用過(guò)程如下:

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

存儲(chǔ)過(guò)程傳遞集合參數(shù)以及返回、接收結(jié)果集

(1)傳遞集合參數(shù)

A、傳遞多個(gè)形參

B、使用表值參數(shù)
??使用表值參數(shù)類型將多個(gè)行插入表中。 一下示例將創(chuàng)建參數(shù)類型,聲明表變量來(lái)引用它,填充參數(shù)列表,然后將值傳遞給存儲(chǔ)過(guò)程。 存儲(chǔ)過(guò)程使用這些值將多個(gè)行插入表中。

/* 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)返回結(jié)果集

A、使用 OUTPUT 游標(biāo)參數(shù)
??以下示例使用 OUTPUT 游標(biāo)參數(shù)將過(guò)程的局部游標(biāo)傳遞回執(zhí)行調(diào)用的批處理、過(guò)程或觸發(fā)器。
??首先,創(chuàng)建在 Currency表上聲明并打開(kāi)一個(gè)游標(biāo)的過(guò)程:

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  

接下來(lái),運(yùn)行以下批處理:聲明一個(gè)局部游標(biāo)變量,執(zhí)行上述過(guò)程以將游標(biāo)賦值給局部變量,然后從該游標(biāo)提取行。

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返回多個(gè)輸出參數(shù)
??這種方法缺點(diǎn)在于如果結(jié)果集中幾百個(gè)元素,那么在存儲(chǔ)過(guò)程就要聲明幾百個(gè)變量,十分麻煩。

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

調(diào)用段:

DECLARE @name varchar(20),@age int
EXECUTE Student.singS 28, @name OUTPUT,@age OUTPUT
print '學(xué)生的姓名為:'+@name+',年齡為:'+@age

C、SELECT返回結(jié)果集
??在存儲(chǔ)過(guò)程中寫(xiě)一段返回一個(gè)結(jié)果集的SELECT語(yǔ)句,如果在調(diào)用段中僅僅EXEC procedure_name [parameter1...parametern],那么該SELECT語(yǔ)句的結(jié)果僅僅只會(huì)輸出到屏幕上,而不能用這個(gè)結(jié)果集做后續(xù)處理。如果要保存此結(jié)果集,只有一種方法,即通過(guò)使用 INSERT/EXEC 將其存儲(chǔ)到永久表、臨時(shí)表或表變量中,從而將結(jié)果流式處理到磁盤(pán)。

①把結(jié)果集存儲(chǔ)在臨時(shí)表
創(chuàng)建存儲(chǔ)過(guò)程:

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

調(diào)用段:

-- 創(chuàng)建一個(gè)臨時(shí)表,和存儲(chǔ)過(guò)程的結(jié)果集結(jié)構(gòu)一致
CREATE TABLE #t1
(
  id int,
  name varchar(50)
)

-- 把結(jié)果集插入臨時(shí)表中
INSERT INTO #t1 EXEC Proc1 'Ada'
-- do something with results
--用完之后要把臨時(shí)表清空
DROP TABLE #t1

②把結(jié)果集存儲(chǔ)在表變量
??但這種方法在查詢的數(shù)據(jù)量較大的情況下比較影響性能,查詢速度較慢,在數(shù)據(jù)量較小的情況下這種差異并不明顯。

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

管理存儲(chǔ)過(guò)程

①查看存儲(chǔ)過(guò)程信息


②修改存儲(chǔ)過(guò)程

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 ]

③刪除存儲(chǔ)過(guò)程

DROP { PROC | PROCEDURE } [ IF EXISTS ] { [ schema_name. ] procedure } [ ,...n ]  
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • oracle存儲(chǔ)過(guò)程常用技巧 我們?cè)谶M(jìn)行pl/sql編程時(shí)打交道最多的就是存儲(chǔ)過(guò)程了。存儲(chǔ)過(guò)程的結(jié)構(gòu)是非常的簡(jiǎn)單的...
    dertch閱讀 3,615評(píng)論 1 12
  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,554評(píng)論 19 139
  • 當(dāng)一個(gè)大型系統(tǒng)在建立時(shí),會(huì)發(fā)現(xiàn),很多的SQL操作是有重疊的,個(gè)別計(jì)算是相同的,比如:業(yè)務(wù)系統(tǒng)中,計(jì)算一張工單的計(jì)算...
    JackFrost_fuzhu閱讀 3,516評(píng)論 0 27
  • 任務(wù)需求:定時(shí)執(zhí)行的任務(wù),調(diào)用存儲(chǔ)過(guò)程,進(jìn)行數(shù)據(jù)遷移。 存儲(chǔ)過(guò)程相關(guān)總結(jié):(存儲(chǔ)過(guò)程的創(chuàng)建 不能伴隨有if exi...
    時(shí)待吾閱讀 3,208評(píng)論 0 4
  • 存儲(chǔ)過(guò)程是一組預(yù)先編輯好的SQL語(yǔ)句組成,編譯后存儲(chǔ)在數(shù)據(jù)庫(kù)中。 存儲(chǔ)過(guò)程可包含程序流、邏輯及對(duì)數(shù)據(jù)庫(kù)的查詢。它們...
    肉肉要次肉閱讀 4,575評(píng)論 0 1

友情鏈接更多精彩內(nèi)容