1. 程式人生 > 實用技巧 >mysql 儲存過程 示例

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”。

寫在最後

  哪位大佬如若發現文章存在紕漏之處或需要補充更多內容,歡迎留言!!!

相關推薦: