21

21. 建立及管理預存程序

何謂預存程序？

建立預存程序

使用 T-SQL 管理預存程序

本章總結

本章將學習 Microsoft SQL Server 預存程序以及使用方法。首先會介紹預存程序的類型，接著學習建立及管理使用者自訂的預存程序與定義參數及變數。建立預存程序的方法有四種，本章會敘述如何使用 Transact-SQL（T-SQL）、SQL Server Enterprise Manager 和建立預存程序精靈，第四種方法為使用 SQL Distributed Management Objects（SQL-DMO），因為該方法與應用程式設計相關，故在此將不作討論。在操作的過程中可以發現，三種建立預存程序的方法都要利用到 T-SQL 程式碼，本章的 〈使用 CREATE PROCEDURE 陳述式〉 一節中會介紹到 T-SQL 程式碼，請特別留意 T-SQL 程式碼的用法。

何謂預存程序？

預存程序（stored procedure） 是 T-SQL 陳述式的集合，SQL Server 會將該集合中的陳述式編譯成一個執行計劃單位。當預存程序第一次執行時，經過編譯的執行計劃即儲存在記憶體的程序快取區中，並且可以重複使用，這樣 SQL Server 就不必在每一次執行相同的任務時重複分析語法是否正確。預存程序和其他程式語言的程序相似，可以接受傳入的參數、將參數的值傳回，或是傳回成功或失敗狀態的訊息。當程序被呼叫時，所有在程序中的陳述式都會被執行。預存程序會執行某項任務所需用到的 T-SQL 陳述式及相關邏輯。由於預存程式可視為一個執行單位，因此可由不同的使用者，重複地執行在同樣的任務上，甚至可跨多應用程式作業。預存程序也為作業提供了單一控制點，確保執行商業規則的正確性與一致性。

您的應用程式端可以透過兩種方法與 SQL Server 通訊：您可以撰寫應用程式以從用戶端傳送 T-SQL 陳述式到 SQL Server 中，或者自行建立預存程序以在伺服器上儲存及執行。如果您從用戶端應用程式傳送 T-SQL 陳述式到伺服器，陳述式會透過網路傳輸，並在每次執行時進行編譯。如果使用預存程序，您可以藉由一個陳述式，從應用程式中呼叫預存程序並執行該預存程式。如之前所提到的，SQL Server 會對第一次執行的預存程序進行編譯，將此執行計劃儲存在記憶體中。當下一次再呼叫相同的執行計劃，就不需再重複編譯。當一項工作執行時需要多項 T-SQL 陳述式，或某些陳述式經常要執行，使用預存程序就不需要每次執行一項陳述式，就得透過網路從用戶端傳回結果，從而減少網路的流量和系統的負擔。

預存程序也可透過其他方法提高執行效能。舉例來說，透過預存程序可減少用戶端和伺服器端間傳送的資料量，用戶端所需處理的資料量相對減少，因此利用預存程序在伺服器上執行，就是一種提高執行效能的方法。若是在預存程序內部測試條件判斷式，可以在預存程序中應用條件陳述式（如在

第二十章 中討論的 IF 和 WHILE 結構）。這種測試邏輯會透過預存程序在伺服器上執行，無須將邏輯寫入應用程式裡，並且伺服器不必立即傳回結果到用戶端以執行測試條件。您還可以從指令碼、批次處理，或利用 T-SQL 陳述式的互動式命命列來呼叫預存程序（本章稍後會介紹）。

預存程序還為使用者提供了簡單的資料庫存取方式。使用者不必知道資料表的結構詳細資訊就可以存取資料庫，換句話說，他們只要執行所需的任務，而不需要直接進入資料表。預存程序即是以此方式確保商業規則。

預存程序可以接收輸入參數，使用區域變數，以及傳回資料。預存程序透過輸出參數、回傳SELECT陳述式執行的結果、或全域資料指標（global cursor）傳回資料。除了使用全域資料指標，其他的技巧在本章稍後都會有範例。

相關資訊

關於全域資料指標的資料，您可在《線上叢書》的 搜尋 標籤頁中，輸入「指標」這個關鍵字，在 Transact-SQL程式語法參考說明 這個位置，選擇 DECLARE CURSOR ，即可找到連線全域資料指標的相關資料。

預存程序共分三種類型： 系統預存程序 、 延伸預存程序 、 簡易使用者自訂預存程序 。 系統預存程序（system sotred procedure） 由SQL Server所內建，並且有字首sp_，用於管理 SQL Server 和顯示資料庫和使用者資訊，在 第十三章 中作過介紹； 延伸預存程序（extended sotred procedure） 屬於動態連結程式庫（DLLs），SQL Server 可以動態載入與執行，通常是使用 C 或 C++ 語言寫成，包含字首xp_； 簡易使用者自訂預存程序 由使用者自行建立自訂執行使用者指定的任務。

說明

當建立簡易使用者自訂預存程序時，不應該使用sp_當字首，因為當SQL Server 遇到有字首為sp_的預存程序時，會先在 master 資料庫中尋找預存程序。舉例來說，如果在 MyDB 資料庫中，將自訂的簡易使用者預存程序命名為sp_myproc，SQL Server 會先在 master 資料庫中尋找該預存程序（當然是找不到），然後才會去使用者資料庫尋找。因此將程序簡單的命名為myproc會更有效率。

我們會先簡單的介紹延伸預存程序，本章的重點則會放在簡易使用者自訂預存程序。延伸預存程序因為可利用諸如 C、C++ 等程式語言建立自己的外部常式，擴充了 SQL Server 環境的使用性及延展性，執行時和其他兩種類型的預存程序相同，即可以傳送參數給延伸預存程序，也可以傳回結果集和狀態。

如前所述，延伸預存程序屬於動態連結程式庫（DLLs），SQL Server 可以動態載入與執行，並直接在 SQL Sever 位址空間中執行。您可以使用 SQL Sever Open Data Services API 撰寫程式。

延伸預存程序可以在 SQL Sever 以外環境撰寫，當一個延伸預存程序撰寫完成，可利用 T-SQL 命令或利用 Enterprise Manager 註冊該延伸預存程序。

相關資訊

在 SQL Sever《線上叢書》可找到更多延伸預存程序的使用範例。

建立預存程序

本節會介紹建立預存程序的三種方法：T-SQL 的 CREATE PROCEDURE 陳述式、Enterprise Manager 和建立預存程序精靈。

使用 CREATE PROCEDURE 陳述式

CREATE PROCEDURE 陳述式的基本語法如下：

    CREATE PROCEDURE [procedure_name]
                     [{@parameter_name data_type}] [= default][OUTPUT]
                     [,...,n]
    AS t-sql_statement(s)

首先我們建立一個簡單的預存程序，此預存程序會在Orders資料表中的每一資料列，選擇（並傳回）三個資料行中的資料，當ShippedDate資料行中的日期晚於RequiredDate的日期，就會傳回資料。請注意，預存程序只能建立在使用中的資料庫上，所以必須先利用 USE 陳述式來指定資料庫。在建立預存程序前，要先確定是否有重複的名稱存在，我們必須用未存在的名稱命名預存程序，或是先刪除已經存在的名稱後再重新命名。用於建立該程式的 T-SQL 程式碼如下：

    USE Northwind
    GO

    IF EXISTS (SELECT name
               FROM   sysobjects
               WHERE  name = "LateShipments" AND
                      type = "P")
    DROP PROCEDURE LateShipments
    GO

    CREATE PROCEDURE LateShipments
    AS
    SELECT  RequiredDate,
            ShippedDate,
            Shippers.CompanyName
    FROM    Orders, Shippers
    WHERE   ShippedDate     > RequiredDate AND
            Orders.ShipVia  = Shippers.ShipperID
    GO

執行上述的 T-SQL 程式碼就會建立預存程序，要執行該預存程序，只要呼叫其名稱：

    LateShipments
    GO

LateShipments會傳回 37 列資料。

如果呼叫該預存程序的陳述式是一個批次中的陳述式，並且不是該批次操作的第一個陳述式，就必須使用 EXECUTE（可縮寫為 EXEC）關鍵字呼叫，使用範例如下：

    SELECT getdate ( )
    EXECUTE LateShipments
    GO

如果呼叫程序的陳述式為該批次操作的第一個陳述式，或為該批次操作的唯一陳述式，您也可以不使用 EXECUTE 關鍵字。

使用參數

接下來讓我們在這個預存程序中，新增一個輸入參數，以便在呼叫預存程序時，同時也傳遞參數給預存程序。要想在預存程序中指定輸入參數，請以 @ 字首為參數名稱的前置字，例如@parameter_name，一個預存程序可指定高達 1024 個參數。在範例中，將建立@ShipperName的參數。當執行預存程序時，輸入船公司名稱，該查詢就只會傳回該船公司的資料列，其 T-SQL 陳述式如下：

    USE Northwind
    GO

    IF EXISTS (SELECT name
               FROM   sysobjects
               WHERE  name = "LateShipments" AND
                      type = "P")
    DROP PROCEDURE LateShipments
    GO

    CREATE PROCEDURE LateShipments @shipperName char(40)
    AS
    SELECT  RequiredDate,
            ShippedDate,
            Shippers.CompanyName
    FROM    Orders, Shippers
    WHERE   ShippedDate             > RequiredDate AND
            Orders.ShipVia          = Shippers.ShipperID AND
            Shippers.CompanyName    = @shipperName
    GO

執行此預存程序時，必須先提供輸入參數，否則會顯示如下的錯誤訊息：

    伺服器：訊息 201，層級 16，狀態 4，程序 LateShipments，行 0
    程序 'LateShipments' 預期使用未提供的參數 '@shipperName'。

想為 Speedy Express 這家船公司傳回符合要求的資料列，請執行以下陳述式：

    USE Northwind
    GO
    EXECUTE LateShipments "Speedy Express"
    GO

該預存程序將傳回 12 個資料列。

您也可以為參數設定預設值，它將適用於當呼叫預存程序時沒有參數的情況。舉例來說，我們將預存程序的參數預設值設為 United Package，其 T-SQL 預存程序程式碼變更如下（只變更 CREATE PROCEDURE 行）：

    USE Northwind
    GO
    IF EXISTS (SELECT  name
               FROM    sysobjects
               WHERE   name = "LateShipments" AND
                       type = "P")
    DROP PROCEDURE LateShipments
    GO

    CREATE PROCEDURE LateShipments @shipperName char(40) = "United
    Package"
    AS
    SELECT  RequiredDate,
            ShippedDate,
            Shippers.CompanyName
    FROM    Orders, Shippers
    WHERE   ShippedDate          > RequiredDate AND
            Orders.ShipVia       = Shippers.ShipperID AND
            Shippers.CompanyName = @shipperName
    GO

如果在執行LateShipments時沒有提供參數，預存程序將使用United Package為@ShipperName的預設值（並傳回 16 個資料列）。即使預設參數已經確定，仍然可以提供輸入參數，此參數會覆寫預設值。

如果您想在預存程序中傳回值給呼叫程式，請在參數名稱後使用 OUTPUT 關鍵字。要將值儲存在變數中以供呼叫預存程序的應用程式使用，也可以在呼叫該預存程序時，使用 OUTPUT 關鍵字。讓我們實際操作一個範例，首先建立新的預存程序以為某個產品選擇單價，@prod_id為 ProductID 的輸入參數，@unit_price輸出參數將傳回單價，名稱為@price的區域變數將在呼叫程式中被宣告，並用於儲存傳回值。下面是用於建立GetUnitPrice預存程序的程式碼：

    USE Northwind
    GO

    IF EXISTS (SELECT  name
               FROM    sysobjects
               WHERE   name = "GetUnitPrice" AND
                       type = "P")
    DROP PROCEDURE GetUnitPrice
    GO

    CREATE PROCEDURE GetUnitPrice @prod_id int, @unit_price money OUT-
    PUT
    AS
    SELECT  @unit_price = UnitPrice
    FROM    Products
    WHERE   ProductID = @prod_id
    GO

您必須在呼叫程式中宣告變數，才可在預存程序呼叫中使用該變數。舉例來說，在下面的程式碼中我們先宣告@price變數並將其資料型別設為money（它必須符合OUTPUT參數資料型別），然後執行此預存程序：

    DECLARE @price money
    EXECUTE GetUnitPrice 77, @unit_price = @price OUTPUT
    PRINT CONVERT(varchar(6), @price)
    GO

PRINT 陳述式為@price傳回值13.00。請注意我們用 CONVERT 陳述式將@price由原來的money資料型別，轉換為varchar資料型別，才可以將該值當成字串、字元資料型別，或以不直接的方式轉換成字元來列印（這些是 PRINT 陳述式的列印要求）。請注意在預存程序和呼叫程式中為 OUTPUT 使用不同名稱的變數，以使您能夠更方便的找到範例中變數的位置，並強調名稱可以不同。

您也可以在執行預存程序時，使用變數指定輸入值，讓預存程序可以接收來自呼叫程式的數值，然後修改該值或利用該值以執行某種作業，再將新的值傳回呼叫程式。操作方法為在執行預存程序前，先為呼叫程式中的某變數分配一個值（或執行查詢以在變數中插入值），再將該變數傳送到預存程序中。現在就來看看如何在預存程序中使用區域變數。

在預存程序中使用區域變數

DECLARE 關鍵字用於建立區域變數，在建立區域變數時，就要指定區域變數名稱及資料型別，而名稱必須以 @ 字首為前置字。一但變數宣告，其值會先被設為 NULL。

區域變數可在批次操作、指令碼（或呼叫程式）或預存程序中宣告。預存程序中的變數通常用來儲存條件陳述式所測試傳回的資料值，或是儲存預存程序 RETURN 陳述式所傳回的資料值。變數也常被用來當作計數器。變數範圍從變數的宣告處開始，宣告該變數的預存程序結束後，該變數就不再有效。

現在就來看一個包含區域變數的預存程序。該預存程序使用 WHILE 迴圈結構插入五個資料列到資料表中。首先建立一個範例資料表，命名為mytable，然後建立一個預存程序，命名為InsertRows。在程序中將使用@loop_counter和@start_val這兩個區域變數，我們會一起宣告這兩個變數，並且以逗號將兩變數區隔。以下為使用 T-SQL 程式碼建立資料表和預存程序的方法：

    USE MyDB
    GO

    CREATE TABLE mytable
    (
         column1 int,
         column2 char(10)
    )
    GO

    CREATE PROCEDURE InsertRows @start_value int
    AS
    DECLARE @loop_counter int, @start_val int
    SET     @start_val = @start_value - 1
    SET     @loop_counter = 0
    WHILE (@loop_counter < 5)
       BEGIN
          INSERT INTO mytable VALUES (@start_val + 1, "new row")
          PRINT (@start_val)
          SET @start_val = @start_val + 1
          SET @loop_counter = @loop_counter + 1
       END
    GO

現在執行起始值為 1 的預存程序，如下所示：

    EXECUTE InsertRows 1
    GO

執行後會列印五個@start_val值：0、1、2、3和4。使用下面的陳述式從mytable中選擇所有的資料列：

    SELECT *
    FROM mytable
    GO

在執行 SELECT 陳述式後，會出現如下的輸出：

    column1  column2
    -------- ----------
    1        new row
    2        new row
    3        new row
    4        new row
    5        new row

當結束預存程序後，@loop_counter和@start_val就無法再被存取。先使用下面的 T-SQL 陳述式嘗試列印：

    PRINT (@loop_counter)
    PRINT (@start_val)
    GO

就會得到這樣的錯誤訊息：

    伺服器：訊息 137，層級 15，狀態 2，行 1
    必須宣告變數 '@loop_counter'。
    伺服器：訊息 137，層級 15，狀態 1，行 2
    必須宣告變數 '@start_val'。

執行批次陳述式操作時，此變數範圍的規則也適用。一旦宣告關鍵字 GO（表示批次陳述式結束），批次陳述式所宣告的區域變數將不能再被使用，區域變數的範圍僅限該批次陳述式使用。以下的預存程序呼叫能讓您了解這個規則：

    USE Northwind
    GO

    DECLARE @price money
    EXECUTE GetUnitPrice 77, @unit_price = @price OUTPUT
    PRINT CONVERT(varchar(6), @price）
    GO

    PRINT CONVERT(varchar(6), @price)
    GO

第一個 PRINT 陳述式列印了批次操作中的區域變數@price；第二個 PRINT 陳述式企圖在批次操作結束後，列印此區域變數，這時就會回傳如下的錯誤訊息：

    13.00

    伺服器：訊息 137，層級 15，狀態 2，行 1
    必須宣告變數 '@price'。

第一個列印陳述式成功的執行（列印出值 13.00）。

您可能想在預存程序中使用不只一條 T-SQL 陳述式，如 BEGIN TRANSACTION、COMMIT 和 ROLLBACK 這些陳述式，操作的方法是要先將這些陳述式群組成為一個單位的交易，使用的細節請參閱

第十九章 。

使用 RETURN

在任何時刻使用 RETURN 關鍵字都可以無條件退出預存程序以回到呼叫程式，也可用於退出批次操作等等。當 RETURN 執行時，預存程序執行到該點即停止執行，並回到呼叫程式中的下一個陳述式，RETURN 也可傳回整數值。

首先，讓我們看一個使用 RETURN 從預存程序中退出的範例。您將修改之前的GetUnitPrice程序來檢查是否提供輸入值，如果沒有，列印此資訊給使用者並回到呼叫程式。要達成此目的，請將輸入參數預設值定義為 NULL，然後檢查程序中的值是否為 NULL，這表示沒有輸入任何值。以下是這個程序的程式碼：

    USE Northwind
    GO

    IF EXISTS (SELECT  name
               FROM    sysobjects
               WHERE   name = "GetUnitPrice" AND
                       type = "P")
    DROP PROCEDURE GetUnitPrice
    GO

    CREATE PROCEDURE GetUnitPrice @prod_id int = NULL
    AS
    IF @prod_id IS NULL
       BEGIN
          PRINT "Please enter a product ID number"
          RETURN
       END
    ELSE
       BEGIN
          SELECT  UnitPrice
          FROM    Products
          WHERE   ProductID = @prod_id
       END
    GO

現在請不要輸入 ProductID 值來執行GetUnitPrice並看看結果。由於呼叫程序陳述式並非該批次操作中的第一個陳述式，因次必須使用 EXECUTE 陳述式執行預存程序。程式碼如下：

    PRINT "Before procedure"
    EXECUTE GetUnitPrice
    PRINT "After procedure returns from stored procedure"
    GO

輸出結果顯示如下：

    Before procedure
    Please enter a product ID number
    After procedure returns from stored procedure

由第二個 PRINT 陳述式可看出，當預存程序執行 RETURN 時，批次操作在 PRINT 陳述式處開始繼續執行。

現在使用 RETURN 來傳回值到呼叫程式中。傳回的值必須是一個整數，常數或變數皆可。變數必須透過宣告才可在呼叫程式中使用。舉例來說，當輸入參數為單價少於 $100 的產品，傳回值 1，否則傳回 99。

    CREATE PROCEDURE CheckUnitPrice @prod_id int
    AS
    IF (SELECT  UnitPrice
        FROM    Products
        WHERE   ProductID = @prod_id) < 100
        RETURN 1
    ELSE
        RETURN 99
    GO

為了呼叫預存程序並能使用傳回值，在呼叫程式中宣告變數並將該變數設為預存程序的傳回值，（輸入參數使用 ProductID 值 66），程式碼如下：

    DECLARE @return_val int
    EXECUTE @return_val = CheckUnitPrice 66
    IF (@return_val = 1) PRINT 'Unit price is less than $100'
    GO

由於指定的產品單價為 $17，因此傳回值為 1，並列印 Unit price is less than $100。當變數用於保存傳回值時，記住其資料型別必須被宣告為整數，這是 RETURN 陳述式的要求。

使用 SELECT 傳回值

在預存程序中可以使用 SELECT 陳述式傳回資料，您可以從 SELECT 查詢或傳回變數值中傳回結果集。

現在來看看幾個範例。首先建立一個預存程序，命名為PrintUnitPrice，該預存程序會傳回輸入參數所指定的產品單價（透過 ProductID），程式碼如下：

    CREATE PROCEDURE PrintUnitPrice @prod_id int
    AS
    SELECT  ProductID,
            UnitPrice
    FROM    Products
    WHERE   ProductID = @prod_id
    GO

將 66 作為輸入參數值呼叫該程序：

    PrintUnitPrice 66
    GO

結果看起來像這樣：

    ProductID       UnitPrice
    ----------      ------------
            66      17.0000
    (影響 1 個資料列)

要使用 SELECT 陳述式傳回變數值，請在陳述式後面接著輸入變數名稱。在下面的範例中，我們會重建CheckUnitPrice預存程序，傳回一變數值，並指定輸出標題：

    USE Northwind
    GO

    IF EXISTS (SELECT  name
               FROM    sysobjects
               WHERE   name = "CheckUnitPrice" AND
                       type = "P")
    DROP PROCEDURE CheckUnitPrice
    GO

    CREATE PROCEDURE CheckUnitPrice @prod_id INT
    AS
    DECLARE @var1 int
    IF (SELECT  UnitPrice
        FROM    Products
        WHERE   ProductID = @prod_id) > 100
        SET     @var1 = 1
    ELSE
        SET     @var1 = 99
    SELECT "Variable 1" = @var1
    PRINT "Can add more TSQL statements here"
    GO

將 66 作為輸入參數值呼叫該程序：

    CheckUnitPrice 66
    GO

執行該預存程序的輸出結果如下：

    Variable    1
    -------------
               99

    (影響 1 個資料列)

    Can add more T-SQL statements here

我們列印 Can add more T-SQL statements here 以強調使用 SELECT 傳回值與使用 RETURN 傳回值之間的差別。當呼叫 RETURN 時，預存程序跟著結束；當呼叫 SELECT 時，預存程序則在 SELECT 傳回結果集後，繼續執行。

在前面的範例中，如果沒有指定輸出標題，只使用SELECT @varl，輸出結果將沒有標題，如下所示：

    (沒有資料行名稱)
    ---------------
    99

    (影響 1 個資料列)

使用 Enterprise Manager

現在讓我們看看如何使用 Enterprise Manager 建立預存程序。要使用 Enterprise Manager 建立預存程序，仍然必須知道如何編寫 T-SQL 陳述式，Enterprise Manager 只提供圖形介面以供建立程序。現在就依照下面步驟重建InsertRows預存程序。

1. 在 Enterprise Manager 展開左側視窗中 MyDB 資料庫，選擇 預存程序 資料夾以刪除預存程序，所有的預存程序都顯示在右側窗格，在InsertRows預存程序（本章稍早已建立，所以應該已經存在）上右點滑鼠，然後從快顯功能表中選擇 刪除 （也可以從快顯功能表重新命名或複製該預存程序），會出現如圖 21-1 所示的 卸除物件 對話方塊，按 全部卸除 以刪除預存程序。

   圖21-1　「卸除物件」對話方塊

- 在 預存程序 上右點滑鼠，從快顯功能表中選擇 新增預存程序 ，出現的視窗如圖 21-2 所示。

圖21-2　「預存程序屬性」視窗

- 在 一般 標籤頁的 文字 方塊中，使用預存程序的名稱來取代 [OWNER].[PROCEDURE NAME]，在本例中，即為InsertRows，然後為預存程序輸入 T-SQL。圖 21-3 顯示了使用 T-SQL 程式碼新增InsertRows後的預存程序屬性視窗。

圖21-3　用 T-SQL 程式碼增加新的預存程序

- 按一下 檢查語法 按鈕，讓 SQL Server 指出預存程序中的 T-SQL 語法錯誤，修正找到的語法錯誤，並反覆按 語法檢查 直到語法檢查成功（如圖 21-4 所示），按一下 確定 。

圖21-4　顯示語法檢查成功視窗

- 在 預存程序屬性 對話方塊中按 確定 以建立您的預存程序，建立完後，返回 Enterprise Manager，在左側窗格中選擇 預存程序 資料夾，便可在右側窗格中看到新建立的預存程序，如圖21-5所示。

圖21-5　在 Enterprise Manage 新建的的預存程序

- 要為新的預存程序指派使用者的執行權限，請在右邊窗格的預存程序名稱上右點滑鼠，在快顯功能表中選擇 屬性 ，出現 預存程序屬性 視窗後，按一下 權限 呼叫 物件屬性 視窗（如圖 21-6 所示），在 EXEC 核取方塊中選擇是否允許該使用者或資料庫角色執行此預存程序。本範例中，我們將InsertRows預存程序的使用權限開放給三個使用者。

圖21-6　「物件屬性」視窗的「權限」標籤頁

- 按一下 套用／確定 以回到 預存程序屬性 視窗，按一下 確定 結束。

您可以編輯已經存在的預存程序，方法為：在程序名稱上按右鈕，從快顯功能表中選擇 屬性 ，在 預存程序屬性 視窗中編輯程序（同圖 21-3 的對話方塊），使用 檢查語法 按鈕檢查語法，確定正確無誤後，按一下 套用 ，然後按一下 確定 。

您還可以利用 Enterprise Manager 管理已經存在的預存程序權限，方法為：在 Enterprise Manager 中的預存程序名稱上右點滑鼠，從快顯功能表中選擇 所有工作 ，然後選擇 管理使用權限 。在 所有工作 中，還可以選擇對該預存程序 建立新的發行集 （將在本書

第二十六章 中討論）、產生 SQL 指令碼以及顯示相依性。假設選擇了 產生 SQL 指令碼 ，SQL Server 會自動建立一個指令碼檔案（由您指定指令碼檔案名稱），該指令碼會包括預存程序的定義。當您想重新建立一個預存程序，就可以使用該指令檔。

使用建立預存程序精靈

第三種方法是使用 建立預存程序精靈 ，該精靈提供了建立預存程序所需撰寫的 T-SQL 程式碼基本結構，可以幫助您插入、刪除或更新資料列至資料表，但無法執行從資料表中尋找資料列的程序。

精靈允許在一個資料庫中建立多個預存程序，而不需退出精靈和重新啟動。但是，若要在另一個資料庫中建立預存程序，就必須再次執行精靈。執行精靈時，請依照下面的步驟：

1. 在 Enterprise Manager，從工具列中選擇 工具／精靈 以呼叫 選擇精靈 對話方塊，在 資料庫 中選擇 建立預存程序精靈 ，如圖 21-7 所示。

   圖21-7　「選擇精靈」對話方塊

- 選擇 確定 叫出 建立預存程序精靈 的畫面，如圖 21-8 所示。

圖21-8　「建立預存程序精靈」歡迎畫面

- 選擇 下一步 顯示 選取資料庫 畫面，在 資料庫名稱 選擇欲建立預存程序的資料庫。
- 選擇 下一步 進入 選取預存程序 畫面（如圖 21-9 所示），在這個視窗中，可以看到在這個資料庫所建立的所有資料表名稱，並有三個包含核取方塊的資料行，分別代表使用精靈所能建立的三種預存程序：插入、刪除及更新資料，請選擇合適的核取方塊。

圖21-9　「選擇預存程序」畫面

這個範例顯示了本書所使用的兩個資料表，其中Bicycle_Inventory資料表被指派了兩個預存程序：一個插入程序和一個更新程序。如後續的步驟所示，在實際建立這些程序之前，您可以加以修改。

說明

當然，一個預存程序可以執行多種類型的資料修改，但是 建立預存程序精靈 視每一個修改類型為獨立的預存程序。您可以將精靈所建立的任何預存程序，用 T-SQL 程式碼變更。

- 按 下一步 顯示 完成建立預存程序精靈 ，如圖 21-10 所示，這個畫面列出所建的預存程序精靈名稱及描述。

圖21-10　「完成建立預存程序精靈」畫面

- 欲重新命名或編輯預存程序，在 完成建立預存程序精靈 畫面中按一下 編輯 ，進入 編輯預存程序屬性 視窗，如圖 21-11 所示，這個畫面列出此預存程序會影響的資料行，在 選取 資料行中勾選的資料行名稱將會被預存程式所使用。

圖21-11　「編輯預存程序屬性」視窗

這個範例顯示中在Bicycle_Inventory資料表中，有六個資料行會受到插入預存程序所影響，此插入預存程序已命名為insert_Bicycle_Inventory_1，每個資料行在 選取 欄裡都有勾選核取方塊，代表六個資料行的值在預存程序執行時都需要輸入，且六個資料行都會被預存程序插入值至該資料表中。

- 要重新命名預存程序，請清除 名稱 編輯方塊中的名稱，並用新的名稱替代。
- 要編輯預存程序，請按一下 編輯 SQL 來顯示 編輯預存程序 SQL 對話方塊，如圖 21-12 所示，在這個對話方塊中，可以檢視預存程序的 T-SQL 程式碼，這裡的 T-SQL 非常的基本。在這個範例中，當呼叫預存程序時，這裡所列出的五個參數將會插入成為新資料列的值。要編輯該程式碼，只要在文字方塊中輸入修改設定。完成編輯後，按一下 剖析 檢查語法錯誤，修正錯誤，然後按一下 確定 返回 完成建立預存程序精靈 畫面。

  圖21-12　「編輯預存程序 SQL」對話方塊

- 確定所有預存程序的程式碼無誤後，按一下 完成 建立預存程序。建立完成後，別忘了為每個預存程序設定權限。（請參閱本章稍早用於介紹設定權限的 〈使用 Enterprise Manager 建立預存程序〉 一節。）

就某些方面來說，精靈並不見得非常有用。所以如果知道如何編寫 T-SQL，就可以使用指令碼或 Enterprise Manager 來建立預存程序。

使用 T-SQL 管理預存程序

現在來看看如何使用 T-SQL 命令來更改、刪除和檢視預存程序內容。

ALTER PROCEDURE 陳述式

ALTER PROCEDURE T-SQL 陳述式用於改變先前由 CREATE PROCEDURE 所建立的預存程序。使用 ALTER PROCEDURE 改變預存程序，不會變更原來預存程序所設的權限，也不會影響任何相依的預存程序或觸發程序（相依程序或觸發程序是呼叫預存程序的程序）。

ALTER PROCEDURE 陳述式的語法和 CREATE PROCEDURE 類似：

    ALTER PROC[EDURE] procedure_name
                      [{@parameter_name data_type}] [=default] [OUTPUT]
                      [,...,n]
    AStsql_statement(s)

在 ALTER PROCEDURE 陳述式中，您必須重寫整個預存程序，以達成需要的變更。舉例來說，讓我們重建前面例子的預存程序GetUnitPrice，並將預存程序更改為檢查大於 $100 的單價，如下所示：

    USE Northwind
    GO

    IF EXISTS (SELECT  name
               FROM    sysobjects
               WHERE   name = "GetUnitPrice" AND
                       type = "P")
    DROP PROCEDURE GetUnitPrice
    GO

    CREATE PROCEDURE GetUnitPrice   @prod_id        int,
                                    @unit_price     money OUTPUT
    AS
    SELECT  @unit_price = UnitPrice
    FROM    Products
    WHERE   ProductID = @prod_id
    GO

    ALTER PROCEDURE GetUnitPrice    @prod_id        int,
                                    @unit_price     money OUTPUT
    AS
    SELECT  @unit_price = UnitPrice
    FROM    Products
    WHERE   ProductID = @prod_id AND
            UnitPrice > 100
    GO

現在使用下列的陳述式授權使用者 DickB 該預存程序的執行權限。

    GRANT EXECUTE ON GetUnitPrice TO DickB
    GO

如前所述，當變更預存程序時，不會變更原預存程序的權限。現在我們更改該預存程序，選擇以UnitPrice大於 $200 的資料列，代替大於 $100 的資料列，程式如下：

    ALTER PROCEDURE GetUnitPrice    @prod_id        int,
                                    @unit_price     money OUTPUT
    AS
    SELECT  @unit_price = UnitPrice
    FROM    Products
    WHERE   ProductID = @prod_id AND
            UnitPrice > 200
    GO

執行 ALTER PROCEDURE 陳述式後，使用者 DickB 仍保留對該預存程序的執行權限。

DROP PROCEDURE 陳述式

DROP PROCEDURE T-SQL陳述式簡單的說，就是用以刪除已經存在的預存程序；預存程序在刪除後不能被復原，如果要重新建立已經刪除的預存程序，就要使用 CREATE PROCEDURE 陳述式重建。刪除預存程序將遺失所有權限設定，重新建立之後必須重新授權。下面是使用 DROP PROCEDURE 來刪除GetUnitPrice預存程序的範例：

    USE Northwind
    GO

    DROP PROCEDURE GetUnitPrice
    GO

說明

要刪除預存程序，必須使用該預存程序所屬的資料庫。請記住當您使用資料庫時，使用 USE 陳述式並且輸入資料庫名稱即可。

sp_helptext預存程序

sp_helptext系統預存程序讓您檢視預存程序定義，以及建立預存程序的陳述式（也可用於列印觸發程序、檢視表、規則或預設值的定義）。當您想要快速使用 ISQL、OSQL 或 SQL Query Analyzer 呼叫出程序的定義時，sp_helptext就很有用。您也可以將輸出指定到一個檔案內，當需要編輯或重建預存程序時，就可叫出檔案直接修改。要使用sp_helptext，必須利用使用者自訂預存程序（或其他物件名稱）的名稱作為參數。舉例來說，要檢視之前建立InsertRows預存程序的陳述式，請使用以下命令：

    USE MyDB
    GO
    sp_helptext InsertRows
    GO

輸出如下：

    Text
    ------------------------------------------------
    CREATE PROCEDURE InsertRows @start_value int
    AS
    DECLARE @loop_counter   int,
            @start_val      int
    SET     @start_val = @start_value - 1
    SET     @loop_counter = 0
    WHILE (@loop_counter < 5)
       BEGIN
          INSERT INTO mytable VALUES (@start_val + 1, 'new row')
          PRINT (@start_val)
          SET @start_val  = @start_val + 1
          SET @loop_counter       = @loop_counter + 1
       END

本章總結

在本章中，您學到了系統預存程序與使用者自訂預存程序的使用方式，以及如何使用 T-SQL 程式碼、Enterprise Manager 和建立預存程序精靈，建立使用者自訂的預存程序；此外，您還學到如何使用參數和變數，以及如何執行預存程序，並看到了用於更改、刪除和檢視預存程序定義的 T-SQL 陳述式。

第二十二章 將學習觸發程序，觸發程序是一種特殊的預存程序，可在特定條件下自動執行。