mysql 儲存過程 示例
1.情景展示
現在有個取票號的需求:
1.將取出當前票號,將它放入中間表,然後更新主表票號(票號+1);
2.以後取票號先從中間表拿,中間表沒有再從主表拿,拿到之後再次放入中間表並更新主表。
用儲存過程如何實現?
2.分析
主表在這裡不展示,只展示中間表的表結構。
3.解決方案
視覺化開發工具:Navicat
選中你的資料庫例項,點選函式,右側就會出現該資料庫例項下所有的儲存過程及函式
Navicat和plsql不同的是:Navicat函式視窗,沒有單獨的儲存過程管理視窗。
在右側出現的函式視窗,點選“新建函式”,起個名字
模式:有三種,IN-->入參(SQL限制條件),OUT-->出參(SQL執行結果),INTOUT-->既是入參也是出參;
名:引數名稱(入參一般以"v_"開頭,返參一般以"out_"開頭,方便區分);
型別:指定引數的資料型別
點選完成,這樣,儲存過程的框架就搭好了。
點選儲存卻會報語法錯誤,具體什麼錯不知道。
後來才發現,mysql與oracle不同,儲存過程每個引數的資料型別必須限制大小。
再次儲存就不會報錯啦。
如何宣告變數?
因為我是從oracle轉過來的,所以一直以oracle的思維方式來建立mysql的儲存過程。
在oracle中,儲存過程想要使用變數,不需要使用關鍵詞DECLARE,並且變數統一放在BEGIN之前,方便統一管理;
而在mysql中,要想使用變數需要滿足的條件:
第一,即使是儲存過程,也要使用:關鍵詞DECLARE;
第二,每使用一個變數前面都必須加declare;
第三,變數的宣告必須在begin和and之間。
第四,變數的資料型別該限制最大值的必須做出限制。
如何使用INTO給變數正確賦值?
一開始,我以為是醬紫的:
原來,正確的寫法是這樣子的:
當將查詢結果塞給兩個及以上變數時,INTO左邊的欄位和INTO右邊的變數數量必須一一對應上才行,並且只能使用一個INTO。
也可以這個樣子:
當將查詢結果塞給一個變數時,可以將這個作為最後一個查詢欄位,並且可以使用INTO賦值,後面不能再跟其他欄位。
IF條件判斷
if條件必須使用小括號()包起來,否則儲存失敗。
CREATE DEFINER=`platform2020_test`@`%` PROCEDURE `PRO_TEST`( IN `V_ORG_CODE` VARCHAR ( 50 ), IN `V_INVOICING_CLERK` VARCHAR ( 50 ), IN `V_BILL_TYPE` VARCHAR ( 10 ), IN `V_MEDICAL_TYPE` VARCHAR ( 10 ), OUT `OUT_BILL_CODE` VARCHAR ( 50 ), OUT `OUT_BILL_NUMBER` VARCHAR ( 50 )) BEGIN /*定義票據終止號變數*/ DECLARE V_END_NUMBER VARCHAR ( 50 ); /*1. 更新主表當前票號(假更新,控制併發)*/ UPDATE CZ_EINVOICES SET CURRENTNO = CURRENTNO WHERE ORGCODE = V_ORG_CODE AND IVCTYPE = V_BILL_TYPE AND MEDICALTYPE = V_MEDICAL_TYPE; /*2. 從票據中間表獲取票號*/ SELECT BILLCODE, BILLNUMBER INTO OUT_BILL_CODE, OUT_BILL_NUMBER FROM VOUCHER_NUMBER WHERE ORGCODE = V_ORG_CODE AND BILLTYPE = V_BILL_TYPE AND MEDICALTYPE = V_MEDICAL_TYPE AND STATUS = 0 -- 只取開票失敗的記錄 ORDER BY BILLNUMBER LIMIT 1; /*3. 票號非空判斷*/ IF ( OUT_BILL_NUMBER IS NULL ) THEN-- 3.1 如果票號為空,繼續執行; /*3.1.1 查詢票據主表*/ SELECT T.ORGCODE, T.IVCCODE, T.CURRENTNO, T.ENDNO INTO V_ORG_CODE, OUT_BILL_CODE, OUT_BILL_NUMBER, V_END_NUMBER FROM CZ_EINVOICES T WHERE T.ORGCODE = V_ORG_CODE AND T.IVCTYPE = V_BILL_TYPE AND T.MEDICALTYPE = V_MEDICAL_TYPE;-- 當前號與最大號比對:當前號>最大號 IF ( OUT_BILL_NUMBER IS NOT NULL AND OUT_BILL_NUMBER <= CAST( V_END_NUMBER AS SIGNED ) ) THEN-- 3.1.2 如果票號非空,繼續執行; /*3.1.2.1 將票據資訊插入中間表*/ INSERT INTO VOUCHER_NUMBER ( ORGCODE, INVOICINGCLERK, MEDICALTYPE, BILLTYPE, BILLCODE, BILLNUMBER, `STATUS` ) VALUES ( V_ORG_CODE, NULL, V_MEDICAL_TYPE, V_BILL_TYPE, OUT_BILL_CODE, OUT_BILL_NUMBER, 1 ); /*3.1.2.2 更新主表當前票號*/ UPDATE CZ_EINVOICES SET CURRENTNO = OUT_BILL_NUMBER + 1 WHERE ORGCODE = V_ORG_CODE AND IVCCODE = OUT_BILL_CODE -- 不同票據型別的票據程式碼都不相同,所以可以不用票據型別和醫療型別當作聯合限定條件 AND CURRENTNO = OUT_BILL_NUMBER; /*3.1.2.3 返回結果*/ SELECT OUT_BILL_CODE, OUT_BILL_NUMBER FROM DUAL; ELSE -- 3.1.3 否則,直接返回空票號(說明該機構沒有對應的票據資訊或當前號>最大號)。 SELECT OUT_BILL_CODE, OUT_BILL_NUMBER FROM DUAL; END IF; ELSE -- 3.2 否則,直接返回該票號。 /*3.2.1 將中間表,當前票號狀態改成1*/ UPDATE VOUCHER_NUMBER SET `STATUS` = 1 WHERE ORGCODE = V_ORG_CODE AND BILLCODE = OUT_BILL_CODE AND BILLNUMBER = OUT_BILL_NUMBER; /*3.2.2 返回結果*/ SELECT OUT_BILL_CODE, OUT_BILL_NUMBER FROM DUAL; END IF; END
當我再次開啟儲存過程的時候,也許會出現這種:
這個位置出現的是資料庫使用者。
在建立儲存過程的時候,紅框裡的程式碼是可以刪掉的,並不會影響儲存過程的建立;
另外,在oracle中習慣使用CREATE OR REPLACE PROCEDURE,但是,mysql建儲存過程的時候,不能加"OR REPLACE";
definer這個值並不會限制函式和儲存過程被呼叫的許可權,但會限制函式和儲存過程訪問資料庫的許可權;
函式和儲存過程在訪問資料庫時,會獲取definer使用者對應的資料庫訪問許可權。
臨時表
在mysql中,查詢也可以使用臨時表 DUAL的。
既可以不加“from dual”;
也可以加“from dual”。
寫在最後
哪位大佬如若發現文章存在紕漏之處或需要補充更多內容,歡迎留言!!!