1. 程式人生 > >資料庫儲存過程,觸發器,遊標,函式

資料庫儲存過程,觸發器,遊標,函式

MySQL5 中添加了儲存過程的支援。

 大多數SQL語句都是針對一個或多個表的單條語句。並非所有的操作都這麼簡單。經常會有一個完整的操作需要多條才能完成。

 儲存過程簡單來說,就是為以後的使用而儲存的一條或多條MySQL語句的集合。可將其視為批檔案。雖然他們的作用不僅限於批處理。

 為什麼要使用儲存過程:優點

通過把處理封裝在容易使用的單元中,簡化複雜的操作。

由於不要求反覆建立一系列處理步驟,這保證了資料的完整性。如果開發人員和應用程式都使用了同一儲存過程,則所使用的程式碼是相同的。還有就是防止錯誤,需要執行的步驟越多,出錯的可能性越大。防止錯誤保證了資料的一致性。

簡化對變動的管理。如果表名、列名或業務邏輯有變化。只需要更改儲存過程的程式碼,使用它的人員不會改自己的程式碼了。

提高效能,因為使用儲存過程比使用單條SQL語句要快。

存在一些職能用在單個請求中的MySQL元素和特性,儲存過程可以使用它們來編寫功能更強更靈活的程式碼。

換句話說3個主要好處簡單、安全、高效能。

    缺點:

 1 一般來說,儲存過程的編寫要比基本的SQL語句複雜,編寫儲存過程需要更高的技能,更豐富的經驗。

 你可能沒有建立儲存過程的安全訪問許可權。許多資料庫管理員限制儲存過程的建立,允許使用者使用儲存過程,但不允許建立儲存過程。

    儲存過程是非常有用的,應該儘可能的使用它們

    執行儲存過程

        MySQL稱儲存過程的執行為呼叫,因此MySQL執行儲存過程的語句為CALL        .CALL接受儲存過程的名字以及需要傳遞給它的任意引數

           

 CALL productpricing(@pricelow , @pricehigh , @priceaverage);

            //執行名為productpricing的儲存過程,它計算並返回產品的最低、最高和平均價格

    建立儲存過程

        CREATE  PROCEDURE 儲存過程名()

           一個例子說明:一個返回產品平均價格的儲存過程如下程式碼:

           CREATE  PROCEDURE  productpricing()

           BEGIN

            SELECT Avg(prod_price)  AS priceaverage

           FROM products;

           END;

        //建立儲存過程名為productpricing,如果儲存過程需要接受引數,可以在()中列舉出來。即使沒有引數後面仍然要跟()。BEGINEND語句用來限定儲存過程體,過程體本身是個簡單的SELECT語句

        MYSQL處理這段程式碼時會建立一個新的儲存過程productpricing。沒有返回資料。因為這段程式碼時建立而不是使用儲存過程。

 

    Mysql命令列客戶機的分隔符

        預設的MySQL語句分隔符為分號 ; Mysql命令列實用程式也是 ; 作為語句分隔符。如果命令列實用程式要解釋儲存過程自身的 ; 字元,則他們最終不會成為儲存過程的成分,這會使儲存過程中的SQL出現句法錯誤

        解決方法是臨時更改命令實用程式的語句分隔符

            DELIMITER //    //定義新的語句分隔符為//

            CREATE PROCEDURE productpricing()

            BEGIN

            SELECT Avg(prod_price) AS priceaverage

            FROM products;

            END //

            DELIMITER ;    //改回原來的語句分隔符為 ;

            \符號外,任何字元都可以作為語句分隔符

        CALL productpricing();  //使用productpricing儲存過程

        執行剛建立的儲存過程並顯示返回的結果。因為儲存過程實際上是一種函式,所以儲存過程名後面要有()符號

    刪除儲存過程

        DROP PROCEDURE productpricing ;     //刪除儲存過程後面不需要跟(),只給出儲存過程名

        為了刪除儲存過程不存在時刪除產生錯誤,可以判斷僅儲存過程存在時刪除

        DROP PROCEDURE IF EXISTS

        使用引數

        Productpricing只是一個簡單的儲存過程,他簡單地顯示SELECT語句的結果。

        一般儲存過程並不顯示結果,而是把結果返回給你指定的變數

            CREATE PROCEDURE productpricing(

            OUT p1 DECIMAL(8,2),

            OUT ph DECIMAL(8,2),

            OUT pa DECIMAL(8,2),

            )

            BEGIN

            SELECT Min(prod_price)

            INTO p1

            FROM products;

            SELECT Max(prod_price)

            INTO ph

            FROM products;

            SELECT Avg(prod_price)

            INTO pa

            FROM products;

            END;

            此儲存過程接受3個引數,p1儲存產品最低價格,ph儲存產品最高價格,pa儲存產品平均價格。每個引數必須指定型別,這裡使用十進位制值。關鍵字OUT指出相應的引數用來從儲存過程傳給一個值(返回給呼叫者)。MySQL支援IN(傳遞給儲存過程)、OUT(從儲存過程中傳出、如這裡所用)和INOUT(對儲存過程傳入和傳出)型別的引數。儲存過程的程式碼位於BEGINEND語句內,如前所見,它們是一些列SELECT語句,用來檢索值,然後儲存到相應的變數(通過INTO關鍵字)

        呼叫修改過的儲存過程必須指定3個變數名:

        CALL productpricing(@pricelow , @pricehigh , @priceaverage);

        這條CALL語句給出3個引數,它們是儲存過程將儲存結果的3個變數的名字

    變數名  所有的MySQL變數都必須以@開始

    使用變數

        SELECT @priceaverage ;

        SELECT @pricelow , @pricehigh , @priceaverage ;   //獲得3給變數的值

        下面是另一個例子,這次使用INOUT引數。ordertotal接受訂單號,並返回該訂單的合計

            CREATE PROCEDURE ordertotal(

           IN onumber INT,

           OUT ototal DECIMAL(8,2)

            )

            BEGIN

            SELECT Sum(item_price*quantity)

            FROM orderitems

            WHERE order_num = onumber

            INTO ototal;

            END;

            //onumber定義為IN,因為訂單號時被傳入儲存過程,ototal定義為OUT,因為要從儲存過程中返回合計,SELECT語句使用這兩個引數,WHERE子句使用onumber選擇正確的行,INTO使用ototal儲存計算出來的合計

    為了呼叫這個新的過程,可以使用下列語句:

        CALL ordertotal(2005 , @total);   //這樣查詢其他的訂單總計可直接改變訂單號即可

        SELECT @total;

    建立智慧的儲存過程

        上面的儲存過程基本都是封裝MySQL簡單的SELECT語句,但儲存過程的威力在它包含業務邏輯和智慧處理時才顯示出來

        例如:你需要和以前一樣的訂單合計,但需要對合計增加營業稅,不過只針對某些顧客(或許是你所在區的顧客)。那麼需要做下面的事情:

            1 獲得合計(與以前一樣)

            2 吧營業稅有條件地新增到合計

            3 返回合計(帶或不帶稅)

        儲存過程的完整工作如下:

            -- Name: ordertotal

            -- Parameters: onumber = 訂單號

            --           taxable = 1為有營業稅 0 為沒有

            --           ototal = 合計

            CREATE  PROCEDURE ordertotal(

            IN onumber INT,

            IN taxable BOOLEAN,

            OUT ototal DECIMAL(8,2)

            -- COMMENT()中的內容將在SHOW PROCEDURE STATUS ordertotal()中顯示,其備註作用

            ) COMMENT 'Obtain order total , optionally adding tax'

            BEGIN

            -- 定義total區域性變數

            DECLARE total DECIMAL(8,2)

            DECLARE taxrate INT DEFAULT 6;

 

            -- 獲得訂單的合計,並將結果儲存到區域性變數total

            SELECT Sum(item_price*quantity)

            FROM orderitems

            WHERE order_num = onumber

            INTO total;

 

            -- 判斷是否需要增加營業稅,如為真,這增加6%的營業稅

            IF taxable THEN

            SELECT total+(total/100*taxrate) INTO total;

                  END IF;

            -- 把區域性變數total中才合計傳給ototal

            SELECT total INTO ototal;

            END;

            此儲存過程有很大的變動,首先,增加了註釋(前面放置--)。在儲存過程複雜性增加時,這樣很重要。在儲存體中,用DECLARE語句定義了兩個區域性變數。DECLARE要求制定變數名和資料型別,它也支援可選的預設值(這個例子中taxrate的預設設定為6%),SELECT 語句已經改變,因此其結果儲存到total區域性變數中而不是ototalIF語句檢查taxable是否為真,如果為真,則用另一SELECT語句增加營業稅到區域性變數total,最後用另一SELECT語句將total(增加了或沒有增加的)儲存到ototal中。

    COMMENT關鍵字  本列中的儲存過程在CREATE PROCEDURE 語句中包含了一個COMMENT值,他不是必需的,但如果給出,將在SHOW PROCEDURE STATUS的結果中顯示

    IF語句   這個例子中給出了MySQLIF語句的基本用法。IF語句還支援ELSEIFELSE子句(前者還使用THEN子句,後者不使用)

    檢查儲存過程

        為顯示用來建立一個儲存過程的CREATE語句,使用SHOW CREATE PROCEDURE語句

            SHOW CREATE PROCEDURE ordertotal;

        為了獲得包括何時、有誰建立等詳細資訊的儲存過程列表。使用SHOW PROCEDURE STATUS.限制過程狀態結果,為了限制其輸出,可以使用LIKE指定一個過濾模式,例如:SHOW PROCEDURE STATUS LIKE ''ordertotal;

MySQL5添加了對遊標的支援

    只能用於儲存過程

    由前幾章可知,mysql檢索操作返回一組稱為結果集的行。都與mysql語句匹配的行(0行或多行),使用簡單的SELECT語句,沒有辦法得到第一行、下一行或前10行,也不存在每次行地處理所有行的簡單方法(相對於成批處理他們)

    有時,需要在檢索出來的行中前進或後退一行或多行。這就是使用遊標的原因。遊標(cursor)是一個儲存在MYSQL伺服器上的資料庫查詢,它不是一條SELECT語句,而是被該語句檢索出來的結果集。在儲存了遊標之後,應用程式可以根據需要滾動或瀏覽其中的資料。

    遊標主要用於互動式應用,其中使用者需要滾動螢幕上的資料,並對資料進行瀏覽或做出更改。

    使用遊標

        使用遊標涉及幾個明確的步驟:

            1 在能夠使用遊標前,必須宣告(定義)它,這個過程實際上沒有檢索資料,它只是定義要使用的SELECT語句

            2 一旦聲明後,必須開啟遊標以供使用。這個過程用錢嗎定義的SELECT語句吧資料實際檢索出來

            3 對於填有資料的遊標,根據需要取出(檢索)的各行

            4 在接受遊標使用時,必須關閉它 如果不明確關閉遊標,MySQL將會在到達END語句時自動關閉它

    建立遊標

        遊標可用DECLARE 語句建立。 DECLARE命名遊標,並定義相應的SELECT語句。根據需要選擇帶有WHERE和其他子句。如:下面第一名為ordernumbers的遊標,使用了檢索所有訂單的SELECT語句

            CREATE PROCEDURE processorders()

            BEGIN

            DECLARE ordernumbers CURSOR

            FOR

            SELECT order_num FROM orders ;

            END;

            儲存過程處理完成後,遊標就消失,因為它侷限於儲存過程

    開啟和關閉遊標

            CREATE PROCEDURE processorders()

            BEGIN

            DECLAREordernumbers CURSOR

            FOR

            SELECT order_num FROM orders ;

            Open ordernumbers ;

            Close ordernumbers ;  //CLOSE釋放遊標使用的所有內部記憶體和資源,因此,每個遊標不需要時都應該關閉

            END;

    使用遊標資料

        在一個遊標被開啟後,可以使用FETCH語句分別訪問它的每一行。FETCH指定檢索什麼資料(所需的要列),檢索出來的資料儲存在什麼地方。它還向前移動遊標中的內部行指標,使下一條FETCH語句檢索下一行,相當於PHP中的each()函式

迴圈檢索資料,從第一行到最後一行

            CREATE PROCEDURE processorders()

            BEGIN

            -- 宣告區域性變數

            DECLARE done BOOLEAN DEFAULT 0;

            DECLARE o INT;

 

            DECLAREordernumbers CURSOR

            FOR

            SELECT order_num FROM orders ;

            -- SQLSTATE02000時設定done值為1

            DECLARE CONTINUE HANDLER FOR SQLSTATE '02000' SET done=1;

            --開啟遊標

            Open ordernumbers ;

            -- 開始迴圈

            REPEAT

            -- 把當前行的值賦給宣告的區域性變數o

            FETCH ordernumbers INTO o;

            -- done為真時停止迴圈

            UNTIL done END REPEAT;

            --關閉遊標

            Close ordernumbers ;  //CLOSE釋放遊標使用的所有內部記憶體和資源,因此,每個遊標不需要時都應該關閉

            END;

        語句中定義了CONTINUE HANDLER ,它是在條件出現時被執行的程式碼。這裡,它指出當SQLSTATE '02000'出現時,SET done=1SQLSTATE '02000'是一個未找到條件,當REPEAT沒有更多的行供迴圈時,出現這個條件。

    DECLARE 語句次序  DECLARE語句定義區域性變數必須在定義任意遊標或控制代碼之前定義,而控制代碼必須在遊標之後定義。不遵守此規則就會出錯

重複和迴圈   除這裡使用REPEAT語句外,MySQL還支援迴圈語句,它可用來重複執行程式碼,直到使用LEAVE語句手動退出為止。通常REPEAT語句的語法使它更適合於對遊標進行的迴圈。

為了把這些內容組織起來,這次吧取出的資料進行某種實際的處理

        CREATE PROCEDURE processorders()

        BEGIN

        -- 宣告區域性變數

        DECLARE done BOOLEAN DEFAULT 0;

        DECLARE o INT;

        DECLARE t DECIMAL(8,2)

 

        DECLAREordernumbers CURSOR

        FOR

        SELECT order_num FROM orders ;

        -- SQLSTATE02000時設定done值為1

        DECLARE CONTINUE HANDLER FOR SQLSTATE '02000' SET done=1;

        -- 建立一個ordertotals的表

        CREATE TABLE IF NOT EXISTS ordertotals( order_num INT , total DECIMAL(8,2))

        --開啟遊標

        Open ordernumbers ;

        -- 開始迴圈

        REPEAT

        -- 把當前行的值賦給宣告的區域性變數o

        FETCH ordernumbers INTO o;

        -- 用上文講到的ordertotal儲存過程並傳入引數,返回營業稅計算後的合計傳給t變數

        CALL ordertotal(o , 1 ,t)

        -- 把訂單號和合計插入到新建的ordertotals表中

        INSERT INTO ordertotals(order_num, total) VALUES(o , t);

        -- done為真時停止迴圈

        UNTIL done END REPEAT;

        --關閉遊標

        Close ordernumbers ;  //CLOSE釋放遊標使用的所有內部記憶體和資源,因此,每個遊標不需要時都應該關閉

        END;

        最後SELECT * FROM ordertotals就能檢視結果了

 使用觸發器

    MySQL5版本後支援觸發器

    只有表支援觸發器,檢視不支援觸發器

    MySQL語句在需要的時被執行,儲存過程也是如此,但是如果你想要某條語句(或某些語句)在事件發生時自動執行,那該怎麼辦呢:例如:

        1 每增加一個顧客到某個資料庫表時,都檢查其電話號碼格式是否正確,區的縮寫是否為大寫

        2 每當訂購一個產品時,都從庫存數量中減少訂購的數量

        3 無論何時刪除一行,都在某個存檔中保留一個副本

    這寫例子的共同之處是他們都需要在某個表發生更改時自動處理。這就是觸發器。觸發器是MySQL響應一下任意語句而自動執行的一條MySQL語句(或位於BEGINEND語句之間的一組語句)

    1 DELETE

    2 INSERT

    3 UPDATE

    其他的MySQL語句不支援觸發器

    建立觸發器

        建立觸發器需要給出4條資訊

        1 唯一的觸發器名;  //儲存每個資料庫中的觸發器名唯一

        2 觸發器關聯的表;

        3 觸發器應該響應的活動(DELETEINSERTUPDATE

        4 觸發器何時執行(處理前還是後,前是BEFORE 後是AFTER

        建立觸發器用CREATE TRIGGER

        CREATE TRIGGER newproduct AFTER INSERT ON products

        FOR EACH ROW SELECT'Product added'

       建立新觸發器newproduct ,它將在INSERT語句成功執行後執行。這個觸發器還鎮定FOR EACH ROW,因此程式碼對每個插入的行執行。這個例子作用是文字對每個插入的行顯示一次product added

        FOR EACH ROW 針對每個行都有作用,避免了INSERT一次插入多條語句

    觸發器定義規則

        觸發器按每個表每個事件每次地定義,每個表每個事件每次只允許定義一個觸發器,因此,每個表最多定義6個觸發器(每條INSERT UPDATE DELETE的之前和之後)。單個觸發器不能與多個事件或多個表關聯,所以,如果你需要一個對INSERT UPDATE儲存執行的觸發器,則應該定義兩個觸發器

    觸發器失敗  如果BEFORE(之前)觸發器失敗,則MySQL將不執行SQL語句的請求操作,此外,如果BEFORE觸發器或語句本身失敗,MySQL將不執行AFTER(之後)觸發器

    刪除觸發器

        DROP TRIGGER newproduct

        觸發器不能更新或覆蓋,所以修改觸發器只能先刪除再建立

    使用觸發器

        我們來看看每種觸發器以及它們的差別

    INSERT 觸發器

        INSERT觸發器在INSERT語句執行之前或之後執行。需要知道以下幾點:

    1 INSERT觸發器程式碼內,可引用一個名為NEW的虛擬表,訪問被插入的行

    2 BEFORE INSERT觸發器中,NEW中的值也可以被更新(允許更改插入的值)

    3 對於AUTO_INCREMENT列,NEWINSERT執行之前包含0,在INSERT執行之後包含新的自動生成值

        提示:通常BEFORE用於資料驗證和淨化(目的是保證插入表中的資料確實是需要的資料)。本提示也適用於UPDATE觸發器

    DELETE 觸發器

        DELETE觸發器在語句執行之前還是之後執行,需要知道以下幾點:

    1 DELETE觸發器程式碼內,你可以引用一個名為OLD的虛擬表,訪問被刪除的行;

    2 OLD中的值全部是隻讀的,不能更新

        例子演示適用OLD儲存將要除的行到一個存檔表中

        CREATE TRIGGERdeleteorder BEFORE DELETE ON orders

        FOR EACH ROW

        BEGIN  

        INSERT INTO archive_orders(order_num , order_date , cust_id)

        VALUES(OLD.order_num , OLD.order_date , OLD.cust_id);

        END;

        //此處的BEGIN  END塊是非必需的,可以沒有

    在任何訂單刪除之前執行這個觸發器,它適用一條INSERT語句將OLD中的值(將要刪除的值)儲存到一個名為archive_orders的存檔表中

    BEFORE DELETE觸發器的優點是(相對於AFTER DELETE觸發器),如果由於某種原因,訂單不能被存檔,DELETE本身將被放棄執行。

    多語言觸發器  正如上面所見,觸發器deleteorder 使用了BEGINEND語句標記觸發器體。這在此例中並不是必需的,不過也沒有害處。使用BEGIN  END塊的好處是觸發器能容納多條SQL語句。

    UPDATE觸發器

        UPDATE觸發器在語句執行之前還是之後執行,需要知道以下幾點:

        1 UPDATE觸發器程式碼中,你可以引用一個名為OLD的虛擬表訪問(UPDATE語句前)的值,引用一名為NEW的虛擬表訪問新更新的值

        2 BEFORE UPDATE觸發器中,NEW中的值可能被更新,(允許更改將要用於UPDATE語句中的值)

        3 OLD中的值全都是隻讀的,不能更新

            例子:保證州名的縮寫總是大寫(不管UPDATE語句給出的是大寫還是小寫)

            CREATE TRIGGER updatevendor BEFORE UPDATE ON vendores FOR EACH ROW SETNEW.vend_state = Upper(NEW.vend_state)

    觸發器的進一步介紹

    1 與其他DBMS相比,MySQL5中支援的觸發器相當初級。以後可能會增強

    2 建立觸發器可能需要特殊的安全訪問許可權,但是觸發器的執行時自動的.如果INSERT UPDATE DELETE能執行,觸發器就能執行

    3 應該用觸發器來保證資料的一致性(大小寫、格式等)。在觸發器中執行這種型別的處理的優點是它總是進行這個處理,而且是透明地進行,與客戶機應用無關

    4 觸發器的一種非常有意義的使用建立審計跟蹤。使用觸發器把更改(如果需要,甚至還有之前和之後的狀態)記錄到另一表非常容易

    5 遺憾的是,MySQL觸發器中不支援CALL語句,這表示不能從觸發器中呼叫儲存過程。所需要的儲存過程程式碼需要複製到觸發器內