天天看點

基于Mysql示例資料庫的存儲過程練習

基于Mysql示例資料庫的存儲過程練習

  • ​​寫在前面​​
  • ​​一、第一個入門示例,建立存儲過程,getAllProducts​​
  • ​​二、第二節,存儲過程變量​​
  • ​​三、存儲過程參數,IN,OUT,INOUT​​
  • ​​第四節 多個傳回參數的時候​​
  • ​​第五節 MYSQL 的 if 聲明​​
  • ​​第六節 MySQL CASE語句​​
  • ​​第七節 如何在 IF 和 CASE 語句之間選擇​​
  • ​​第八節 MySQL 中循環​​
  • ​​8.1 WHILE循環​​
  • ​​8.2 REPEAT循環​​
  • ​​第九節 MySQL Cursor(光标)(隻需了解階段,不做重點)​​
  • ​​第十節 如何查出MySQL資料庫中的存儲過程​​
  • ​​第十一節 MYSQL中存儲過程中的錯誤處理(了解即可,不做重點)​​
  • ​​第十二節 如何使用SIGNAL和RESIGNAL語句來提高存儲過程中的錯誤條件(了解即可,不做重點)​​
  • ​​第十三節 MySQL存儲函數(一般内置的函數已經可以滿足業務需求,這裡可自定義函數,并在SQL中調用)​​

寫在前面

此處是資料庫的腳本(建表語句,包括資料),執行方法這裡就不介紹了!​​classicmodels.sql​​

一、第一個入門示例,建立存儲過程,getAllProducts

USE classicmodels;-- 選擇資料庫
SHOW TABLES;
delimiter //
CREATE PROCEDURE getAllProducts ()
BEGIN
  SELECT * FROM products ;
END //
delimiter ;
-- 調用存儲過程 getAllProducts
CALL getAllproducts();
 -- 這裡會正确傳回findAll      

二、第二節,存儲過程變量

-- 聲明變量(在存儲過程内部)
-- DECLARE 變量名 變量類型 (size) DEFAULT default_value;      

– 聲明如下

– DECLARE a INT DEFAULT 1;

– DECLARE x,y INT DEFAULT 0;

– 可同時聲明多個變量

– DECLARE total_count INT;

– SET total_count = 10;

三、存儲過程參數,IN,OUT,INOUT

– IN/OUT/INOUT 名字 類型,IN/OUT/INOUT 名字 類型,IN/OUT/INOUT 名字 類型

– 以逗号隔開,可傳多個

– 執行個體

delimiter //
CREATE PROCEDURE getOfficeByCountry (IN countryName VARCHAR(255))
BEGIN
  SELECT
    *
  FROM
    offices
  WHERE
    country = countryName ;
END //
delimiter ;
-- 調用存儲過程
call getOfficeByCountry('usa');
call getOfficeByCountry('france');      

– OUT 參數示例

delimiter //
CREATE PROCEDURE CountOrderByStatus (
  IN orderStatus VARCHAR (255),
  OUT total INT
)
BEGIN
  SELECT
    count(orderStatus) INTO total
  FROM
    orders
  WHERE
    STATUS = orderStatus ;
END //
delimiter ;
-- 調用存儲過程
call CountOrderByStatus('Shipped',@total);
SELECT @total;      
call CountOrderByStatus('Shipped',@aa);
SELECT @aa;      

– INOUT 參數示例

delimiter //
CREATE PROCEDURE set_counter (
  INOUT count INT (4),
  IN inc INT (4)
)
BEGIN
  SET count = count + inc ;
END //
delimiter ;      
-- 這個怎麼運作,調用
SET @counter = 1;
CALL set_counter (@counter, 1);
SELECT @counter ;-- 傳回 2      
call set_counter(@counter,6);
SELECT @counter ;-- 傳回 8      

第四節 多個傳回參數的時候

delimiter //
CREATE PROCEDURE get_order_by_cust(
 IN cust_no INT,
 OUT shipped INT,
 OUT canceled INT,
 OUT resolved INT,
 OUT disputed INT)
BEGIN
 -- shipped
 SELECT count(*) INTO shipped
        FROM orders WHERE
            customerNumber = cust_no
                AND status = 'Shipped';
 -- canceled
 SELECT
            count(*) INTO canceled
        FROM
            orders
        WHERE
            customerNumber = cust_no
                AND status = 'Canceled';
 
 -- resolved
 SELECT
            count(*) INTO resolved
        FROM
            orders
        WHERE
            customerNumber = cust_no
                AND status = 'Resolved';
 
 -- disputed
 SELECT
            count(*) INTO disputed
        FROM
            orders
        WHERE
            customerNumber = cust_no
                AND status = 'Disputed';
 
END //
DELIMITER ;      
-- 調用存儲過程,輸出時按存儲過程的輸出順序,輸出對應字段
CALL get_order_by_cust (141 ,@a ,@b ,@c ,@d);
SELECT @a total_shiped ,@b total_canceled ,@c ,@d total_disputed ;      

第五節 MYSQL 的 if 聲明

-- 句式 1
  -- IF expression THEN
  --  statements;
  -- END
  -- IF;

-- 句式 2
-- IF expression THEN
--  statements;
-- ELSE
--  - statements;
-- END IF;

-- 句式 3
-- IF expression THEN
--  statements;
-- ELSEIF
-- ELSEIF - expression THEN
-- ELSEIF - statements;
-- ..
-- ELSE
-- ELSE
--  - statements;
-- END IF;      

– MySQL IF語句示例

delimiter //
CREATE PROCEDURE GetCustomerLevel (
  IN p_customerNumber INT (11),
  OUT p_customerLevel VARCHAR (10)
)
BEGIN
  DECLARE creditlim DOUBLE ; 
   SELECT creditlimit INTO creditlim
    FROM customers WHERE customerNumber = p_customerNumber;
    IF creditlim > 50000 THEN
        SET p_customerLevel = 'PLATINUM' ;
    ELSEIF (creditlim <= 50000 AND creditlim >= 10000) THEN
        SET p_customerLevel = 'GOLD' ;
    ELSEIF creditlim < 10000 THEN
        SET p_customerLevel = 'SILVER' ;
    END IF ;
END //-- 老是會報錯,即使複制過來也是,因為 末尾的 // 

delimiter ;      
CALL GetCustomerLevel (144 ,@customerLevel) ;
SELECT @customerLevel ;      

第六節 MySQL CASE語句

-- 聲明定義
  --  CASE case_expression
  --  WHEN when_expression_1 THEN
  --      commands
  --  WHEN when_expression_2 THEN
  --      commands...
  --  ELSE
  --      commands
  --  END CASE;      

– 簡單 CASE 示例一

DELIMITER //
CREATE PROCEDURE GetCustomerShipping (
   IN p_customerNumber INT (11),
   OUT p_shiping VARCHAR (50)
)
BEGIN
   DECLARE customerCountry VARCHAR (50) ; 
   SELECT country INTO customerCountry FROM customers
      WHERE customerNumber = p_customerNumber ; 
      CASE customerCountry
      WHEN 'USA' THEN
            SET p_shiping = '2-day Shipping' ;
      WHEN 'Canada' THEN
            SET p_shiping = '3-day Shipping' ;
      ELSE
            SET p_shiping = '5-day Shipping' ;
      END CASE ;
END //
delimiter ;      

調用存儲過程

CALL GetCustomerShipping(144,@p_shiping);
SELECT @p_shiping;      

– SQL測試腳本,可一鍵運作,輸出結果

SET @customerNo = 112;
 
SELECT country into @country
FROM customers
WHERE customernumber = @customerNo;
 
CALL GetCustomerShipping(@customerNo,@shipping);
 
SELECT @customerNo AS Customer,
       @country    AS Country,
       @shipping   AS Shipping; -- USA  2-day Shipping  112      
-- 搜尋 CASE 的 statement 
--  CASE
--     WHEN condition_1 THEN commands
--     WHEN condition_2 THEN commands
--     ...
--     ELSE commands
--  END CASE;      

CASE示例 二

DELIMITER //
CREATE PROCEDURE GetCustomerLevel_2 (
  IN p_customerNumber INT (11),
  OUT p_customerLevel VARCHAR (10)
)
BEGIN
  DECLARE
    creditlim DOUBLE ; SELECT
      creditlimit INTO creditlim
    FROM
      customers
    WHERE
      customerNumber = p_customerNumber ; CASE
    WHEN creditlim > 50000 THEN
    SET p_customerLevel = 'PLATINUM' ;
    WHEN (
      creditlim <= 50000
      AND creditlim >= 10000
    ) THEN
    SET p_customerLevel = 'GOLD' ;
    WHEN creditlim < 10000 THEN

    SET p_customerLevel = 'SILVER' ;
    END CASE ; 
END //
delimiter ;      

– 測試腳本 2

CALL GetCustomerLevel_2(144,@p_customerLevel);
SELECT @p_customerLevel as customerLevel; -- PLATINUM      

第七節 如何在 IF 和 CASE 語句之間選擇

– 首先考慮以下幾點

– 一個簡單的CASE語句是不是更可讀IF語句,當你對一個範圍内唯一值的比較單一的表達。此外,簡單CASE語句比IF語句更有效。

– 當您基于多個值檢查複雜表達式時,該IF語句更容易了解。

– 如果選擇使用該CASE語句,則必須確定至少有一個CASE條件比對。否則,您需要定義錯誤處理程式以捕獲錯誤。回想一下,您不必使用該IF語句執行此操作。

– 在大多數組織中,總會有一些稱為開發指南的文檔,它為開發人員提供了程式設計風格的命名約定和指南。您應該參考本文檔并遵循開發實踐。

– 在某些情況下,之間的混合IF,并CASE讓您的存儲過程更具可讀性和效率。

第八節 MySQL 中循環

三種: WHILE,REPEAT 和 LOOP。

8.1 WHILE循環

– WHILE expression DO

– statements

– END WHILE

– 示例

DELIMITER //
-- DROP PROCEDURE IF EXISTS test_mysql_while_loop //
CREATE PROCEDURE test_mysql_while_loop ()
BEGIN
  DECLARE x INT ; 
  DECLARE str VARCHAR (255) ;
    SET x = 1 ;
    SET str = '' ;
    WHILE x <= 5 DO

    SET str = CONCAT(str, x, ',') ;
    SET x = x + 1 ;
    END WHILE ; 
    SELECT str ;
 END //
DELIMITER ;      

調用函數

call test_mysql_while_loop();      

8.2 REPEAT循環

– REPEAT

– statements;

– UNTIL expression – 注意UNTIL 表達式中沒有分号 “;”

– END REPEAT

示例 repeated

DELIMITER //
DROP PROCEDURE IF EXISTS mysql_test_repeat_loop //
    CREATE PROCEDURE mysql_test_repeat_loop ()
    BEGIN
      DECLARE x INT ;
      DECLARE str VARCHAR (255) ;
        SET x = 1 ;
        SET str = '' ;
        REPEAT
            SET str = CONCAT(str, x, ',') ;
            SET x = x + 1 ; 
        UNTIL x > 5
        END REPEAT; 
        SELECT str ; 
      END //
    DELIMITER ;      

– 調用函數

CALL mysql_test_repeat_loop();

-- 8.3 LOOP循環
-- LOOP,LEAVE和ITERATE語句      

– 示例

delimiter //
CREATE PROCEDURE test_mysql_loop ()
BEGIN
  DECLARE
    x INT ; DECLARE
      str VARCHAR (255) ;
    SET x = 1 ;
    SET str = '' ; loop_label :
    LOOP
        IF x > 10 THEN
            LEAVE loop_label ;
        END IF ;
            SET x = x + 1 ;
        IF (x MOD 2) THEN
            ITERATE loop_label ;
        ELSE
            SET str = CONCAT(str, x, ',') ;
    END IF ;
    END LOOP;
    SELECT str ;
END //
delimiter ;      
-- 調用存儲過程
  call test_mysql_loop();  -- 2,4,6,8,10,      

第九節 MySQL Cursor(光标)(隻需了解階段,不做重點)

– MySQL遊标是隻讀的,不可滾動且不敏感的。

– 隻讀:您無法通過遊标更新基礎表中的資料。

– 不可滾動:您隻能按SELECT語句确定的順序擷取行。您無法以相反的順序擷取行。

– 此外,您不能跳過行或跳轉到結果集中的特定行。

– 未定型:有兩種光标:未定型遊标和不敏感遊标。敏感光标指向實際資料,而不敏感光标使用資料的臨時副本。

– 敏感性遊标比不敏感遊标執行得更快,因為它不必建立臨時資料副本。但是,對來自其他連接配接的資料所做的

– 任何更改都将影響敏感光标正在使用的資料,是以,如果不更新敏感光标正在使用的資料,則更安全。MySQL遊标是敏感的。

第十節 如何查出MySQL資料庫中的存儲過程

-- 10.1 展示特征 
  SHOW PROCEDURE STATUS [LIKE 'pattern' | WHERE expr];  
  --  示例
    SHOW PROCEDURE STATUS;
    SHOW PROCEDURE STATUS WHERE db = 'classicmodels';
    SHOW PROCEDURE STATUS WHERE name LIKE '%product%';
-- 10.2 顯示存儲過程的源代碼
  SHOW CREATE PROCEDURE stored_procedure_name;
  SHOW CREATE PROCEDURE getAllProducts;      

第十一節 MYSQL中存儲過程中的錯誤處理(了解即可,不做重點)

-- 11.1 聲明處理程式
  DECLARE action HANDLER FOR condition_value statement;
  -- 如果條件的值與之比對  condition_value,MySQL将執行statement并繼續或退出目前代碼塊action。

  -- 所述action接受下列值之一:

  -- CONTINUE:繼續執行封閉代碼塊(BEGIN... END)。
  -- EXIT :聲明處理程式的封閉代碼塊的執行終止。

-- 11.2 MySQL錯誤處理示例      

第十二節 如何使用SIGNAL和RESIGNAL語句來提高存儲過程中的錯誤條件(了解即可,不做重點)

第十三節 MySQL存儲函數(一般内置的函數已經可以滿足業務需求,這裡可自定義函數,并在SQL中調用)

-- 聲明 statement
  --  CREATE FUNCTION function_name(param1,param2,…)
  --     RETURNS datatype
  --    [NOT] DETERMINISTIC
  --  statements      

示例

DELIMITER //
CREATE FUNCTION CustomerLevel (p_creditLimit DOUBLE) RETURNS VARCHAR (10) DETERMINISTIC
BEGIN
  DECLARE lvl VARCHAR (10) ;
  IF p_creditLimit > 50000 THEN
      SET lvl = 'PLATINUM' ;
  ELSEIF (p_creditLimit <= 50000 AND p_creditLimit >= 10000) THEN
      SET lvl = 'GOLD' ;
  ELSEIF p_creditLimit < 10000 THEN
      SET lvl = 'SILVER' ;
  END IF ;
  RETURN (lvl) ;
END //
delimiter ;      
SELECT
      customerName,
      CustomerLevel (creditLimit)
   FROM
      customers
   ORDER BY
      customerName ;