程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 數據庫知識 >> MYSQL數據庫 >> MySQL綜合教程 >> MySQL設計與開發規范

MySQL設計與開發規范

編輯:MySQL綜合教程

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)

傳真

email

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

DATE

'0000-00-00'

TIME

'00:00:00'

DATETIME

'0000-00-00 00:00:00'

TIMESTAMP

'0000-00-00 00:00:00'

YEAR

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

16777215

INT

4

-2147483648

2147483647

 

 

0

4294967295

BIGINT

8

-9223372036854775808

9223372036854775807

 

 

0

18446744073709551615

如果number(M,N)中N大於0則對應MySQL的decimal類型,如oracle的number(5,2)則MySQL為decimal(5,2),值得注意的是,在超出范圍的情況下ORACLE會報錯,而MySQL會取它能表示的最大值來替代原來的值,如decimal(5,2)/number(5,2)能表達的范圍為 -999.99-999.99 如果要插入1000的數據,oracle會提示超表達范圍的錯,而MySQL會以999.99來替代

 

規則5:固定長度的字符串使用CHAR,單字符字段使用CHAR(1)類型;

規則6:字段避免使用NULL值,用默認值來替代,修改時間,審核時間等用“0000-00-00 00:00:00”這樣的默認值進行替代,備注用‘’的空字符進替代;

規則7:不建議使用ENUM,SET類型,用TINYINT替代;

 

建議1: 盡量不使用BLOB,TEXT類型,大字段建議單獨設計表,通過關聯進行查找;

建議2: 建議使用UNSIGNED 存儲非負數值;

同樣的字節數,存儲的數值范圍更大

4.3.3 索引

規則1: 無特別說明,每個表的索引不得超過5個;

規則2: 單字段上的索引不得超過2個;(即一個單字段最多可在上面建立一個單字段索引和一個組合索引包含這個字段)

規則3: 復合索引原則上不得超過3個字段;

規則4: 外鍵列需要創建索引;

 

建議1: 頻繁出現在where子句裡的字段建議建立索引;

建議2: 用來和其他表關聯的字段建議建立索引;

建議3: 索引字段建議有高的選擇性和過濾性(count(distinct)/count(*)>0.6);

建議4: 建立索引的時候,建議考慮到SELECT和INSERT,UPDATE,DELETE的平衡;

建議5: 一般建議在查詢數據量10%以下使用索引;

建議6: WHERE子句的查詢條件構成索引字段前導字段;

建議7: 選擇性更高的字段放在組合字段索引的前導字段;

建議8: 如果字段選擇性接近,則把頻繁查詢的字段放在前面;

建議9: 進行GROUP BY或者是ORDER BY的字段應在組合字段索引的前導字段;

 

4.3.6 視圖

規則1:視圖中不允許出現ORDER BY排序;

規則2:基於多表關聯的視圖,必須在字段名前指定表別名;

 

建議1:視圖的基礎數據盡量從表中獲取,盡量不要嵌套視圖;

 

4.3.7 存儲過程

規則1: 避免將業務邏輯放在存儲過程中,那樣容易將業務邏輯和DB耦合在一起;

規則2: 存儲過程,必須有異常捕獲代碼;

規則3: 存儲過程中嚴禁使用GOTO語句進行跳轉;

規則4: 有循環更新的存儲過程,必須進行批量提交,且必須進行事務控制;

說明:MySQL存儲過程中必須用START TRANSACTION 來顯示開始一個事務,否則會按默認的每個DML做個一個事務。

規則5: 存儲過程中如果使用了游標,則在存儲過程正常或者異常退出必須關閉所有打開的游標;

規則6: 存儲過程中如果有更新,必須在異常捕獲代碼中做回退操作;

規則7: 注釋格式如下,存儲過程說明放在在COMMENT中,其他用”##”進行注釋說明;

CREATE PROCEDURE prc_vendor

COMMENT '說明:同步下發接收; 參數:xxxx; 返回:標志0=成功;’

##建立:xxx 2012.07.17

## Modify by xxx 2012.08.08 增加供應商狀態字段

##Modify by xxx 2012.11.10 增加供應商英文名稱字段

BEGIN

...

END;

建議1:存儲過程每次被更新,應在注釋中說明更新的內容,更新的日期,以及更新的責任人,並且在更新前保留舊版本代碼

建議2:盡量避免在存儲過程中使用動態SQL。

 

4.3.8 函數

規則1: 避免將業務邏輯放在函數中,那樣容易將業務邏輯和DB耦合在一起;

規則2: 函數中,如果進行了事務處理,必須有異常捕獲代碼;

規則3: 函數中嚴禁使用GOTO語句進行跳轉;

規則4: 有循環更新的函數,必須進行批量提交,且必須進行事務控制;

規則5: 函數中如果使用了游標,則在函數正常或者異常退出必須關閉所有打開的游標;

規則6: 函數中如果對數據進行了更新操作,必須在異常捕獲代碼中做回退操作;

規則7: 注釋格式如下

CREATE FUNCTION func_get_serialno

(

p_request VARCHAR ###請求編號

)

return VARCHAR

COMMENT ’說明:產生序列號函數,通過serialno_config配置表響應產生序列號; 參數:p_requestid 請求編號 返回:返回需要的序列號‘

##建立:xxx 2013.07.02

##modified by xxx 2014-6-18 xxx

BEGIN

...

END;

 

建議1:函數每次被更新,應在注釋中說明更新的內容,更新的日期,以及更新的責任人,並且在更新前保留舊版本代碼;

建議2:函數盡量只是實現復雜的計算功能,不對數據庫進行更新操作;

4.3.9 觸發器

規范1:如無必要,不得設計觸發器,任何觸發器的設計,必須得到DBA批准;

規范2:應用的完整性不應由觸發器保證,而是通過代碼的事務控制;

建議1:有高度一致性依賴的邏輯,觸發器應設計為BEFORE而非AFTER方式;

 

4.4 開發規范

4.4.1. 基本規范

 

規則1: 避免使用存儲過程、觸發器、函數等,容易將業務邏輯和DB耦合在一起,並且MySQL的存儲過程、觸發器、函數中存在一定的BUG;

規則2: 禁止進行字段數據類型的隱式轉換,所有轉換必須進行明確的數據類型轉換;

說明:隱式轉換會導致字段上的索引失效, 最為常見的隱式類型轉換常見於時間類型與字符串類型之間,建議所有時間類型字段在myBatis中均以時間類型傳入,或者以字符串傳入然後通過時間函數轉換字符串為合法的時間格式 ,如下:
SELECT name

FROM member

WHERE vgmt_create=DATE_FORMATE('2009010101:02:03','%Y-%m-%d %H:%i:%s');

規則3: 禁止在多表關聯的時候,在非索引字段上的關聯;

規則4: 進行模糊查詢時,禁止條件中字符串直接以“%”開頭;

規則5: 在使用for update子句時一定注意限制條件,避免鎖定全表或者不需要被鎖定的行記錄。如無必要鎖定數據則應避免使用for update;

規則6: 在進行結果集合並(union或union all)時, 如不需要進行結果去重,則必須使用union all,而不能使用union;且盡量減少進行數據集的去重;

規則7: 除非必要,避免使用 != 等非等值操作符,會導致用不到索引;

規則8: 禁止在 WHERE 條件中出現的過濾字段上使用任何函數進行類型或格式的轉換;正確的做法是把傳入的值轉換為列類型所需要的;

錯誤的寫法:

SELECT username

FROM gl_user

WHERE DATE_FORMAT(gmt_create, %Y%m%d%H%i%s')='20090501022300‘;

 

正確的寫法:

SELECT username

FROM gl_user

WHERE gmt_create=DATE_FORMAT('20090501022300', '%Y-%m-%d %H:%i:s');

 

規則9: 禁止使用order by rand();

order by rand() 會將數據從磁盤中讀取,進行排序,會消耗大量的IO和CPU。

規則10:避免大使用大SQL,可以拆分成多個小SQL來替代;

 

4.4.2. 綁定變量使用規范

規則1: 應用端所有查詢的 where 條件中的變量,都需要使用綁定變量來實現,以防SQL注入,同時性能也會更優;

規則2: 在 myBatis 的 SqlMap 文件中綁定變量使用 "#{var_name}"表示,替代變量使用"${var_name}";所有需要動態 Order By 條件的查詢,在使用替代變量過程中,需要將可能傳入的內容以枚舉類寫死在代碼中,禁止接受任何外部傳入內容;對於不變的常量條件,請使用常量而不是變量;

\

規則3: IN子句,使用"Iterate + 數組類型變量"的方式實現綁定變量而不是通過代碼拼接 Query 語句;

例如:

\

myBatis會生成user_level in (1,2,3,4,5 ...)的語句

 

4.4.3. 分頁規范

假如有類似下面分頁語句:

SELECT * FROM table ORDER BY create_time DESC LIMIT 10000,10;

這種分頁方式會導致大量的IO,因為MySQL使用的是提前讀取策略。

推薦分頁方式:

SELECT * FROM table WEHRE create_time < last_time ORDER BY create_time DESC LIMIT 10;

SELECT * FROM table INNER JOIN (SELECT id FROM table ORDER BY create_time LIMIT 10000,10) AS t USING(id);

 

4.4.4. 建議

建議1: 盡量使用if來簡化SQL訪問數據庫的次數;

示例:

如下兩個語句實現的功能

SELECT COUNT(*) , SUM(salary)

FROM employees

WHERE department_id = 20

AND first_name LIKE 'SMITH%';

 

SELECT COUNT(*) , SUM(salary)

FROM employees

WHERE department_id = 30

AND first_name LIKE 'SMITH%';

可以使用IF改寫為如下語句

SELECT COUNT(IF(department_id=20, '*', NULL)) d20_count,

COUNT(IF(department_id=30, '*', NULL)) d30_count,

SUM(IF(department_id=20, salary, NULL)) d20_sal,

SUM(IF(department_id=30, salary, NULL)) d30_sal

FROM employees

WHERE first_name LIKE 'SMITH%';

建議2: 避免使用HAVING子句, HAVING 只會在檢索出所有記錄之後才對結果集進行過濾. 這個處理需要排序,總計等操作. 如果能通過WHERE子句限制記錄的數目,那就能減少這方面的開銷;

示例

如下語句使用的是HAVING子句

SELECT last_name, avg(salary)

FROM employees

GROUP BY last_name

HAVING last_name != 'Grant'

AND last_name != 'Fay'

 

改寫使用WHERE的語句如下

SELECT last_name, avg(salary)

FROM employees

WHERE last_name != 'Grant'

AND last_name != 'Fay'

GROUP BY last_name

建議3: 盡量少用not exist/no in等反向寫法。如果一定要用時,盡量選擇not exist;

建議4: 盡量少用is null/is not null等null的處理;

建議5: SQL語句中IN子句裡的值不應超過300;

建議6: 對於大表查詢中的列項應盡量避免進行諸如CAST()或CONVERT()的轉換;

建議7: 盡量避免進行全表掃描,限制條件盡可能多,以便更快搜索到要查詢的數據;

建議8: SELECT查詢語句建議增加limit 1000 限定返回的行數;

建議9: 進行數據庫結構設計的時候,考慮適當的冗余,盡量確保應用讀寫數據的SQL簡潔;

建議10: 要返回MySQL自增序列的ID值,可以考慮使用函數LAST_INSERT_ID(),此函數只能返回同

一個SESSION最近一次對有AUTO_INCREMENT屬性表INSERT的ID值

4.5. 開發實用技術

4.5.1. CHAR(N)或VARCHAR(N)中的N解釋

MySQL中此兩類字符串定義時候填寫的長度N,不是字節數的意思 ,而是字符數的意思。

我們MySQL所有數據庫的字符集都為UTF8,字符集校對規則為UTF8_bin。對於中文漢字,實際存儲的時候占三個字節,而數據或字母,則只占一個字節。例如:

CREATE TABEL company_inventory (color VARCHAR(44) COMMENT '顏色');

則color最多能存儲40個字符。

4.5.2. 日期操作函數

獲取當前時間:NOW(),CURDATE()、CURTIME()

其中, NOW()函數精確到秒, 格式:YYYY-MM-DD HH:MM:SS

CURDATE函數精確到天,格式:YYYY-MM-DD

CURTIME函數精確到秒,格式:HH:MM:SS

 

日期數值的加減函數:

DATE_ADD(date,INTERVAL expr type)

DATE_ SUB(date,INTERVAL expr type)

常用的幾種type類型:YEAR、MONTH、DAY、HOUR、MINUTE,其中expr可以為正數或負數,我們在開過程中,一般使用DATE_ADD()函數,若要作日期減去一個數字的方式,就使用負數。

DATEDIFF(expr1,expr2),是返回 開始日期expr1與 結束日期expr2之間,相差的天數 ,返回值為正數或負數。

 

返回日期某部分信息的函數:

YEAR(expr1) 返回日期expr1部分的年份;

MONTH(expr1) 返回日期expr1部分的月份;

DAY(expr1)返回expr1部分的天數;

WEEKDAY(expr1)返回expr1對應的星期數字

 

4.5.3. 類型轉換函數

字符串轉換成日期方式,DATE_FORMAT()或STR_TO_DATE(),

兩個函數的格式如下:

DATE_FORMAT(expr1,format)

STR_TO_DATE(expr1, format)

常用的日期格式YYYY-MM-DD HH:MM:SS 對應的format為%Y-%m-%d %H:%i:%S

通用的類型轉換函數:

CAST(expr AS type)

CONVERT(expr,type)

CONVERT(expr USING transcoding_name)

 

4.5.4. INNODB與MYISAM的主要區別

 

MyISAM

InnoDB

構 成上的區別:

每個MyISAM表在磁盤上存儲三個文件。文件的名字以表的名字開始,擴展名指出文件類型。

.frm文件存儲表定義。

.MYD文件存儲數據 (MYData)。

.MYI文件存儲索引 (MYIndex)。

InnoDB表空間數據文件和日志文件,InnoDB表的大小只受限於操作系統文件的大小

事務處理上方面:

MyISAM類型的表強調的是性能,其執行速度比InnoDB類型更快,但是不提供事務支持

InnoDB提供事務支持事務,外部鍵等高級 數據庫功能

SELECT UPDATE,INSERT,Delete操 作

如果執行大量的SELECT,MyISAM是更好的選擇

1.如果你的數據執行大量的INSERT或UPDATE,出於性能方面的考慮,應該使用InnoDB表

2.DELETE FROM table時,InnoDB不會重新建立表,而是一行一行的 刪除。

3.LOAD TABLE FROM MASTER操作對InnoDB是不起作用的,解決方法是首先把InnoDB表改成MyISAM表,導入數據後再改成InnoDB表,但是對於使用的額外的InnoDB特性(例如外鍵)的表不適用

表的具體行數

select count(*) from table,MyISAM只要簡單的讀出保存好的行數,注意的是,當count(*)語句包含 where條件時,兩種表的操作是一樣的

InnoDB中不保存表的具體行數,也就是說,執行select count(*) from table時,InnoDB要掃描一遍整個表來計算有多少行

表鎖,MyISAM表鎖讀寫互相阻塞,寫鎖優先級高於讀鎖。參數選項low_priority_updates設置寫鎖優先級比讀鎖低、參數選項concurrent_insert配置是否使用並發插入特性,concurrent_insert=0 表示不允許並發插入,concurrent_insert=1表示允許對沒有空數據塊的表使用並發插入(缺省),concurrent_insert=2表示對所有表允許並發插入

提供行鎖(locking on row level),提供與Oracle類型一致的不加鎖讀取(non-locking read in SELECTs)。MySQL的行鎖是針對索引加的鎖,不是針對記錄加的鎖,在不通過索引條件查詢的時候,InnoDB使用的是表鎖,而不是行鎖。

另外,即便在條件中使用了索引字段,但是否使用索引來檢索數據是由MySQL通過判斷不同執行計劃的代價來決定的,如果MySQL認為全表掃描效率更高,比如對一些很小的表,它就不會使用索引,這種情況下InnoDB將使用表鎖,而不是行鎖

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved