本规范的主要目的是但愿规范数据库设计与开发,尽可能避免因为数据库设计与开发不当而产生的麻烦;同时好的规范,在执行的时候能够培养出好的习惯,好的习惯是软件质量的很好保证。html
本规划的适用人员范围包括涉及数据库设计与开发的相关技术人员。mysql
本规范采用如下术语描述:linux
★规则:也称为强规范是编程时必须强制遵照的原则sql
★建议:编程时必须加以考虑的原则数据库
★说明:对此规则或建议进行必要的解释编程
★示例:对此规则或建议从正、反两个方面给出数组
规则1: 数据库代码中,关键字大写,其余内容小写;并发
示例:oracle
以下代码不符合规范:(关键字未大写)数据库设计
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 语句中换行的字段名应缩进并与上一行的第一个字段名对齐。
规则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_'开头表明游标变量。存放游标记录集。
规则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)
);
规则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
);
规则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关键字来命名,;
规则1:涉及到要作分库分表的表用有序UUID作主键,UUID主键类型选择CHAR(32);
规则2:不涉及分库分表的表选用自增加ID作主键,主键类型使用unsigned int或unsigned big int;
规则3:主键无特别要求的,字段名统必定义为 id;
规则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:不一样的表的外键,若是引用的是相同表的相同字段,则外键字段名及类型应保持一致;
规则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_”+”表名_”+“字段名”命名;
规则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;
规则1:函数命名以”func_”开头,后接函数的功能;
示例
以下语句不符合规范(未以func_开头)
CREATE FUNCTION get_money
BEGIN
……
END;
以下语句符合规范
CREATE FUNCTION func_get_money
BEGIN
……
END;
规则1:存储过程以“proc_”开头,后接功能描述;
示例
以下语句不符合规范(未以proc_开头)
CREATE PROCEDURE update_user
BEGIN
……
END;
以下语句符合规范
CREATE PROCEDURE proc_update_user
BEGIN
……
END;
规则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;
规则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;
规则1: 数据库名:retail_前缀+模块名,如POS系统的数据库名为retail_pos,MDM的数据库名为retail_mdm,用户名与数据库名尽可能一致;
示例
以下语句符合规范
retail_pos pos系统
retail_mps 营促销系统
retail_oc 订单中心
retail_pms 采购管理系统
retail_mdm mdm
retail_gms 货品管理系统
retail_fms 财务管理系统
规则1:数据库设计文档中,必须包含表数据保留时间;
规则2:数据库设计文档中,必须包含表在最大保留时间下的数据量;
规则3:数据库设计文档中,必须包含表的读写频率;
规则4:和其余表有关联的表,和其余表功能一致的字段类型以及长度,尽可能使用相同的列名;
规则5: 每一个表应设计一个主键;
规则6:数据库字符集,表字符集,字段字符集统一选用UTF8字符集,校对规则统一使用大小写敏感的utf8_bin;
规则7:表引擎选用INNODB引擎;
规则8:必需要有表的注释,用于描述表的功能;
建议1:对于须要同步到数据仓库的表,原则上必须包含同步频率以及同步机制;
建议2:历史表后缀建议用“_hist”;
规则1:字段必需要有注释信息,若是字段的值是有限的(如状态值只有“有效”、“无效”,如性别只有“男”、“女”等)必须在字段注释中对每一个值表达的意思进行描述;
规则2:定长字符列使用CHAR类型, 不定长字符型使用VARCHAR类型;
规则3:日期字段只须要表达年月日的选用DATE类型,须要表达年月日时分秒的字段选用DATETIME类或TIMESTAMP类型,但请注意各自能表达的范围以及TIMESTAMP的时区特性;
说明:MySQL中的DATETIME对应ORACLE的DATE类型,而MySQL的DATE类型只是ORACLE DATE类型的年月日部分不包括时分秒部分,MySQL TIME类型是ORACLE DATE 类型的时分秒部分,下表是MySQL各时间类型的格式样例
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 存储非负数值;
一样的字节数,存储的数值范围更大
规则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的字段应在组合字段索引的前导字段;
规则1:视图中不容许出现ORDER BY排序;
规则2:基于多表关联的视图,必须在字段名前指定表别名;
建议1:视图的基础数据尽可能从表中获取,尽可能不要嵌套视图;
规则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。
规则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:函数尽可能只是实现复杂的计算功能,不对数据库进行更新操做;
规范1:如无必要,不得设计触发器,任何触发器的设计,必须获得DBA批准;
规范2:应用的完整性不该由触发器保证,而是经过代码的事务控制;
建议1:有高度一致性依赖的逻辑,触发器应设计为BEFORE而非AFTER方式;
规则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来替代;
规则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 ...)的语句
假若有相似下面分页语句:
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);
建议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值
MySQL中此两类字符串定义时候填写的长度N,不是字节数的意思 ,而是字符数的意思。
咱们MySQL全部数据库的字符集都为UTF8,字符集校对规则为UTF8_bin。对于中文汉字,实际存储的时候占三个字节,而数据或字母,则只占一个字节。例如:
CREATE TABEL company_inventory (color VARCHAR(44) COMMENT '颜色');
则color最多能存储40个字符。
获取当前时间: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对应的星期数字
字符串转换成日期方式,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)
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将使用表锁,而不是行锁 |