189.存儲過程和觸發器

第9章存儲過程和觸發器sql

•    9.1 存儲過程

 

9.1.1 存儲過程的概念

u 存儲過程是指封裝了可重用代碼的、存儲在服務器上的程序模塊或例程。存儲過程是數據庫對象之一,它相似於其餘高級編程語言中的過程或子程序,編譯成可執行代碼後保存在服務器上,可屢次調用。數據庫

 

u 其特色體如今:編程

Ø 能夠接受多個輸入參數,可以以多輸出參數的格式返回多個值。安全

Ø 在服務器端運行,使用EXECUTE(簡寫爲EXEC)語句來執行。服務器

Ø 能夠調用其餘存儲過程,也能夠被其餘語句或存儲過程調用,但不能直接在表達式中使用。網絡

Ø 具備返回狀態值,代表被調用是成功仍是失敗。但不返回取代其名稱的值,這是它與函數的不一樣之處。架構

Ø 存儲過程已在服務器註冊。編程語言

 

 

存儲過程的優勢主要體如今:編輯器

Ø 提升程序的執行效率。存儲過程執行在第一次被執行之後,其執行規劃就駐留在高速緩衝存儲器中。在之後的每次操做中,只需從高速緩衝存儲器中調用已編譯好的二進制代碼執行便可,而沒必要從新編譯再執行,從而提升了執行效率。模塊化

Ø 具備較高的安全特性。做爲一種數據庫對象,存儲過程要求擁有相應權限的用戶才能執行它。同時,它也提供了一種更爲靈活的安全性管理機制:用戶能夠被授予權限來執行存儲過程,而沒必要對存儲過程當中引用的對象擁有訪問權限。

Ø  若是一個存儲過程是用於更新某一個數據表的,那麼只要用戶擁有執行該存儲過程的權限,他就能夠經過執行該存儲過程的方法來實現對指定數據表的更新操做,而沒必要直接擁有對該數據表操做的權限。

Ø 減小網絡通訊流量。因爲存儲過程在服務器端執行,用戶每次只需發出一條執行命令,而沒必要發出存儲過程全部的冗長代碼,於是減小了網絡的數據流量。

Ø 容許模塊化程序設計,提升代碼的可重用性。存儲過程一旦被建立,之後就能夠在全部程序中屢次調用。這有利於程序的結構化設計,提升程序的可維護性和代碼的可重用性。

 

 

9.1.2 存儲過程的類型

Ø 在SQL Server 2008中,存儲過程能夠分爲兩種類型:SQL存儲過程和CLR存儲過程。SQL存儲過程是指出由SQL語言編寫而造成的存儲過程,它是SQL語句的集合。CLR(Common Language Runtime)存儲過程是指引用Microsoft.NET Framework公共語言運行時(CLR)方法的存儲過程,它在.NET Framework程序集中是做爲類的公共靜態方法實現的。

 

Ø 目前常使用的是SQL存儲過程,因此本書要介紹的也就是這類存儲過程。

 

Ø 根據來源和應用目的的不一樣,又能夠將存儲過程分爲用戶存儲過程、系統存儲過程和擴展存儲過程。

 

 

1. 系統存儲過程

ü 系統存儲過程是SQL Server 2008自己定義的、看成命令來執行的一類存儲過程。它主要用於管理SQL Server 數據庫和顯示有關數據庫及用戶的信息,一般前綴「sp_」。

Ø  sp_addrolemember就是一個用於爲數據庫角色添加成員的系統存儲過程。從邏輯結構看,系統存儲過程出如今每一個系統定義數據庫和用戶定義數據庫的sys構架中。讀者最好可以熟悉一些經常使用的系統存儲過程,以避免重複開發。

 

2. 用戶存儲過程

ü 用戶存儲過程是指由用戶經過利用SQL語言編寫的、具備特定功能的一類存儲過程。因爲系統存儲過程以「sp_」爲前綴,擴展存儲過程以「xp_」,因此用戶存儲過程在定義時最好不要使用「sp_」或「xp_」爲前綴。若是須要,用戶存儲過程應以「up_」爲前綴,「u」是單詞user的頭字母。

ü 本章將主要介紹用戶存儲過程的定義、修改和刪除等基本管理操做。

 

 

3. 擴展存儲過程

ü 擴展存儲過程是指SQL Server的實例能夠動態加載和運行的動態連接庫(DLL)。經過擴展存儲過程,可使用其餘編程語言(如C語句)建立本身的外部程序,實現了SQL程序與其餘語言程序的鏈接與融合。

ü 擴展存儲過程直接在SQL Server的實例地址空間中運行,可使用SQL Server擴展存儲過程API完成編程。但因爲後續的SQL Server版本中將不支持擴展存儲過程,因此在新的工程開發項目中應儘可能少用或不用這種功能。

 

 

9.1.3 存儲過程的建立和調用

存儲過程是由CREATE PROCEDURE語句來建立,其語法以下:

CREATE { PROC | PROCEDURE } [schema_name.] procedure_name [ ; number ]
  [ { @parameter [ type_schema_name. ] data_type }
      [ VARYING ] [ = default ] [ [ OUT [ PUT ] ] [ ,...n ]
[ WITH <procedure_option> [ ,...n ]
[ FOR REPLICATION ]
AS { <sql_statement> [;][ ...n ] | <method_specifier> }[;]

<procedure_option> ::=
    [ ENCRYPTION ]
    [ RECOMPILE ]
    [ EXECUTE_AS_Clause ]

<sql_statement> ::= { [ BEGIN ] statements [ END ] }

<method_specifier> ::= EXTERNAL NAME assembly_name.class_name.method_name

 

 

對涉及的參數說明以下:

ü schema_name:設定存儲過程所屬架構的名稱。

ü procedure_name:存儲過程的名稱。它是一個合法的標識符,在架構中是惟一的。存儲過程名通常不能使用前綴「sp_」,此前綴由系統存儲過程使用。若是過程名以井號「#」開頭,則表示建立的過程將局部臨時過程,這種過程名的長度不超116個字符(含#);若是以雙井號「##」開頭,則表示是全局臨時過程,這種過程名的長度不超128個字符(含##)。

ü number:用於對同名的存儲過程進行分組的整數。例如,myPro;一、myPro;2等。

ü @parameter:存儲過程帶的參數,data_type爲參數所屬架構的數據類型。參數能夠是一個或者多個,最多爲2,100個參數。在定義時參數能夠設置默認值,而對於沒有設置默認值的參數,在調用時必須爲其提供值。在默認狀況下,參數只能表明常量表達式,而不能用於表明表名、列名或其餘數據庫對象的名稱。若是指定了FOR REPLICATION,則沒法聲明參數。

ü  OUTPUT(或OUT):若是指定了OUTPUT(或OUT),則表示該參數爲輸出參數。輸出參數用於將存儲過程處理後的某些結果返回給調用它的語句。遊標(cursor)數據類型參數必須指定OUTPUT,同時還必須指定關鍵字VARYING。通常狀況下,text、ntext和image類型參數不能用做OUTPUT 參數。

ü  VARYING:指定輸出參數支持的結果集。僅適用於遊標類型參數。

ü  default:設定參數的默認值。若是定義了default值,則在調用存儲過程時無需爲此參數指定值,不然必須指定參數值才能調用。默認值必須是常量或NULL。

ü  RECOMPILE:該選項用於指示SQL Server不要將存儲過程的執行規劃保存在高速緩衝存儲器中,由於該過程在執行時要從新編譯,而後才運行。若是指定了FOR REPLICATION,則不能使用此選項。

ü  ENCRYPTION:指示SQL Server對CREATE PROCEDURE語句的原始文本進行加密,加密後的代碼的輸出在SQL Server 2008的任何目錄視圖中都不能直接顯示。

ü  EXECUTE AS:該子句用於指定在其中執行存儲過程的安全上下文。

ü  FOR REPLICATION:若是選擇該選項,則表示建立的存儲過程只能在複製過程當中執行。該類過程不能聲明參數,忽略RECOMPILE選項。

ü  <sql_statement>:表示包含在過程當中的一個或多個SQL語句。

ü  <method_specifier>:CLR存儲過程的標識。assembly_name.class_name.method_name用於指定.NET Framework程序集的方法,以便CLR存儲過程引用。

 

【例9.1】(簡單的存儲過程)建立一個存儲過程,它能夠輸出學生的學號、姓名、平均成績以及所在系別。

該過程名爲「myPro1」,所使用的SQL語句以下:

USE MyDatabase;     -- 設置當前數據庫

GO

IF OBJECT_ID('myPro1','P') IS NOT NULL -- 判斷是否已存在名爲「myPro1」存儲過程

    DROP PROCEDURE myPro1;    -- 若是存在則刪除,不然沒法建立(不是必備代碼)

GO

CREATE PROCEDURE myPro1   -- 定義存儲過程myPro1

AS

    SELECT s_no, s_name, s_avgrade, s_dept

    FROM student;

GO 

 

    在SQL Server Management Studio中編寫上述代碼,而後運行此代碼便可在服務器端生成存儲過程myPro1,此後就能夠調用此存儲過程。

 

 

Ø  調用一個存儲過程,通常是用EXECUTE(或EXEC)語句來完成。但也能夠直接將過程名看成一個條命令來執行。

【例子】對於上面定義的過程myPro1,如下三種執行方式都是有效且等價的:

 

•    myPro1;             -- 這種沒有EXECUTE或EXEC的執行方式必須位於批處理中的第一條語句
EXEC myPro1;        -- 這種格式一般用於嵌入到其餘語言中
EXECUTE myPro1;     -- 這種格式一般用於嵌入到其餘語言中

 

 

 

 

 

 

 【例9.2】(帶參數的存儲過程)對於例9.1,進一步要求可以按照成績段來查詢學生的相關信息。知足本例要求的存儲過程須要帶參數,用於界定成績段。該存儲過程定義的代碼以下:

USE MyDatabase;

GO

CREATE PROCEDURE myPro2    -- 定義帶兩個參數的存儲過程

    @mingrade numeric(3,1) = 60,     -- 參數@mingrade的默認值爲60

    @maxgrade numeric(3,1)    -- 參數@maxgrade沒有設置默認值

AS

    -- 查詢平均成績在@mingrade到@maxgrade之間的學生信息

    SELECT s_no, s_name, s_avgrade, s_dept 

    FROM student

    WHERE s_avgrade>= @mingrade AND s_avgrade <= @maxgrade; 

GO

 

 

Ø  上述存儲過程帶有兩個參數,因此調用該過程時必須爲之指定相應的參數值。對於有默認值的參數,若是不指定參數值,則使用默認值,但調用格式要正確。例如,對於存儲過程myPro2,可經過執行下列語句來查詢平均成績在60到90分之間的學生信息(它們都是等價的):

EXEC myPro2 60, 90;

EXEC myPro2 @mingrade = 60, @maxgrade = 90;

EXEC myPro2 @maxgrade = 90, @mingrade = 60;

EXEC myPro2 @maxgrade = 90; -- 參數@mingrade使用默認值60

 

 

      但若是試圖使用下列方式來執行過程myPro2,則是錯誤的或與題意相背:

 

EXEC myPro2 90;       -- 錯誤的調用格式,少了一個參數

EXEC myPro2 90, 60;  -- 能成功調用,但與題意相背

 

 

 

 

 

 

【例9.3】(帶通配符參數的存儲過程)建立一個存儲過程,使之可以按照姓名模糊查詢並列出學生的學號、姓名和平均成績;若是在調用時不帶參數,則列出全部學生的相關信息。

該存儲過程使用帶通配符的方法來實現,其代碼以下:

 

USE MyDatabase;

GO

CREATE PROCEDURE myPro3          

    @s_name varchar(8) = '%'

AS 

    SELECT s_no, s_name, s_avgrade, s_dept 

    FROM student

    WHERE s_name LIKE @s_name; 

GO

 

 

 

 

Ø 調用該過程時,若是帶參數值則按姓名進行模糊查詢,若是不帶參數值則列出全部學生的相關信息。

 【例子】下列語句將列出全部的姓「王」的學生信息:

EXEC myPro3 '王%';  

 

Ø 而執行下列語句後則列出全部學生的學號、姓名和平均成績:

EXEC myPro3;

 

 

    【例9.4】(帶OUTPUT參數的存儲過程)建立一個存儲過程,使之可以求出全部學生成績的總和以及女學生成績的總和。

      OUTPUT參數能夠從存儲過程當中「帶回」返回值,所以利用OUTPUT參數可讓存儲過程具備返回值功能。

     本例中的存儲過程要求有兩個返回結果,所以在定義存儲過程時須要聲明帶兩個OUTPUT參數。

       定義該過程的代碼以下:

 

USE MyDatabase;

GO

CREATE PROCEDURE myPro4      

    @ s_total real OUTPUT,                                  --聲明OUTPUT參數

    @ s_total _female real OUTPUT,           --聲明OUTPUT參數

AS

    SELECT @ s_total =SUM(s_avgrade)                     --求全部學生成績總和

    FROM student;

    SELECT @ s_total _female =SUM(s_avgrade)              --求女學生成績總和

    FROM student

   WHERE s_sex=‘女’

GO

 

 

 

 

Ø  對於帶OUTPUT參數的存儲過程,其調用方法與其餘的存儲過程的調用方法有所不一樣。首先要聲明相應的變量來存放返回結果,而後在調用過程的時候要帶關鍵字OUTPUT,不然沒法將返回結果保存下來。

        【例子】要獲取存儲過程myPro4返回的結果並打印出來,相應的代碼以下:

 

        DECLARE @total real, @total_female real;

        EXEC myPro4 @total OUTPUT, @total_female OUTPUT;  -- 調用時要帶關鍵字OUTPUT

        print @total;

        print @total_female;

 

 

 

 

【例9.5】(加密存儲過程)建立一個加密的存儲過程。

Ø 加密存儲過程是指在存儲過程被建立後對保存在服務器端的過程文本代碼進行加密,從而沒法使用文本編輯器來查看代碼。

Ø 加密存儲過程的方法很簡單,只要在定義時使用WITH ENCRYPTION子句便可。如下是一個加密存儲過程的定義代碼:

USE MyDatabase;        

GO

CREATE PRO CEDURE myPro5   WITH ENCRYPTION          

AS

    SELECT s_no, s_name, s_avgrade, s_dept

    FROM student;

GO

 

Ø 一個存儲過程的定義文本能夠用系統存儲過程sp_helptext來查看。但執行下列語句後,會顯示對象已加密的信息,這表示myPro5的定義文本已經被加密:

EXEC sp_helptext myPro5;

 

 

 

9.1.4 存儲過程的修改和刪除

1. 修改存儲過程

Ø  存儲過程的修改可用ALTER PROCEDURE語句來實現,修改後用戶對該存儲過程擁有的權限並無發生改變。

Ø ALTER PROCEDURE語句的語法以下:

ALTER { PROC | PROCEDURE } [schema_name.] procedure_name [ ; number ]
    [ { @parameter [ type_schema_name. ] data_type }
    [ VARYING ] [ = default ] [ [ OUT [ PUT ] ] [ ,...n ]
[ WITH <procedure_option> [ ,...n ] ]
[ FOR REPLICATION ]
AS
     { <sql_statement> [ ...n ] | <method_specifier> }
<procedure_option> ::=
    [ ENCRYPTION ]
    [ RECOMPILE ]
    [ EXECUTE_AS_Clause ]
<sql_statement> ::= { [ BEGIN ] statements [ END ] }
<method_specifier> ::= EXTERNAL NAME assembly_name.class_name.method_name

 

 注意:若是原來的存儲過程在定義時使用了WITH ENCRYPTION或WITH RECOMPILE選項,那麼只有在ALTER PROCEDURE語句中也選擇了這些選項,這些選項纔有效;另外,使用ALTER PROCEDURE修改後,原過程的權限和屬性將保持不變。

 

 

Ø  【例9.6】  對例9.3建立的存儲過程myPro3進行修改,使之可以按照姓名(s_name)或系別(s_dept)進行查詢。

該修改操做可用下列的ALTER PROCEDURE來實現。

ALTER PROCEDURE myPro3                

    @s_name varchar(8) = '趙%',

    @s_dept varchar(50) = '%'

AS 

    SELECT s_no, s_name, s_avgrade, s_dept 

    FROM student

    WHERE s_name LIKE @s_name OR s_dept LIKE @s_dept;

GO

 

       修改後的過程與原來過程的權限徹底同樣。不一樣的是它除了能夠按姓名查詢外,還能夠按系別查詢。

Ø  在SSMS中修改存儲過程的方法是,在對象資源管理中右擊要修改的存儲過程所對應的節點,並在彈出的菜單中選擇「修改」命令,而後在打開的查詢編輯器窗口中修改過程的定義代碼便可。但對加密存儲過程,則沒法用這種方法修改。

 

 

2. 刪除存儲過程

Ø 當一個存儲過程再也不使用時,就應該將它從數據庫中刪除。刪除一個存儲過程的SQL語句是DROP PROCEDURE。實際上,在前面介紹的例子中已經屢次用到。其語法以下:

DROP { PROC | PROCEDURE } { [ schema_name. ] procedure } [ ,...n ]

 

     從該語法中能夠看出,一條DROP PROCEDURE語句能夠同時刪除一個或多個存儲過程。

 【例子】同時刪除過程myPro1, myPro2, myPro3,可以使用下列語句來完成:

DROP PROCEDURE myPro1, myPro2, myPro3;

 

 

Ø 也能夠在SSMS中刪除一個存儲過程。方法是:在對象資源管理器中右擊要刪除的存儲過程所對應的節點,而後在彈出的菜單中選擇「刪除」命令,最後根據提示刪除存儲過程。

Ø 注意,當一個存儲過程被刪除之後,全部用戶對其擁有的操做權限也將所有被刪除。

 

•    9.2 觸發器

 

9.2.1 關於觸發器

Ø 觸發器是數據庫服務器中發生事件時自動執行的一種特殊的存儲過程。與通常存儲過程不一樣的是,觸發器不是被調用執行,而是在相應的事件發生時激發執行的,而且不能傳遞參數和接受參數。它與數據表關係密切,通常用於實現比較複雜的數據完整性規則、檢查數據的有效性、實現對用戶操做和數據狀態的實時監控、實現數據庫的一些管理任務和其餘的一些附加功能等。

 

Ø 觸發器執行的前提是要有相應事件的發生,這些事件主要是針對數據表。在SQL語言中,引起事件的語句主要是DML和DDL語句,所以又有DML事件和DDL事件、以及DML觸發器和DDL觸發器之稱。另外,自從SQL Server 2005開始,SQL Server增長了一類新的觸發器——LOGON觸發器(登陸觸發器)。利用登陸觸發器能夠實現對登陸用戶的鎖定、限制和跟蹤等。

 

 

1. DML觸發器

Ø 數據庫操縱語言(DML)主要包含INSERT、UPDATE、DELETE等語句。這些語句做用於數據表或視圖的時候,將產生相應的事件——DML事件。此類事件一旦發生可引發相關觸發器的執行,所以這類事件一般稱爲DML事件,相應的觸發器稱爲DML觸發器。也能夠這樣理解,DML觸發器是在運行DML語句時因爲產生DML事件而被執行的一類觸發器。

 

Ø 根據觸發器的執行與觸發事件發生的前後關係,又能夠將DML觸發器分爲AFTER觸發器和INSTEAD OF觸發器。

(1)AFTER觸發器:在DML觸發事件發生後才激發執行的觸發器,也就是說,先執行INSERT、UPDATE、DELETE語句而後才執行AFTER觸發器。這類觸發器只適用於數據表,不適用於視圖。AFTER觸發器通常用於檢查數據的變更狀況,以便採起相應的措施。例如,如發現錯誤,將拒絕或回滾更改的數據;

 (2)INSTEAD OF觸發器:「INSTEAD OF」的中文意思就是「代替」之意,由此不難理解:INSTEAD OF觸發器是在DML觸發事件發生以前(即數據被更新以前)執行的,這種執行將代替DML語句的執行。也就是說,INSTEAD OF觸發器是在DML觸發事件發生以前執行,而且取代相應的DML語句(INSERT、UPDATE或DELETE語句),轉而去執行INSTEAD OF觸發器定義的操做(此後再也不執行此DML語句)。INSTEAD OF觸發器既適用於數據表,也適用於視圖。但對同一個操做只能定義一個INSTEAD OF觸發器。

  

  若是根據觸發事件的類型劃分,DML觸發器一般又能夠分爲INSERT觸發器、UPDATE觸發器和DELETE觸發器:

Ø INSERT觸發器:執行INSERT語句而激發執行的觸發器;

Ø UPDATE觸發器:執行UPDATE語句而激發執行的觸發器;

Ø DELETE觸發器:執行DELETE語句而激發執行的觸發器。

 

 

2. DDL觸發器

Ø DDL觸發器是一種由執行DDL語句產生觸發事件而觸發執行的觸發器。DDL語句包括CREATE、ALTER、DROP、GRANT、DENY、REVOKE 和UPDATE STATISTICS 等語句。

Ø 與DML觸發器不一樣的是,DDL觸發器的觸發事件是執行DDL語句而引發的事件,這種觸發器是在觸發事件發生後執行的;而DML觸發器的觸發事件則是由執行DML語句引發的,可在事件發生前或發生後執行。另外,DDL觸發器的做用域不是架構,於是不能使用OBJECT_ID來查詢有關DDL觸發器的元數據。DDL觸發器可用於執行數據庫級的管理任務,如審覈和規範數據庫操做等。

Ø DML觸發器的觸發事件類型比較簡單,主要包括INSERT、DELETE和UPDATE等三種事件。但DDL觸發器的觸發事件就比較多,表9.1列了出經常使用的幾種事件。記住這幾種事件對之後的觸發器編程頗有幫助。

 

 

 

 

3. LOGON觸發器(登陸觸發器)

Ø 登陸觸發器是SQL Server 2005開始新增長的一類爲響應LOGON事件(登陸)而激發執行的觸發器。也就是說,只要有用戶登陸,登陸觸發器便可激發執行。所以,經過登陸觸發器能夠知道誰登陸了服務器以及什麼時候登陸的,並能夠實現如何跟蹤用戶的活動,還能夠限制特定用戶只能在特定時間段登陸等。

Ø 觸發事件對觸發器來講是關鍵的,因此許多時候又用引起觸發事件的SQL語句來對觸發器進行分類和命名。

 【例子】  INSERT觸發器、DELETE觸發器、UPDATE觸發器等。但這種分類不是嚴格的,只是爲闡明問題之便。

 

 

9.2.2 建立觸發器

1. 建立DML觸發器

    建立DML觸發器的SQL語法以下:

CREATE TRIGGER [ schema_name. ]trigger_name
ON { table | view }
[ WITH <dml_trigger_option> [ ,...n ] ]
{ FOR | AFTER | INSTEAD OF }
{ [ INSERT ] [ , ] [ UPDATE ] [ , ] [ DELETE ] }
[ WITH APPEND ]
[ NOT FOR REPLICATION ]
AS { sql_statement  [ ; ] [ ...n ] | EXTERNAL NAME <method specifier [ ; ] > }
<dml_trigger_option> ::= [ ENCRYPTION ] [ EXECUTE AS Clause ]
<method_specifier> ::= assembly_name.class_name.method_name

參數說明以下:

Ø trigger_name:設置觸發器的名稱,但不能以#或##開頭。

Ø schema_name:設置觸發器所屬架構的名稱。

Ø table | view:執行DML觸發器的表或視圖,也分別稱爲觸發器表或觸發器視圖。

Ø WITH ENCRYPTION:選擇該子句,則表示對觸發器文本進行加密。

Ø EXECUTE AS:指定用於執行該觸發器的安全上下文,即設置操做權限。

Ø AFTER:表示定義AFTER觸發器,即DML觸發器在觸發事件發生後執行。若是僅指定FOR關鍵字,則默認使用AFTER。

Ø INSTEAD OF:表示定義INSTEAD OF觸發器,即DML觸發器在觸發事件發生以前執行。

Ø { [DELETE] [,] [INSERT] [,] [UPDATE] }:指定觸發事件,若是選擇了DELETE,則表示建立DELETE觸發器,其餘類推。

Ø WITH APPEND:指定添加一個與當前觸發器類型相同的另一個觸發器。該子句不適用於INSTEAD OF觸發器。該功能在將來中將被刪除,建議不要使用。

Ø NOT FOR REPLICATION:該選項用於指示覆制代理修改到觸發器表時不執行觸發器。

Ø sql_statement:SQL語句。

Ø < method_specifier >:只適用於CLR觸發器,指定程序集與觸發器綁定的方法。

 

 

Ø  若是在CREATE TRIGGER語句中選擇了INSERT 、UPDATE或DELETE選項,則表示建立INSERT、UPDATE或DELETE觸發器。對於這類觸發器(DML觸發器),有兩種臨時表與它們有着密切的聯繫,它們是表DELETED和表INSERTED。這兩種臨時表都是在觸發器執行時被建立,執行完畢後被刪除。對它們的維護和管理是由SQL Server自動完成,用戶不能對這兩個表進行直接操做。

Ø  具體地,在執行INSERT觸發器時建立表INSERTED,執行DELETE觸發器時建立表DELETED,執行UPDATE觸發器時則同時建立表INSERTED和表DELETED,其中表INSERTED保存了更新的數據記錄,表DELETED則保存更新前的數據記錄(不受到更新影響的記錄不含在其中)。

Ø  SQL Server對這兩個表的操做過程以下:

ü 表INSERTED:在執行INSERT或UPDATE語句時,對用於插入或用於更新的數據記錄拷貝一個副本,並將該副本保存到表INSERTED中。可見,表INSERTED是觸發器表被插入或被更新後的一個子集。

ü 表DELETED:在執行DELETE和UPDATE語句時,將觸發器表中被刪除或被更新的數據記錄拷貝到表DELETED中。可見,表DELETED和觸發器表是不相交的(不會含有相同的記錄)。

 

 

【例9.7】建立一個觸發器,使之拒絕執行UPDATE操做,並輸出「對不起,您無權修改數據!」。

Ø 這是一個INSTEAD OF觸發器,其實現代碼以下:

USE MyDatabase
GO
CREATE TRIGGER myTrigger1
ON student
INSTEAD OF UPDATE
AS
BEGIN
  PRINT '對不起,您無權修改數據!';
END
GO

 

Ø 該觸發器的做用是,執行對錶student的UPDATE操轉爲執行觸發器myTrigger1(輸出「對不起,您無權修改數據!」),而再也不執行此UPDATE操做,所以表student中的數據並未受到該UPDATE操做的任何影響。

 

 

【例9.8】假設表student是記錄學生的一些註冊登記信息,表SC則是記錄註冊後的學生的選課信息。有的學生試圖不註冊而直接選課(顯然,在實際中是不容許出現這種狀況的),所以一個管理系必須可以杜絕這一點。這就涉及到兩個表之間的約束問題,如下定義一個AFTER觸發器來實現這種約束。

USE MyDatabase
GO
CREATE TRIGGER myTrigger2 ON SC
AFTER INSERT
AS
BEGIN
  DECLARE @s_no char(8), @n int;
  SELECT @s_no = P.s_no             -- 將正在插入的記錄的s_no字段值保存在@s_no中
  FROM SC AS P INNER JOIN INSERTED AS I 
  ON P.s_no = I.s_no;
SELECT @n = COUNT(*)                 -- 在表student中查找是否有s_no字段值等於@s_no的學生
  FROM student
  WHERE s_no = @s_no;  
IF @n <> 1
  BEGIN
     RAISERROR (‘該學生沒有註冊,選課無效。', 16, 1);
     ROLLBACK TRANSACTION;    -- 回滾(撤銷前面的插入操做)
   END
   ELSE PRINT '成功插入數據';
END
GO

 

Ø  上述觸發器經過使用內查詢來找出當前插入記錄的s_no字段值,並將該值保存在變量@s_no中。其中,使用了臨時表INSERTED,該表包含了INSERT語句中已插入的記錄。而後根據變量@s_no的值在表student中進行查詢,若是存在這樣的學生則函數COUNT(*)的值爲1,不然爲0。最後根據函數COUNT(*)的值來決定插入的選課信息是否有效。

 

【例9.9】出於某種緣由(如學生因退學、出國而取消學籍),有時候須要將表student中的記錄刪除,這時也應該將表SC中對應學生的選課信息刪除,以保持數據庫的完整性。這種完整性的保持能夠經過定義以下的AFTER觸發器來實現。

USE MyDatabase
GO
CREATE TRIGGER myTrigger3 ON student
AFTER DELETE
AS
BEGIN
  DECLARE @s_no char(8);
  SELECT @s_no = I.s_no        
  FROM DELETED AS I;
  DELETE FROM SC
  WHERE s_no = @s_no;
END
GO

 

      該觸發器的做用就是,當在表student中刪除某一條件記錄時,表SC中與該記錄對應的記錄(字段s_no值相同的記錄)將自動被刪除,以保持兩個表中數據的參照完整性。

 

 

2. 建立DDL觸發器

Ø 建立DDL觸發器的語法以下:

CREATE TRIGGER trigger_name
ON { ALL SERVER | DATABASE }
[ WITH <ddl_trigger_option> [ ,...n ] ]
{ FOR | AFTER } { event_type | event_group } [ ,...n ]
AS { sql_statement  [ ; ] [ ...n ] | EXTERNAL NAME < method specifier >  [ ; ] }
<ddl_trigger_option> ::= [ ENCRYPTION ] [ EXECUTE AS Clause ]
<method_specifier> ::= assembly_name.class_name.method_name

 

 

Ø 其參數意義基本同DML觸發器。此外,其特有的參數包含如下幾種:

ü DATABASE:將觸發器的做用域指定爲當前數據庫,這時只要數據庫中出現event_type或event_group,就會激發該觸發器。

ü ALL SERVER:將觸發器的做用域指定爲當前服務器,這時只要在服務器上出現event_type或event_group便可激發該觸發器。

ü event_type | event_group:分別爲SQL語言事件的名稱和事件分組的名稱。

 

 

 【例9.10】建立一個觸發器,用於禁止在當前數據庫中刪除任何的數據表。

Ø 該觸發器是一個DDL觸發器,其做用範圍是整個數據庫。其實現代碼以下:

USE MyDatabase
IF EXISTS (SELECT * FROM sys.triggers
   WHERE parent_class = 0 AND name = 'myTrigger4')
DROP TRIGGER myTrigger4 ON DATABASE         -- 刪除已存在的同名觸發器
GO
CREATE TRIGGER myTrigger4
ON DATABASE
FOR DROP_TABLE, ALTER_TABLE
AS
   PRINT '禁止刪除或修改數據庫中的任何數據表!';
   ROLLBACK;
GO

 

Ø 因爲DDL觸發器不能使用OBJECT_ID來查詢有關DDL觸發器的元數據,因此只能經過查詢系統數據表sys.triggers的方法來判斷觸發器是否存在。

Ø 該觸發器建立之後,發出刪除或修改數據表的任何命令都是被禁止的。

 

 

 【例9.11】  建立一個觸發器,使之可以禁止在服務器上建立任何服務器登陸。

Ø 該觸發器的做用範圍是整個服務器,其實現代碼以下:

IF EXISTS (SELECT * FROM sys.server_triggers
    WHERE name = 'myTrigger5')
DROP TRIGGER myTrigger5       -- 刪除已存在的同名觸發器
ON ALL SERVER
GO
CREATE TRIGGER myTrigger5
ON ALL SERVER
FOR CREATE_LOGIN  
AS
    PRINT '禁止建立服務器登陸。'   
    ROLLBACK;
GO

 

 

 

9.2.3 修改觸發器

Ø 觸發器的修改是由ALTER TRIGGER語句來完成。但修改不一樣類型的觸發器,ALTER TRIGGER語句的語法是不相同的。

Ø 如下分別是修改DML觸發器和DDL觸發器的SQL語句:

ALTER TRIGGER schema_name.trigger_name      -- 修改DML觸發器
ON ( table | view )
[ WITH <dml_trigger_option> [ ,...n ] ]
( FOR | AFTER | INSTEAD OF )
{ [ DELETE ] [ , ] [ INSERT ] [ , ] [ UPDATE ] }
[ NOT FOR REPLICATION ]
AS { sql_statement [ ; ] [ ...n ] | EXTERNAL NAME <method specifier> [ ; ] }
<dml_trigger_option> ::= [ ENCRYPTION ] [ <EXECUTE AS Clause> ]
<method_specifier> ::= assembly_name.class_name.method_name

ALTER TRIGGER trigger_name        -- 修改DDL觸發器
ON { DATABASE | ALL SERVER }
[ WITH <ddl_trigger_option> [ ,...n ] ]
{ FOR | AFTER } { event_type [ ,...n ] | event_group }
AS { sql_statement [ ; ] | EXTERNAL NAME <method specifier> [ ; ] }
}
<ddl_trigger_option> ::= [ ENCRYPTION ] [ <EXECUTE AS Clause> ]
<method_specifier> ::= assembly_name.class_name.method_name

 

 

Ø  其中涉及的參數與觸發器定義語法中的參數同樣。注意,不能爲DDL觸發器指定架構schema_name。

Ø  修改觸發器的優勢:

     主要是用戶擁有對它的操做權限不會由於對觸發器的修改而發生改變。另外,若是原來的觸發器定義時使用WITH ENCRYPTION或WITH RECOMPILE選項建立的,只有在ALTER TRIGGER中也包含這些選項時,這些選項纔有效。

 

 

【例9.12】但願修改例9.11中建立的觸發器myTrigger5,使之由「禁止建立服務器登陸」改成「禁止建立數據庫」。

對於這個修改操做,咱們能夠用下面的ALTER TRIGGER語句來完成:

ALTER TRIGGER myTrigger5
ON ALL SERVER
FOR CREATE_DATABASE  
AS
    PRINT '禁止在服務器上建立數據庫。'   
    ROLLBACK;
GO

 

 

 

9.2.4 禁用和刪除觸發器

1. 禁用和啓用觸發器

Ø 有時候(特別是在調試階段)咱們並不但願頻繁地觸發執行一些觸發器,但又不能將之刪除,這時最好先禁用這些觸發器。

Ø 禁用一段時間之後,通常還需從新啓用它,這又要涉及到觸發器啓用的概念。

Ø 如下分別是禁用和啓用觸發器的SQL語法:

DISABLE TRIGGER { [ schema. ] trigger_name [ ,...n ] | ALL }
ON { object_name | DATABASE | ALL SERVER } [ ; ]

ENABLE TRIGGER { [ schema_name . ] trigger_name [ ,...n ] | ALL }
ON { object_name | DATABASE | ALL SERVER } [ ; ]

 

u 參數說明以下:

Ø trigger_name:要禁用的觸發器的名稱。

Ø schema_name:觸發器所屬架構的名稱。但DDL觸發器沒有架構。

Ø ALL:若是選擇該選項,則表示對定義在ON子句做用域中的全部觸發器所有禁用。

Ø object_name:觸發器表或視圖的名稱。

Ø DATABASE:將做用域設置爲整個數據庫。

Ø ALL SERVER:將做用域設置爲整個服務器。

 

 

【例9.13】對例9.12修改後獲得的用於禁止在當前服務器中建立數據庫的DDL觸發器myTrigger5,咱們可使用列的SQL語句來禁用它。

DISABLE TRIGGER myTrigger5 ON ALL SERVER;

 

  若是將上述語句中的「myTrigger5」改成「ALL」,則表示禁用全部定義在服務器做用域中的觸發器。這樣就能夠避免一個一個地去執行禁用操做。

Ø  如下是啓用觸發器myTrigger5的語句:

ENABLE TRIGGER myTrigger5 ON ALL SERVER;

Ø  若是要啓用全部定義在服務器做用域中的觸發器,則可使用下列的語句來完成:

ENABLE TRIGGER ALL ON ALL SERVER;

 

 

 

【例9.14】如下定義了一個DML觸發器,它不容許對錶student進行更新操做:

USE MyDatabase
GO
CREATE TRIGGER myTrigger6
ON student
INSTEAD OF UPDATE
AS
BEGIN
   RAISERROR ('對錶student進行更新!', 16, 10)
   ROLLBACK;
END
GO

 

Ø  若是要禁用該DML觸發器,則能夠用下列的DISABLE TRIGGER語句來完成:

DISABLE TRIGGER myTrigger6 ON student;

 

 Ø  從新啓用myTrigger6則用下列語句完成:

ENABLE TRIGGER myTrigger6 ON student;

 

 Ø  若是將上面語句中的「myTrigger6」改成「ALL」,則表示啓用全部做用在表student上的觸發器。

 

2. 刪除觸發器

Ø 當確信一個觸發器再也不使用時,應當將之刪除。DML觸發器和DDL觸發器的刪除方法有所不一樣,如下分別是刪除這兩種觸發器的SQL語法:

DROP TRIGGER schema_name.trigger_name [ ,...n ] [ ; ]  -- 刪除DML觸發器
DROP TRIGGER trigger_name [ ,...n ] ON { DATABASE | ALL SERVER } [ ; ] -- 刪除DDL觸發器

 

Ø 在刪除DDL觸發器時須要指定觸發器名稱和做用域(即,是DATABASE仍是ALL SERVER),而刪除DML觸發器時則只需指定其名稱。

 

 

 

相關文章
相關標籤/搜索