数据库编程规范
1.1??????????? ? 前言
目前在軟件圈內有這么一個現象,就是:DBA 不太懂寫PL/SQL ,而開發人員寫的又是五花八門,而且效率不高。如此以來,造成諸多弊端:
1. 可讀性差。讀別人寫的一個程序花費的時間,比自己寫一個程序的花費時間還要長;非但別人看不懂,時間久了連自己也看不懂了。
2. 可維護性差。程序越寫越長,越改越爛,像懶婆娘的裹腳布,又臭又長。
3. 可移植性差。今天用oracle 寫一套,明天換成SQL Server 的時候再寫一套,眾多的數據庫開發人員在程序的苦海中重復著低級勞動……
4. 效率和性能差。一個存儲過程或SQL 執行效率簡直可以讓你感覺到對時間的絕望,你很快就理解什么是相對論了。
1.2??????????? ? 編程規范概述
??? 事實上為了統一軟件開發過程中關于數據庫設計時的命名規范和編程規范,正規一些的IT 公司都會制定一些關于數據庫對象的命名和編程規范。
否則的話,你寫你的我寫我的,各自為戰不兼容,彼此看不懂,甚至到最后連自己都弄不明白了,這樣的經歷相信每個人都曾經遇到過。
例如下面這段簡單的代碼,您看的明白嗎?就算暫時明白,過個一年半載您還明白嗎?就算您記憶超群,想必也會忘記,不是么?
A:=1;b:=2;SeLeCT username from EmPLOyee where id=a aNd Type=b;
如果你改為下面這樣的寫法,相信稍微懂點數據庫的人都應該看的明白不是?
vID:=1;?? --A 為ID
vType=2;? --B 為類型
SELECT username FROM employee WHERE id=vID AND type=vType;
1.3??????????? ? 書寫規范
丑陋的書寫規范不僅可讀性較差,而且給人以敬而遠之的感覺,就是您是大俠也不行啊;而良好的書寫規范則給人以享受和藝術的體驗。
1.3.1 大小寫風格
規則1.3.1.1
所有數據庫關鍵字和保留字均使用大寫;關于字段、變量的大小寫風格在1.4 詳細介紹。
1.3.2 縮進風格
規則 1.3.2.1
程序塊嚴格采用縮進風格書寫,保證代碼清晰易讀,風格一致,縮進格數統一為2 /4 個。
必須使用空格,不允許使用TAB 鍵。以免用不同的編輯器閱讀程序時,因TAB 鍵所設置的空格數目不同而造成程序布局不整齊。
規則1.3.2.2
同一條語句需要占用多于一行時,每行的其它關鍵字與第一行的關鍵字進行右對齊。
IF flag=1 THEN
? SELECT username???????????? -- 同上一行相比縮進4 個空格
??? INTO vuserinfo??????????? --INTO 與SELECT 進行右對齊
??? FROM userinfo???????????? --FROM 與SELECT 進行右對齊
?? WHERE userid=:iuserid;???? --WHERE 與SELECT 進行右對齊
END IF;
1.3.3 空格及換行
規則1.3.3.1
不允許把多個語句寫在一行中,即一行只寫一條語句。
規則1.3.3.2
避免將復雜的SQL 語句寫到同一行,建議要在關鍵字和謂詞處換行。
規則1.3.3.3
相對獨立的程序塊之間必須加空行。
BEGIN 、END 獨立成行
規則1.3.3.4
太長的表達式應在低優先級操作符處換行,操作符或關鍵字放在新行之首。劃分出新行應當適當地縮進,使排版整齊,語句可讀。
不同類型的操作符混合使用時,建議使用括號進行隔離,以使代碼清晰。
規則1.3.3.5
減少控制語句的檢查次數,如在 IF…ELSE 控制語句中,對最常用符合條件,盡量前置以被檢查到。
DECLARE
? -- 定義局部變量
? vFlag VARCHAR2(10);? -- 判斷標志
? …
BEGIN??
? IF ((a=b AND a=c AND a=d) OR? -- 在OR 處斷行,可使得邏輯更為清晰
????? (a=e AND e=f)) THEN
--Process something
? IF vFlag=1 THEN????? --vFlag=1 為經常出現之條件,可有效減少判斷檢查次數
--Process something
? ELSIF vFlag=2 THEN? --vFlag=2 為次之出現的條件
--Process something
? ELSE
?? --Process something
? END IF;
1.3.4 其它
規則 1.3.4.1
避免使用SELECT * 語句;不要用* 來代替所有字段,應給出字段列表,以避免表結構發生變化時應用程序出現無法識別的情況。
規則 1.3.4.2
INSERT 語句必須給出字段列表,以避免表結構發生變化時發生編譯錯誤。
規則 1.3.4.3
當一個PL/SQL 或SQL 語句中涉及到多個表時,始終使用別名來限定表名和字段名,這使其它人閱讀起來更方便,避免了含義模糊的引用,并能夠別名中清晰地判斷出表名和相關字段名。
規則 1.3.4.4
確保變量和參數在類型和長度與表數據列類型和長度相匹配。說明:如果與表數據列寬度不匹配,則當較寬或較大的數據傳進來時會產生運行異常。
DECLARE
? -- 定義相關表字段變量
? vDeptNo????? salary.Deptno%type;???? --not VARCHAR2(10) ,以適應變化
? vEmployeeNo? salary.EmployeeNo%type; --not VARCHAR2(10) ,以適應變化
? vSalary????? salary.Salary%type;???? --not NUMBER ,以適應變化
BEGIN
? --Process something
END;
1.4??????????? ? 命名規范
??? 一千個讀者就有一千個哈姆雷特,對于命名規范來說,想做到完全統一的確是不可能的任務。命名規范更多的是個人層面的愛好,就算有命名規范,也不過是體現制訂規范的相關人的愛好而已。
因此即使無法完全做到一致,但是我們仍然要盡量去遵守,必要的時候需要通過代碼檢查和專家評審來進行約束,因為一個不成熟的規范總會勝過沒有規范。
1.4.1 表和字段命名規范
在此僅提供幾種常見的命名方法( 表和字段的命名方式雷同) 。
以用戶權限字段/ 表為例:
| UserPrivilege | 適合那些英文比較好,并且喜歡抑揚頓挫和有藝術美感的人。 |
| userprivilege | 適合那些英文好,且比較嚴謹的人,畢竟全部小寫很容易與數據庫關鍵字相區別。 |
| tbl_user_privilege | 適合那些做開發的人,開發的人會習慣性的給變量加前綴。 ( 這里指表的命名,字段一般很少加前綴) |
| yhqx | 熱愛中文的人,前提是恐怕您得對這些縮寫先做好相關備注,等大家習慣了才行。 |
實際上這幾種命名規范各有千秋,很難去指責或否定哪種更好,完全取決于整個公司多數人的習慣,記住沒有十全十美的命名規范,只有絕大多數人心甘情愿的去遵從了,那就是好的命名規范。
就我個人而言,我更偏向于第一種命名習慣。
規則 1.4.1.1
不建議使用數據庫關鍵字和保留字(不建議并不意味著不能使用),只是為了避免不必要的沖突和麻煩;
例如name,id,level,remark,description 等等。
有興趣的話,大家可以參考下SELECT * FROM v$reserved_words WHERE reserved='Y'
實際上oracle 不建議大家使用v$reserved_words 表中所有的關鍵字,無奈這些關鍵字太多了;reserved='Y' 的關鍵字則是被完全禁止的。
規則 1.4.1.2
嚴禁使用帶空格的名稱來對字段和表命名;在產生數據庫腳本并重新加載的時候可能會出現意想不到的錯誤而被迫終止。
1.4.2 其它對象命名
用戶自定義的數據庫對象名包括表、視圖、主外鍵、索引、觸發器、函數、存儲過程、序列、同義詞、數據庫鏈接、包和包體等等。
規則 1.4.2.1
其它對象的命名也與表和字段的命名規則類似,風格保持一致即可
規則 1.4.2.2
除數據庫名稱長度為1 -8 個字符,其余為1 -30 個字符,database link 名稱也不要超過30 個字符;
命名只能使用英文字母,數字和下劃線
規則 1.4.2.3
除表外,其它各種對象的命名最好用不同的前綴加以區別。采用前綴的方式來命名對象則很容易通過排序對對象進行區別。
如在命名規范中各組成部分以_ 分割,則前綴建議也以_ 分割;反之則可加可不加
| 對象名 | 前綴 | 范例 |
| 表(table) | tbl_/t_ ( 或不加前綴) | userinfo/t_user_info/ tbl_user_info |
| 視圖(view) | v_/v | v_user_info/vuserinfo |
| 序列(sequence) | seq_ | seq_user_info |
| 簇(cluster) | c_ | c_user_info |
| 觸發器(trigger) | trg_ | trg_user_info |
| 存儲過程(procedure) | sp_/p_ | sp_user_info/p_user_info |
| 函數(function) | f_/fn_ | fn_user_info/f_user_info |
| 物化視圖 (materialized view) | mv_ | mv_user_info |
| 包和包體 (package & package body) | pkg_ | pkg_user_info |
| 類和類體 (type & type body) | typ_ | typ_user_info |
| 主鍵(primary key) | pk_ | pk_user_info |
| 外鍵(foreign key) | fk_ | fk_user_info_fieldname |
| 唯一索引(unique index) | uk_ | uk_user_info_fieldname |
| 普通索引(normal index) | idx_ | idx_user_info_fieldname |
| 位圖索引(bitmap index) | bk_ | bk_user_info_fieldname |
| 同義詞(synonym) | 依據于所 分配的表所屬模塊/ 模式 | |
| 數據庫鏈接(database link) | 無特殊要求 |
1.5??????????? ? 變量命名
規則1.5.1
所有PL/SQL 中的變量與對象命名規則相似
| 變量類型 | 前綴 | 范例 |
| 輸入變量 | i_/i | i_user_id/iuserid |
| 輸出變量 | o_/o | o_user_name/ousername |
| 輸出輸入變量 | io_/io | io_user_name/iousername |
| 普通變量 | v_/v | v_user_id/vuserid |
| 全局變量 | gv_/gv | gv_user_id/gvuserid |
| 常量 | 大寫 | PI |
| 游標 | cur_ | cur_userinfo |
| 用戶自定義類型 | type_ | type_user_info |
| 保存點(save point) | spt_ | spt_user_info |
規則1.5.2
命名不允許使用中文或者特殊字符。
命名中若使用特殊約定或縮寫,則要注釋說明。
規則1.5.3
使用有意義、易于記憶、描述性強、簡短及唯一的英文單詞/ 拼音縮寫。自己特有的命名風格,要自始自終保持一致,不可來回變化。
說明:個人命名風格,在符合所在項目組的命名規則的前提下,才可以使用。
規則1.5.4
對于變量命名,禁止取單個字符( 如i 、j … ) ,建議除了要有具體含義外,還能表明變量類型等。
說明:變量,尤其是局部變量,如果用單個字符表示,很容易敲錯( 如i 寫成j) ,而編譯時又檢查不出來,有可能為了這個小小的錯誤而花費大量的時間。
1.6??????????? ? 注釋規范
注 釋規范是判斷一個開發人員優劣和成熟度的重要指標。一個優秀的研發人員必然是經過深思熟慮然后才洋洋灑灑妙筆生花的,注釋的書寫體現了一個人思考問題的全 過程和步驟;話又說回來,就算代碼寫的爛,只要注釋寫的好,至少也會給人以良好的感覺;同時也能造福后人,不是么?呵呵。
規則1.6. 1
一般情況下,源程序有效注釋量必須在30% 左右。
說明:注釋的原則是有助于對程序閱讀理解,在該加的地方都加了,注釋不宜太多也不能太少,注釋語言須準確、易懂、簡潔、精煉。
規則1.6. 2
統一文件頭的注釋.
主要是對相關過程、函數進行功能性描述、修訂記錄、以及入參出參說明
對存儲過程、函數的任何修改,都需要在注釋后添加修改人、修改日期及修改原因等修訂說明。
/***********************************************************
名稱: sp_xxx
功能描述:
修訂記錄:
版本號??? 編輯時間??? 編輯人? 修改描述
1.0.0???? 2010-01-01? John??? 1 、創建此存儲過程
1.0.1???? 2010-02-01? Sandy?? 2 、增加傳入參數
入參出參描述:
iparameter1??????? IN VARCHAR2(20)? 傳入參數1
iparameter2??????? IN VARCHAR2(20)? 傳入參數2
iparameter1??????? OUT VARCHAR2(20)? 傳入參數1
iparameter2??????? OUT VARCHAR2(20)? 傳入參數2
返回值描述:( 主要針對函數)
? 0 - Success
? 1 - normal fail
***********************************************************/
規則1.6. 3
所有變量定義需要加注釋,說明該變量的用途和含義。
規則1.6. 4
注釋內容要清晰、明了、含義準確,防止注釋二義性
在代碼的功能、意圖層次上進行注釋,提供有用、額外的信息。
避免在一行代碼或表達式的中間插入注釋。
盡量使用”-- ”進行注釋;行尾注釋須使用”-- ”。
規則1.6. 5
對程序分支必須書寫注釋。
說明:這些語句往往是程序實現某一特定功能的關鍵,對于維護人員來說,良好的注釋幫助更好的理解程序,有時甚至優于看設計文檔。
在程序塊的結束行右方加注釋,以表明程序塊結束。
規則1.6. 6
注釋應與其描述的代碼相似,對代碼注釋應放在其上方或右方( 對單條語句的注釋) 相近位置,不可放在下面。
注釋與所描述的內容進行同樣的縮排。
注釋上面的代碼應空行隔開。
建議1.6. 7
注釋用中文書寫
有一次,同事寫了一個900 行的存儲過程,里面定義了十幾個游標以進行遍歷,這個存儲過程缺乏注釋,執行一次居然要一天一夜,已經達到了無法容忍的地步。
??? 因為缺乏注釋,我花了整整一天的時間來對該存儲過程進行分析,然后用了半天時間來進行改寫和調試。
其實很簡單定義,我定義了一些對應的臨時表,把游標遍歷替換成SQL 的集合操作,把整個的一個大事務分割成若干小事務,只是修改了部分代碼,結果執行時間就變成了短短的3 分鐘。
當然游標也并非不可觸及的,既然存在就有他存在的理由。
1.7??????????? ? 語法規范
??? 良好的語法規范有助于書寫出高效、完備的PL/SQL 程序,同時有助于提高系統的容錯性、健壯性、可追溯性。
規則1.7 .1
避免隱式的數據類型轉換。
說明:在書寫代碼時,必須確定表的結構和表中各個字段的數據類型,特別是書寫查詢條件時的字段就更要注意了。這個是導致SQL 性能不佳常犯的錯誤之一。
規則1.7 .2
為了方便不同的數據庫平臺的移植,盡量使用SQL99 標準,而不要使用Oracle 的方言。
例如:DECODE 函數完全可以用CASE WHEN 語句代替,而且可編程性更強。
(+)= 右關聯用RIGHT OUTER JOIN 語句代替。
=(+) 左關聯用 LEFT OUTER JOIN 語句代替。
規則1.7 .3
對于非常復雜的SQL( 特別是多層嵌套,帶子句或相關的查詢) ,應該先考慮是否設計不當引起的,對于復雜的一些SQL 可以考慮使用程序實現,原則上遵循一句話只做一件事情。
關于處理的優先級
1、? 靜態SQL> 動態SQL
2、? 綁定變量的SQL> 動態SQL (在OLTP 系統中建議這么做)
3、? SQL>PL/SQL 的過程,極端復雜的SQL 除外
4、? SQL> 游標遍歷
5、? Oracle 函數> 自定義函數
6 、盡量使用Oracle 分析函數代替同一個表多次的關聯。
規則1.7 .4
原則上不要使用動態SQL ,如果非得使用動態SQL ,建議使用綁定變量。
規則1.7 .5
一定要及時關閉和釋放游標
規則1.7 .6
建議在異常處理中,把收集到錯誤信息記入錯誤日志表,以備查詢和分析。
CREATE OR REPLACE PROCEDURE sp_increament_xxx
/***********************************************************
名稱: sp_increament_xxx
功能描述:xxx 表/ 模塊數據增量更新,錯誤原因分析通過tbl_task_table 日志表
?
修訂記錄:
版本號??? 編輯時間??? 編輯人? 修改描述
1.0.0???? 2010-05-01? John??? 1 、創建此存儲過程
1.0.1???? 2010-06-01? Sandy?? 2 、更新xxx 字段在xxx 處
?
入參出參描述:
? N/A
?
返回值描述:( 主要針對函數)
? N/A?
***********************************************************/
AS
??? v_err_num NUMBER;
??? v_err_msg VARCHAR2(100);
??? v_begin_date DATE;
??? v_end_date DATE;
BEGIN
?
??? v_err_num:=0;
??? v_err_msg:='';
?
??? -- 某表增量更新步驟
??? BEGIN
??????? SAVEPOINT spt_xxx;
?
??????? -- 從任務表中獲取更新初始時間
??????? SELECT lasttime INTO v_begin_date FROM tbl_task_table T
???????? WHERE id='sp_increament_xxx';?
??????? -- 從源數據表中獲取更新最后時間
??????? SELECT MAX(oper_date) INTO v_end_date FROM tbl_table_source;??
???? ??? -- 為提高執行效率,將增量數據寫入臨時表中
??????? INSERT INTO tmp_tbl_table_source
??????????? (fieldname1,fieldname2,fieldname3,
???????????? fieldname4,fieldname5,fieldname6)
??????? SELECT
???????????? fieldname1,fieldname2,fieldname3,
???????????? fieldname4,fieldname5,fieldname6
????????? FROM tbl_table_source sourcetable
???????? WHERE sourcetable.create_date > v_begin_date
?????????? AND sourcetable.create_date <= v_end_date;
??????? -- 再講增量數據從臨時表更新到最終目標表
??????? MERGE INTO tbl_table_original_dest desttable
????? ?? USING tmp_tbl_table_source tmptable
?????????? ON (desttable.primarykey = tmptable.primarykey) -- 匹配判斷標準,根據主鍵判斷
???????? WHEN MATCHED THEN????????????????????????????????? -- 如果已存在,更新原紀錄
???????? UPDATE SET desttable.fieldname1 = tmptable.fieldname1,
???? ??????????????? desttable.fieldname1 = tmptable.fieldname1,
??????????????????? desttable.fieldnamem = tmptable.fieldnamem,
??????????????????? desttable.fieldnamen = tmptable.fieldnamen
???????? WHEN NOT MATCHED THEN????????????????????????????? -- 如果不存在,插入新紀錄
???????? INSERT (fieldname1,fieldname2,fieldname3,
???????????????? fieldname4,fieldname5,fieldname6)
???????? VALUES (tmptable.fieldname1,tmptable.fieldname2,tmptable.fieldname3,
???????????????? tmptable.fieldname4,tmptable.fieldname5,tmptable.fieldname6);??
??????? -- 更新任務表相應的狀態、時間???????????
??????? UPDATE tbl_task_table
?????????? SET lasttime_=v_end_date,status='SUCCESS'
???????? WHERE id='sp_increament_xxx';
??????? COMMIT;
?
??? -- 異常處理,把錯誤記入相關日志表,可以及時找到錯誤原因并進行分析。
??? EXCEPTION
??????? WHEN OTHERS THEN
??????????? ROLLBACK TO SAVEPOINT spt_xxx;
??????????? v_err_num := SQLCODE;
??????????? v_err_msg := SUBSTR(SQLERRM, 1, 100);
??????????? UPDATE tbl_task_table
?????????????? SET lasttime_=v_begin_date,status='FAIL'
???????????? WHERE id='sp_increament_xxx';
??????????? COMMIT;
??? END;
?
??? -- 某表增量更新步驟
??? BEGIN
??????? ...
??? END;
?
END;
本例為數據庫定時調用存儲過程,同時也只是為了演示存儲過程的全部過程;對于常用的由客戶端調用的存儲過程,建議不要捕獲異常,而由客戶程序進行直接處理。
規則1.7 .7
不要將空的變量值直接與比較運算符( 符號) 比較。如果變量可能為空,應使用IS NULL 或IS NOT NULL 或NVL 函數進行比較。
規則1.7 .8
盡可能地使用相關表字段的類型定義,形如%TYPE 、%ROWTYPE 。這樣做當表結構發生變動的時候,能夠最大程度的做到容錯性和健壯性。
規則1.7 .9
存儲過程中變量的聲明應集中在AS 和BEGIN 關鍵字之間,不允許在代碼中隨意定義變量,定義變量時,完成相同功能模塊的變量應放在一起,與不同模塊的變量應空行隔開,增加代碼的可讀性。
1.8??????????? ? 腳本規范
??? 腳本規范有助于進行版本基線的管理、版本控制,也有助于系統的自動部署、定位和解決部署過程中出現的問題。
規則 1.8.1
所有腳本按分類或內容分開存放,并按以下順序存儲:
1.?????? 創建數據庫角色、用戶腳本
2.?????? 創建數據庫表空間、數據文件腳本
3.?????? 創建數據類型腳本,自定義的數據類型
4.?????? 創建業務表腳本,表是其他依賴關系的基礎
5.?????? 創建臨時表腳本,可能會在過程腳本中用到
6.?????? 創建視圖腳本
7.?????? 創建主外鍵腳本
8.?????? 創建索引腳本
9.?????? 創建觸發器腳本
10.??? 創建函數、存儲過程腳本
11.??? 初始化數據腳本
12.??? 創建作業腳本
規則1.8.2
創建每個對象代碼的首部應該有對象注釋
規則1.8.3
每個函數、存儲過程應單獨創建腳本,在配置庫上按照功能模塊存放到不同的目錄下。
并在相應的目錄下,創建一個運行所有腳本的總腳本轉載于:https://www.cnblogs.com/hanmos/archive/2011/03/27/1997025.html
總結
- 上一篇: 怎么在泰山上体验不同的登山路线?
- 下一篇: 三种 SQL 执行语句