俗話說事不預則廢,無規矩不成方圓。sql
對sql腳本程序的設計,我的認爲應該是從編碼規範開始。網絡
前段時間公司一些同事提交的腳本,風格迥異,讓我審覈起來倍感難受,絲毫沒有審覈代碼的快感。oracle
特整理了公司部分常規的編碼規範,對新人進行了培訓,但願同志們寫的代碼更美觀和高效(注:規範部分收集於網絡,感謝網絡大蝦們的貢獻):app
1. 不管是 PL/SQL 對象仍是 PL/SQL 對象內部用的變量和遊標等,都必須能從名稱上能讓人理解變量和遊標的含義,並且必定要作好註釋。函數
2. 傳到 CVS 上的文本文件所有采用 ’.sql’ 做爲文件名的後綴oop
存儲過程的命名必須符合USP_DETAILNAME 格式,其中 USP 表示是存儲過程,DETAILNAME 是與存儲過程意義相關的意義的名稱,如:USP_BUSINESS_RULE 。測試
軟件包的命名必須符合PKG_ DETAILNAME 格式,其中 PKG 表示是軟件包, DETAILNAME 是與軟件包意義相關的意義的名稱,例如:PKG_SORCE_ALL 。fetch
觸發器的的命名必須符合TRG_DETAILNAME 格式,其中TRG 表示是觸發器,DETAILNAME 是與觸發器用途相關的意義的名稱,具體能夠參考已有的TRIGGER 對象。編碼
函數的命名必須符合FNC_DETAILNAME 格式,其中FNC 表示是函數,,DETAILNAME 是與函數意義相關的意義名稱,例如:FNC_SALARYCOUNT 。設計
1. 變量的命名必須符合規約, 請採用 」 XXX_variablename 」 開頭, 其中XXX 表示變量的類型。Variablename 表示變量名,如爲函數傳入參數請根據傳入類型加上IN,OUT,INOUT 等,如: IN_NUM_CUST_CODE.
2. 若是依據表的字段請採用%type 或者%rowtype 方式。請不要直接定義變量。
如: NUM_CUST_CODE TableName.CUST_CODE%type
3. 顯示遊標請採用 」 CUR_ 」 開頭,在代碼開頭請說明,同時說明遊標的定義.
如:CUR_CUST_INFO
4. 全部的變量前必須帶上變量類型的標誌,NUMBER 型採用NUM,VARCAHR2 採用VAR ,而在函數等入口的傳入方法IN_VAR_CUST_NAME,OUT_NUM_CODE,INOUT_NUM_CODE 的方式來區分變量的類型是否位IN,OUT,INOUT 類型
5. 不能超過30 個字符,不能採用關鍵字,要以字母開頭。
注: 以上命名規則將主要在於新建對象時使用,若是在原有的PL/SQL 對象的基礎上修改,請按照原來代碼的命名規範,注意作好代碼的註釋。同時必須在PL/SQL 代碼開頭,作好註釋說明。
PL/SQL 的編碼規範包括:
² 註釋
² 變量命名
² 書寫格式
² 邏輯分支
² 循環處理
1. 請在全部程序一開始處嚴格按以下格式寫出註釋塊:
-- *******************************************
-- 過程名:
-- 功能描述:
-- 輸入參數說明:
-- 輸出參數說明:
-- 調用的過程或函數:
-- 建立人員:
-- 建立日期:
-- 修改人員:
-- 修改日期:
-- 修改緣由:
-- 修改結果:
-- 版本說明:
-- ********** *********************************
2. 一般在PL/SQL 塊的declare ,begin ,exception 和end 部分設置分隔線和註釋;
3. 每一個變量都須要加上變量的註釋,說明變量的用途;
4. 請在重要的程序段和難懂的程序段加上分隔線和註釋;
5. 請註明遊標等的用途和用法
6. 註釋量與程序量的總比例儘可能作到1 :1 。
變量取名請 遵照命名規範,對使用頻繁、關鍵變量,爲了便於 閱讀和修改, 請在定義時加上註釋標明其含義。
爲了便於閱讀和調試,儘可能少用單字母變量,禁止使用諸如i 、l 等做爲變量名,同時注意小寫字母l 和數字1 之間的區別使用。並嚴禁使用關鍵字,要符合ORACLE 的命名規範。
1. 刪除 在刪除代碼先後上 /*deleted by yourname on yyyy/mm/dd start*/ 和 /*deleted by yourname on yyyy/mm/dd end*/ 同時請註明刪除的緣由。
2. 修改 將原有的代碼所有註釋(刪除)掉,在最後說明註釋的緣由,同時將你新增長的代碼寫在註釋後,首先加上 /*modified by yourname on yyyy/mm/dd start*/ 和 /* modified by yourname on yyyy/mm/dd end*/須要保證
a. 全部被註釋的代碼都是原來的代碼
b. 全部沒有被註釋的代碼都是新增長的代碼
3. 增長,在增長的代碼前加後 /*added by yourname on yyyy/mm/dd start*/ 和 /*added by yourname on yyyy/mm/dd end*/ 同時請註釋說明,增長的緣由等。
爲了便於閱讀和調試,儘可能少用單字母變量,禁止使用諸如i 、l 等做爲變量名,同時注意小寫字母l 和數字1 之間的區別使用。
請注意,必須在PL/SQL 代碼的開頭處,詳細說明你所作的修改,包括修改了那些地方,修改的緣由,修改的日期,修改人等。
1. 用語句分層縮進的寫法顯示嵌套結構的層次;
2. 在註釋段與程序段、以及不一樣程序段插入空行;
3. 每行只寫一條語句。
邏輯分支的格式以下:
l IF……THEN
……
ELSE
……
END IF;
l CHOOSE CASE
CASE 1 …
……
CASE 2 …
……
CASE ELSE …
……
CASE END;
循環處理的格式以下:
l FOR …… LOOP
……
END LOOP;
l WHILE ……LOOP
……
END LOOP;
l DECLARE
CURSOR cursor_name IS
……
(SQL STATEMENT FOR THER CURSOR)
BEGIN
FOR variable_name IN cursor_name LOOP
…
(STATEMENT)
END LOOP;
END
遊標的自己就是一個SQL 的工做區,用於處理多行或單行的查詢處理,主要分爲以下兩類
1. implicit cursor 由DML 和PL/SQL 的SELECT 隱式的定義,不能使用FETCH,OPEN,COLSE 等來控制SQL 遊標,但可使用遊標的屬性, 如select xxx into xxx from xxx.
2. explicit cursor 主要由程序控制, 用於顯示返回一行或者多行數據。
執行的四個步驟
:
1.
聲明:定義遊標的名字和結構,select 中可使用order by
2. 打開遊標:執行查詢同時綁定全部涉及到的變量
執行的內容:
爲select 分配內存並分析select 語句
綁定輸入的變量
配置指針在活動集的第一行
注意: 若是 查詢不返回結果,不會引起PL/SQL 的異常,你能夠在執行fetch 後測試返回的結果
若是遊標內的聲明包括update 。同樣會執行行鎖定
3.Fetch :把當前行的值賦給變量,每一個fetch 都會把遊標指針向下移動一行。
若是到了最後一行就會自動退出for loop
4. 關閉:釋放活動的集, 能夠再次使用open
遊標的幾個屬性:
SQL%ROWCOUNT 受最近執行的SQL 語句影響的行的數目。(一個整數值)
SQL%FOUND Boolean 屬性,若是最近的SQL 語句影響了一行或多行,其值爲
TRUE 。
SQL%NOTFOUND Boolean 屬性,若是最近的SQL 語句沒有影響任何行,其值爲
TRUE 。
SQL%ISOPEN 老是爲FALSE ,緣由是PL/SQL 老是它們結束執行後當即關閉內隱遊標。
例子
EG1: 常規用法
CURSOR c1 IS
SELECT empno, ename
FROM emp;
emp_record c1%ROWTYPE;
BEGIN
OPEN c1;
. . .
FETCH c1 INTO emp_record;
EG2: 使用for 循環實現遊標
DECLARE
CURSOR c1 IS
SELECT empno, ename
FROM emp;
emp_record c1%ROWTYPE;
BEGIN
FOR emp_record IN c1 LOOP
-- implicit open and implicit fetch occur
IF emp_record.empno = 7839 THEN
...
END LOOP; -- implicit close occurs
END;
遊標FOR 循環不須要FETCH 語句的。遊標打開,在循環中每次重複提取一行,全部的行都處理後,遊標會自動關閉。
EG3: 不定義遊標的方式
BEGIN
FOR emp_record IN ( SELECT empno, ename
FROM emp) LOOP
-- implicit open and implicit fetch occur
IF emp_record.empno = 7839 THEN
...
END LOOP; -- implicit close occurs
END;
EG1: 帶變量的遊標
你必須指定指定參數的數據類型,但不用指定大小
DECLARE
CURSOR c1
(v_deptno NUMBER, v_job VARCHAR2) IS
SELECT empno, ename
FROM emp
WHERE deptno = v_deptno
AND job = v_job;
BEGIN
OPEN c1(10, 'CLERK');
...
EG5:FOR UPDATE---- 當加上for update 則把整個表或字段鎖住了。
SELECT ... FROM ...
FOR UPDATE [OF column_reference ][NOWAIT]
DECLARE
CURSOR c1 IS
SELECT empno, ename
FROM emp
FOR UPDATE NOWAIT;
NOWAIT: 返回一個oracle 的錯誤信息若是此行給其餘的會話鎖住了。
EG6 :WHERE CURRENT OF
用於在遊標中刪除和更新當前行
必須使用 FORUPDATE 去鎖住行
使用WHERE CURRENT OF 去指向當前的行
DECLARE
CURSOR c1 IS
SELECT ...
FOR UPDATE NOWAIT;
BEGIN
...
FOR emp_record IN c1 LOOP
UPDATE ...
WHERE CURRENT OF c1;
...
END LOOP;
COMMIT;
END;
1.2. 8 異常處理
PL/SQL 的異常主要分爲三大類
1. Predefined Exception 異常
2. Non-Predefined Exception 異常
3. User Defined Exception 異常
其中1,2 將隱式raised ,3 須要顯示raised
以下例子
Predefined Exception
BEGIN SELECT ... COMMIT;
EXCEPTION
WHEN NO_DATA_FOUND THEN
statement1;
statement2;
WHEN TOO_MANY_ROWS THEN
statement1;
WHEN OTHERS THEN
statement1;
statement2;
statement3;
END;
…….
Non-Predefined Exception
DECLARE
e_products_invalid EXCEPTION;
PRAGMA EXCEPTION_INIT (
e_products_invalid, -2292);
v_message VARCHAR2(50);
BEGIN
. . .
EXCEPTION
WHEN e_products_invalid THEN
:g_message := 'Product code
specified is not valid.';
. . .
END;
User-Defined Exception
DECLARE
e_amount_remaining EXCEPTION;
. . .
BEGIN
. . .
RAISE e_amount_remaining;
. . .
EXCEPTION
WHEN e_amount_remaining THEN
:g_message := 'There is still an amount
in stock.';
. . .
END;
RAISE_APPLICATION_ERROR
DECLARE
…….
Invalidpart EXCEPTION;
BEGIN
…….
IF SQL%NOTFOUND THEN
RAISE invalidpart;
END IF;
EXCEPTION
WHEN invalidpart THEN
Raise_application_error(-20003,’Invalid Part id #’|| partnum);
WHEN OTHERS THEN
Raise_application_error(-20000,errNum||errMsg);
END
² 使用EXCEPTION 關鍵字在一個PL/SQL 塊的聲明部分聲明用戶自定義異常
² 使用PL/SQL 命令RAISE 檢測用戶自定義異常
² PL/SQL 可以使用Raise_Application_Error 過程返回一個用戶自定義錯誤數和消息給調用環境。全部的用戶自定義錯誤消息必須在-20000 到-20999 之間
² PL/SQL 程序可使用WHEN OTHERS 異常處理來處理沒有特定處理的全部異常,WHEN OTHERS 必定放在異常處理的最後
² PL/SQL 程序可使用特殊的SQLCODE 和SQLERRM 函數返回oracle 內部錯誤號碼和消息
² SQLCODE SQLERRM ,SQLCODE 返回一個NUMBER 型的錯誤類型,而SQLERRM 將返回錯誤類型相關的錯誤信息描述。
DECLARE
v_error_code NUMBER;
v_error_message VARCHAR2(255);
BEGIN
...
EXCEPTION
...
WHEN OTHERS THEN
ROLLBACK;
v_error_code := SQLCODE ;
v_error_message := SQLERRM ;
INSERT INTO errors VALUES(v_error_code,
v_error_message);
END;