MySQL 設計與開發規範
1 目的
本規範的主要目的是希望規範資料庫設計與開發,儘量避免由於資料庫設計與開發不當而產生的麻煩;同時好的規範,在執行的時候可以培養出好的習慣,好的習慣是軟體質量的很好保證。
2 適用範圍
本規劃的適用人員範圍包括涉及資料庫設計與開發的相關技術人員。
3 術語約定
本規範採用以下術語描述:
★規則:也稱為強規範是程式設計時必須強制遵守的原則
★建議:程式設計時必須加以考慮的原則
★說明:對此規則或建議進行必要的解釋
★示例:對此規則或建議從正、反兩個方面給出
4 規範及建議
4.1 書寫規範
4.1.1 SQL書寫規範
規則1: 資料庫程式碼中,關鍵字大寫,其他內容小寫;
示例:
如下程式碼不符合規範:(關鍵字未大寫)
select last_name ,job_id
from employees;
如下程式碼符合規範:
SELECT last_name, job_id
FROM employees;
規則2:程式塊應採用縮排風格書寫,保證程式碼可讀,風格一致,縮排格數統一為4格;
規則3:程式碼中需要空位時,統一採用英文空格鍵輸入,不允許用TAB鍵 產生空位;
說明:不同的編輯器對TAB的空位格數設定不一致,會導致使用TAB鍵產生空位的程式碼格式混亂;
規則4:同一條語句佔用多行時,每一行的開始應是關鍵字, 且關鍵字應和第一行左對齊,如確實不能從關鍵字分行,則分行處應對其上一行被分行的同類程式碼的最左邊;
示例:
如下程式碼不符合規範(分行書寫時,其餘行未和第一行左對齊)
SELECT last_name,
job_id
FROM employees;
如下程式碼也不符合規範(分行時,不是從關鍵字分行)
SELECT last_name,
job_id FROM employees;
如下程式碼符合規範
SELECT last_name, job_id
FROM employees;
如下程式碼符合規範
SELECT last_name,
first_name,
job_id
FROM employees;
規則5:查詢資料時,儘量不使用SELECT *,而是給出明確的欄位,但該規則不包括SELECT COUNT(*)語 句;
示例
如下語句不符合規範(SELECT操作未給出欄位)
SELECT *
FROM employees;
如下語句符合規範
SELECT last_name, first_name
FROM employees;
規則6:INSERT語句應該給出欄位列表;
示例
如下語句不符合規範(INSERT操作未給出欄位名稱)
INSERT INTO employees
VALUES
(
'GUO',
'DAVID',
100
);
如下語句符合規範
INSERT INTO employees
(
last_name,
first_name,
job_id
)
VALUES
(
'GUO',
'DAVID',
100
);
規則7:從表中同一筆記錄中獲取記錄的欄位值,須使用一SQL語句得到,不允許分多條SQL語句;
示例
如下語句不符合規範(從同一個表中取出記錄,分成兩條語句分別掃描)
UPDATE employees_new
SET last_name=
(
SELECT last_name
FROM employees
WHERE job_id = 100
)
WHERE job_id = 100;
UPDATE employees_new
SET first_name =
(
SELECT first_name
FROM employees
WHERE job_id = 100
)
WHERE job_id = 100;
如下語句符合規範
UPDATE employees_new
SET first_name =
(
SELECT last_name
FROM employees
WHERE job_id = 100
),
last_name =
(
SELECT first_name
FROM employees
WHERE job_id = 100
)
WHERE job_id = 100;
規則8:SQL語句中的逗號後面應增加一個空格,以使得程式碼清晰;
示例
如下程式碼不符合規範(逗號後面沒有空格)
SELECT last_name,job_id
FROM employees;
如下程式碼符合規則
SELECT last_name, job_id
FROM employees;
規則9:不允許將SQL語句寫成一行,再短的SQL也應該在謂詞處分行;
示例
如下程式碼不符合規範(未在謂詞部分進行分行)
SELECT last_name, job_id FROM employees WHERE job_id = 1;
如下程式碼符合規範
SELECT last_name, job_id
FROM employees
WHERE job_id = 1;
規則10:運算子以及比較符左邊或者右邊只要不是括號,則空一格;
示例
如下程式碼不符合規範(運算子沒有空格)
SELECT CURRENT_DATE+INTERVAL 1 DAY
FROM dual;
如下程式碼符合規範
SLEECT CURRENT_DATE + (INTERVAL 1 DAY)
FROM dual;
規則11:不同型別的操作符混合使用時,應使用括號明確的表達運算的先後關係;
示例
如下程式碼不符合規範(運算優先順序關係易混淆)
SELECT a*b/c+d*e
FROM dual;
如下程式碼符合規範
SELECT ((a * b) / c) + (d * e)
FROM dual;
規則12:任何SQL書寫單行不得超過120字元(含左邊的縮排);
建議1:對於INSERT…VALUES和UPDATE語句,一行寫一個欄位,每個欄位相對於INSERT語句空4格,欄位後面緊跟註釋(註釋語句左對齊),VALUES和INSERT左對齊,左括號和右括號與INSERT、VALUES左 對齊;
示例:
如下程式碼不符合建議(欄位未和INSERT語句空格)
INSERT INTO sm_user
(
user_id, --使用者ID,主鍵
user_name, --使用者名稱
login_name --登入名
)
VALUES
(
p_user_id,
p_user_name,
p_login_name
);
如下程式碼符合建議
INSERT INTO sm_user
(
user_id, --使用者ID,主鍵
user_name, --使用者名稱
login_name --登入名
)
VALUES
(
p_user_id,
p_user_name,
p_login_name
);
建議2:INSERT…SELECT 語句時,應使每行的欄位順序對應,以每行最多不超過4個欄位,以方便程式碼閱讀,括號的內容另起一行縮排4格開始書寫,關鍵字單詞左對齊,左括號、右括號另起一行與左對齊;
示例
如下程式碼不符合建議(欄位未和括號分行)
INSERT INTO sm_duty_bak(duty_id, duty_name, created_by, creation_date,
last_updated_by, last_update_date, disable_date)
SELECT duty_id, duty_name, created_by, creation_date,
last_updated_by, last_update_date, disable_date
FROM sm_duty
WHERE duty_id=88;
如下程式碼符合建議
INSERT INTO sm_duty_bak
(
duty_id, duty_name, created_by, creation_date,
last_updated_by, last_update_date, disable_date
)
SELECT
duty_id, duty_name, created_by, creation_date,
last_updated_by, last_update_date, disable_date
FROM sm_duty
WHERE duty_id = 88;
說明:
1.SELECT 語句中每行的欄位應與INSERT 語句對應。
2.INSERT 語句中換行的欄位名應縮排並與上一行的第一個欄位名對齊。
3.SELECT 語句中換行的欄位名應縮排並與上一行的第一個欄位名對齊。
4.1.2 儲存過程書寫規範
規則1:不允許將多行語句書寫在同一行;
示例
如下程式碼不符合規範(將兩行定義書寫在同一行)
SET v_count = 1; SET v_creation_date = CURRENT_DATE;
如下程式碼符合規範
SET v_count = 1;
SET v_creation_date = CURRENT_DATE;
規則2:相對獨立的程式塊之間應加空行;
示例
如下程式碼不符合規範(變數定義和程式段之間無空行)
SET v_duty_id = 1;
IF (v_disabled_date > v_current_date) THEN
SELECT duty_name
into v_duty_name
FROM sm_duty
WHERE duty_id = :duty_id;
…
END IF;
如下程式碼符合規範
SET v_duty_id = 1;
IF (v_disabled_date > v_current_date) THEN
SELECT duty_name
into v_duty_name
FROM sm_duty
WHERE duty_id = :duty_id;
…
END IF;
規則3:當一個SQL 語句中涉及到多個表時,始終使用別名來限定欄位名,這使其它人閱讀起來更方便,避免了含義模糊的引用,其中能夠通過別名清晰地判斷出表名;
說明 : 別名命名時,儘量避免使用無意義的代號a、b 、c… , 而應該有意義( 如表mtl_system_items_b 對應別名為msi,po_headers_all 別名對應為pha)。
示例
如下語句不符合規範(未使用有明確含義的表別名)
SELECT a.wip_entity_name, a.wip_entity_id, a.date_released
FROM wip.wip_entities b,
wip.wip_discrete_jobs a
WHERE b.wip_entity_id = a.wip_entity_id
AND a.status_type = 3
如下語句符合規範
SELECT wdj.we_entity_name, wdj.wip_entity_id, wdj.date_released
FROM wip.wip_entities we,
wip.wip_discrete_jobs wdj
WHERE we.wip_entity_id = wdj.wip_entity_id
AND we.status_type = 3
規則4:確保變數/引數的型別和長度與表資料欄位的型別和長度相匹配;
說明:如果與表資料列寬度不匹配,則當較寬或較大的資料傳進來時會產生執行異常。
示例
如下程式碼不符合規範(假定表wap_user的欄位user_name的定義為VARCHAR(10))
CREATE PROCEDURE ps_add()
BEGIN
DECLARE v_user_name VARCHAR(15);
UPDATE wap_user
SET user_name = v_user_name
WHERE sky_id = 100;
END;
如下程式碼符合規範
CREATE PROCEDURE ps_add()
BEGIN
DECLARE v_user_name VARCHAR(10);
UPDATE wap_user
SET user_name = v_user_name
WHERE sky_id = 100;
END;
規則5:儲存過程程式碼塊必須有註釋;
建議1:減少控制語句的判斷次數,比如在ELSE(IF…ELSE) 語句中,儘量將盡快能檢測到結果的判斷放在前面;
示例
如下語句不符合規範(假定v_count=1的條件大多數情況會滿足)
IF (v_count = 0) THEN
NULL;
ELSEIF (v_count = 1) THEN
NULL;
END IF;
如下語句符合規範(假定v_count=1的條件大多數情況會滿足)
IF (v_count = 1) THEN
NULL;
ELSEIF (v_count = 0) THEN
NULL;
END IF;
建議2:儘量避免使用巢狀的IF語句,在這種情況下應使用多個IF語句來判斷其可能性;
示例
如下語句不符合規範(使用了巢狀的IF語句來進行判定)
IF v_count = 0 THEN
IF v_flag = 0 THEN
NULL;
ELSE
NULL;
END IF;
ELSE v_count = 1 THEN
IF v_flag = 0 THEN
NULL;
ELSE
NULL;
END IF;
END IF;
如下語句符合規範
IF (v_count = 0) AND (v_flag = 0) THEN
NULL;
ELSEIF (v_count = 0 ) AND (v_flag = 1) THEN
NULL;
ELSEIF (v_count = 1) AND (v_flag = 0) THEN
NULL;
ELSEIF (v_count = 1) AND (v_flag = 1) THEN
NULL;
END IF;
建議3:儲存過程、函式、觸發器、程式塊中定義的變數和輸入、輸出引數在命名上有所區分;
說明:
用'v_ '開頭代表程式塊中定義的普通變數。
用'p_ '開頭代表輸入引數變數。
用'x_ '開頭代表輸入輸出或輸出引數變數。
用'cur_'開頭代表遊標變數。存放遊標記錄集。
4.2 物件命名規範
4.2.1 通用規則
規則1:任何資料庫物件的命名,不得使用漢字;
示例
如下語句不符合規範(表明和欄位名使用了漢字)
CREATE TABLE 使用者
(
使用者名稱 VARCHAR(100),
pass_word VARCHAR(16)
);
如下語句符合規範
CREATE TABLE wap_user
(
user_name VARCHAR(100),
pass_word VARCHAR(16)
);
規則2:庫名,表名,欄位名不得超過30個字元,使用者名稱不得超過16個字元;
庫名,表名,欄位名最多支援64個字元,為了統一規範、易於辨識以及減少傳輸量,必須不超過30個字元。
示例
如下語句不符合規範(表命名達到65位長度)(修改)
CREATE TABLE wap_user_tel_number_region_user
(
user_name VARCHAR(100),
pass_word VARCHAR(16)
);
如下語句符合規範
CREATE TABLE wap_user_tel_number_region
(
user_name VARCHAR(100),
pass_word VARCHAR(16)
);
規則3:使用者物件命名應全部為小寫,使用下劃線“_”分割;
說明:由於linux作業系統上的檔名是區分大小寫的,所以MySQL表名是區分大小寫的。
示例
如下語句不符合規範(表名應全部為小寫)
CREATE TABLE Wap_user_tel_number_region
(
user_name VARCHAR(100),
pass_word VARCHAR(16)
);
如下語句符合規範
CREATE TABLE wap_user_tel_number_region
(
user_name VARCHAR(100),
pass_word VARCHAR(16)
);
規則4:命名應使用富有意義的英文,禁止使用拼音首字母, 一般情況下不建議使用拼音命名;
示例
如下語句不符合規範(表名使用了中文且欄位使用了拼音首字母簡寫)
CREATE TABLE wap_yonghu
(
yhm VARCHAR(100),
pass_word VARCHAR(16)
);
如下語句符合規範
CREATE TABLE wap_user
(
user_name VARCHAR(100),
pass_word VARCHAR(16)
);
規則5:命名不得使用資料庫保留字;
說明:使用了資料庫保留字,會導致需要訪問該物件時,需要程式碼做特別的轉換才能訪問
示例
如下程式碼不符合規範(假定user為資料庫保留字)
CREATE TABLE wap_user
(
USER VARCHAR(100),
pass_word VARCHAR(16)
);
如下程式碼符合規範
CREATE TABLE wap_user
(
user_name VARCHAR(100),
pass_word VARCHAR(16)
);
4.2.2 表
規則1:同類業務的表,以相同的表示該類業務的英文開頭;
說明:同類業務的表以相同的英文開頭,在邏輯上清晰,且可避免維護過程中對該類表的誤操作
示例
如下語句不符合規範(假定表wap_user和表user_login_log都屬於wap類業務)
CREATE TABLE wap_user
(
user_name VARCHAR(100),
pass_word VARCHAR(16)
);
CREATE TABLE user_login_log
(
user_name VARCHAR(100),
login_date DATE
);
如下語句符合規範
CREATE TABLE wap_user
(
user_name VARCHAR(100),
pass_word VARCHAR(16)
);
CREATE TABLE wap_user_login_log
(
user_name VARCHAR(100),
login_date DATE
);
說明:各子系統不用加子系統名稱字首,如POS系統的表不用都加pos_字首,如果遇到需要同步其他系統的表的表名與本系統的表名相同時,用子系統名稱做字尾的形式重新命名其他子系統表名,如POS系統需要同步MDM表bill_item_dtl而POS系統也存在這樣的表名,則把MDM的表名重新命名為bill_item_dtl_mdm。
規則2:同類表,如果按照時間不同建立的表,字尾格式一般情況下應為’_YYYY[MM[DD]]’格式;
示例
如下語句不符合規範(將年份2010簡寫為10,導致含義模糊)
CREATE TABLE wap_user_login_1004
(
user_name VARCHAR(100),
login_date date
);
CREATE TABLE wap_user_login_1005
(
user_name VARCHAR(100),
login_date DATE
);
如下語句符合規範
CREATE TABLE wap_user_login_201004
(
user_name VARCHAR(100),
login_date DATE
);
CREATE TABLE wap_user_login_201005
(
user_name VARCHAR(100),
login_date DATE
);
4.2.3 欄位
規則1:欄位命名應具有含義,能反映該欄位儲存的內容,且欄位應增加欄位備註;
示例
如下語句不符合規範(假定儲存的欄位為使用者名稱和密碼,如下的欄位名毫無意義也沒有備註)
CREATE TABLE wap_user
(
col1 VARCHAR(100),
col2 VARCHAR(16)
);
如下語句符合規範
CREATE TABLE wap_user
(
user_name VARCHAR(100),
pass_word VARCHAR(16)
);
如下語句是使用了無意義欄位名,但增加了欄位說明,不作為推薦方法,但確實欄位名無法表述含義時,必須使用該方法;
CREATE TABLE wap_user
(
col1 VARCHAR(100) comment 'username',
pass_word VARCHAR(16) comment 'password'
);
規則2:同種用途的欄位,在所有表中,應保持有同樣的欄位型別和欄位長度,並儘量保持一致的欄位命名;
示例
如下語句不符合規範(欄位user_name在兩個有業務關係的表中欄位長度不一致,易導致業務介面衝突)
CREATE TABLE wap_user
(
user_name VARCHAR(100),
pass_word VARCHAR(16)
);
CREATE TABLE wap_user_login_log
(
user_name VARCHAR(80),
login_date DATE
);
如下語句符合規範
CREATE TABLE wap_user
(
user_name VARCHAR(100),
pass_word VARCHAR2(16)
);
CREATE TABLE wap_user_login_log
(
user_name VARCHAR(100),
login_date DATE
);
以下是建議的公共欄位名稱及型別
create_user |
VARCHAR(32) |
建檔人 |
create_time |
datetime |
建立時間 |
update_user |
VARCHAR(32) |
更新人 |
update_time |
datetime |
更新時間 |
remark |
VARCHAR(255) |
備註 |
contact_name |
VARCHAR(32) |
聯絡人 |
tel |
VARCHAR(20) |
電話號碼 |
mob |
VARCHAR(20) |
手機號碼 |
address |
VARCHAR(100) |
聯絡地址 |
zip_code |
VARCHAR(10) |
郵編 |
identity_card |
VARCHAR(25) |
身份證號 |
fax |
VARCHAR(20) |
傳真 |
|
VARCHAR(64) |
電郵 |
建議1: 欄位名建議不要用JAVA關鍵字來命名,;
4.2.4 主鍵
規則1:涉及到要做分庫分表的表用有序UUID做主鍵,UUID主鍵型別選擇CHAR(32);
規則2:不涉及分庫分表的表選用自增長ID做主鍵,主鍵型別使用unsigned int或unsigned big int;
規則3:主鍵無特別要求的,欄位名統一定義為 id;
4.2.4 外來鍵
規則1:外來鍵名應以”fk_”開頭,後接表名;
示例
如下語句不符合規範(外來鍵名未以fk_開頭)
alter table wap_user_login_log
add constraint wap_user_login_log_f foreign key(user_name) REFERENCES tb_user_name(user_name)
如下語句符合規範
ALTER TABLE wap_user_login_log
ADD CONSTRAINT fk_wap_user_login_log FOREIGN KEY(user_name) REFERENCES tb_user_name(user_name)
規則2:不同的表的外來鍵,如果引用的是相同表的相同欄位,則外來鍵欄位名及型別應保持一致;
4.2.5 索引
規則1:唯一索引應以”uk_”+”表名_”+”欄位名”命名;
示例
如下語句不符合規範(唯一索引未以uk_開頭)
ALTER TABLE wap_user
ADD UNIQUE wap_user_username_u (username)
如下語句符合規範
ALTER TABLE wap_user
ADD UNIQUE uk_wap_user_username (username)
規則2:普通索引應以”idx_”+”表名_”+“欄位名”命名;
示例
如下語句不符合規範(不符合索引命名規範)
ALTER TABLE wap_user
ADD INDEX wap_user_user_id_idx (user_id)
如下語句符合規範
ALTER TABLE wap_user
ADD INDEX idx_wap_user_user_id (user_id)
規則3:全文索引索引應以”fullidx_”+”表名_”+“欄位名”命名;
4.2.6 檢視
規則1:檢視命名應以“v_”+“表名[_表名[_表名]]”命名,如果表名過多可以用“v_”+“功能描述”來命名;
示例
如下語句不符合規範(檢視和表是不可以同名的,如下語句會引起錯誤且不符合規範)
CREATE VIEW wap_user
AS
SELECT first_name, last_name, job_id
FROM wap_user;
如下語句符合規範
CREATE VIEW v_wap_user
AS
SELECT first_name, last_name, job_id
FROM wap_user;
4.2.7 函式
規則1:函式命名以”func_”開頭,後接函式的功能;
示例
如下語句不符合規範(未以func_開頭)
CREATE FUNCTION get_money
BEGIN
……
END;
如下語句符合規範
CREATE FUNCTION func_get_money
BEGIN
……
END;
4.2.8 儲存過程
規則1:儲存過程以“proc_”開頭,後接功能描述;
示例
如下語句不符合規範(未以proc_開頭)
CREATE PROCEDURE update_user
BEGIN
……
END;
如下語句符合規範
CREATE PROCEDURE proc_update_user
BEGIN
……
END;
4.2.9 觸發器
規則1:觸發器以“trig_”+表名+“_ins/del/upd”+”_before/after”命名;
示例
如下語句不符合規範(未遵循命名規範)
CREATE TRIGGER trigger1
AFTER DELETE ON wap_user
BEGIN
……
END;
如下語句符合規範
CREATE TRIGGER trig_wap_user_del_after
AFTER DELETE ON wap_user
BEGIN
……
END;
4.2.10 臨時表
規則1:臨時表以“tmp_”開頭,後接功能描述;
示例
如下語句不符合規範
CREATE TEMPORARY TABLE tab_tmp1
(
user_name VARCHAR(100),
pass_word VARCHAR(16)
);
如下語句符合規範
CREATE TEMPORARY TABLE tmp_wap_user
(
user_name VARCHAR(100),
pass_word VARCHAR(16)
);
規則2:如果是在上線/割接中被重新命名的表,命名應是原表名+“_YYYYMMDD”;
示例
如下語句不符合規範(臨時表以old結尾,而非日期結尾)
RENAME TABLE wap_user TO wap_user_old;
如下語句符合規範
RENAME TABLE wap_user TO wap_user_20100416;
4.2.11 使用者及資料庫名
規則1: 資料庫名:retail_字首+模組名,如POS系統的資料庫名為retail_pos,MDM的資料庫名為retail_mdm,使用者名稱與資料庫名儘量一致;
示例
如下語句符合規範
retail_pos pos系統
retail_mps 營促銷系統
retail_oc 訂單中心
retail_pms 採購管理系統
retail_mdm mdm
retail_gms 貨品管理系統
retail_fms 財務管理系統
4.3物件設計規範
4.3.1 表設計
規則1:資料庫設計文件中,必須包含表資料保留時間;
規則2:資料庫設計文件中,必須包含表在最大保留時間下的資料量;
規則3:資料庫設計文件中,必須包含表的讀寫頻率;
規則4:和其他表有關聯的表,和其他表功能一致的欄位型別以及長度,儘量使用相同的列名;
規則5: 每個表應設計一個主鍵;
規則6:資料庫字符集,表字符集,欄位字符集統一選用UTF8字符集,校對規則統一使用大小寫敏感的utf8_bin;
規則7:表引擎選用INNODB引擎;
規則8:必須要有表的註釋,用於描述表的功能;
建議1:對於需要同步到資料倉庫的表,原則上必須包含同步頻率以及同步機制;
建議2:歷史表字尾建議用“_hist”;
4.3.2 欄位
規則1:欄位必須要有註釋資訊,如果欄位的值是有限的(如狀態值只有“有效”、“無效”,如性別只有“男”、“女”等)必須在欄位註釋中對每個值表達的意思進行描述;
規則2:定長字元列使用CHAR型別, 不定長字元型使用VARCHAR型別;
規則3:日期欄位只需要表達年月日的選用DATE型別,需要表達年月日時分秒的欄位選用DATETIME類或TIMESTAMP型別,但請注意各自能表達的範圍以及TIMESTAMP的時區特性;
說明:MySQL中的DATETIME對應ORACLE的DATE型別,而MySQL的DATE型別只是ORACLE DATE型別的年月日部分不包括時分秒部分,MySQL TIME型別是ORACLE DATE 型別的時分秒部分,下表是MySQL各時間型別的格式樣例
Data Type |
“Zero” Value |
'0000-00-00' |
|
'00:00:00' |
|
'0000-00-00 00:00:00' |
|
'0000-00-00 00:00:00' |
|
0000 |
DATETIME與TIMESTAMP型別的區別
DATETIME |
TIMESTAMP |
|
儲存長度 |
8位元組 |
4位元組 |
時區支援 |
不支援 |
支援 |
表達範圍 |
1000-01-01 00:00:00 9999-12-31 23:59:59 |
1970-01-01 00:00:01 2038-01-19 03:14:07 |
儲存格式 |
實際格式儲存 |
UTC格式 |
規則4:ORACLE轉MySQL之NUMBER欄位型別轉換;
number(M,N)如果N是0則為整形,對應MySQL的整形型別,下面是MySQL的各整形型別的所需位元組數及能表達的範圍(摘抄至官網5.6),
Type |
Storage |
Minimum Value |
Maximum Value |
(Bytes) |
(Signed/Unsigned) |
(Signed/Unsigned) |
|
TINYINT |
1 |
-128 |
127 |
0 |
255 |
||
SMALLINT |
2 |
-32768 |
32767 |
0 |
65535 |
||
MEDIUMINT |
3 |
-8388608 |
8388607 |
0 |