 Oracle  数据库整体设计规范(必读) 1.设计 1. 应用里面,多个数据库之间请不要通过 DBLINK 访问。 2. 请不要采用触发器。 3. 请不要使用视图和物化视图。 4. 请不要使用外键约束,如果数据存在外键关系,请在程序层面实现。 5. 请尽量不要使用 job,如果不得已必须使用,Job 的设计必须是可重复执行的。 6. 请尽量不要采用存储过程。 7. 应用必须具有自动重连的机制。但是又要避免每执行一条 SQL 语句就检查一下 DB 的可 用性。 2.命名 a) 命名应使用富有意义的英文词汇(见词知义),多个单词组成的,中间以下划线分割。 b) 命名只能使用英文字母,数字和下划线。 c) 命名避免使用 Oracle 保留字和系统关键字。 d) 命名长度以不超过 15 个字符为宜(避免超过 20)。 e) 命名全部采用小写,并且名称前后不能加引号。  数据库对象设计规范 1. 表 设计 a) 在设计时尽量包含两个日期字段:gmt_created(创建日期),gmt_modified(修改日期)且 非空, 对表的记录进行更新的时候,必须包含对 gmt_modified 字段的更新。 b) 尽可能使用简单数据类型,不要使用类似数组或者嵌套表这种复杂类型。 c) 必须要有主键,且尽量不要使用有实际意义的字段做主键。 d) 需要 join 的字段,数据类型保持绝对一致。 e) 当表的字段数非常多时,可以将表分成两张表,一张作为条件查询表,一张作为详 细内容表(主要是为了性能考虑)。 f) 当字段的类型为枚举型或布尔型时,建议使用 char(1)类型。 命名 a) 同一个模块的表尽可能使用相同的前缀,表名尽可能表达含义,例如: CRM_SAL_FUND_ITEM。 b) 字段命名应尽可能使用表达实际含义的英文单词或缩写,不要使用类似“VALUE1” 这种无意义的字段名。 c) 布尔值类型的字段命名为 is+描述。如 member 表上表示是否为 enabled 的会员的字 段命名为 IsEnabled。 字段类型 类型 规范 NUMBER(p,s) 固定精度数字类型 NUMBER 不固定精度数字类型,当不确定数字的精度时使用,PK 通常使用此类型 DATE 当仅需精确到秒时,选择 DATE 而不是 TIMESTAMP 类型 TIMESTAMP 扩展日期类型,不建议使用 VARCHAR2 变长字符串,最长 4000 个字节 CHAR 定长字符串,除非是 CHAR(1),否则不要使用 CLOB 当超过 4000 字节时使用,但是要求这个字段必须单独创建到一张表中, 然后有 PK 与主表关联。此类型应该尽量控制使用 字段注释 a) 标准字段注释由一组"@"开头的标签+空格+文本组成。 以 MD_USER 表的部分字段为例: Name Type Comments PARTY_ID VARCHAR2(20) @desc 主键 ID CORPORATION_ID VARCHAR2(20) @desc 用户所在公司 ID @fk md_corporation.party_id STATUS VARCHAR2(20) @desc 状态 @values disable|enable:未激活状 态|激活状态 IS_PRI_ACCOUNT CHAR(1) @desc 是否为主账号。后台生成 UK 时使用 @values y|n:是帐号,非主帐号 @logic 一个公司内部,有且仅有 一个主账号存在 b) 注释标签说明 标签名 中文含义 必填 备注 @desc 字段中文描述 Yes @fk 字段对应的外键字段 @values 取值范围说明。多个值以"|" 分隔 如此字段的值由系统自动生成,可忽略不书写。 @sample 数据范本 对于复杂数据格式,最好给一个数据范本。 @formula 计算公式 写明该字段由哪些字段以何种公式计算得到。 @logic 数据逻辑 简要写明该字段的数据是在何种业务规则下,如何变 化的。 @redu 标识此字段冗余 @depr 标识此字段已废弃 简要写明:废弃人 废弃日期 废弃原因 2. 索引 设计 a) Bitmap 索引通常不适合我们的环境。 b) 索引根据实际业务SQL,建立对应的索引字段。 c) 不要创建带约束的索引,所有的约束效果都通过显示创建约束然后再 using index 一 个已经创建好的普通索引来实现。 命名 a) __ind,各部分以下划线(_)分割。 b) 多单词组成的 column name,取前几个单词首字母,加末单词组成 column_name。如: sample 表 member_id 上的索引:sample_mid_ind。 3. 约束 设计 a) 主键最好是无意义的,由 Sequence 产生的 ID 字段,类型为 number,不建议使用组 合主键。 b) 若要达到唯一性限制的效果,不要创建 unique index,必须显式创建普通索引和约束 (pk 或 uk),即先创建一个以约束名命名的普通索引,然后创建一个约束,用 using index ...指定索引。 c) 当删除约束的时候,为了确保不影响到 index,最好加上 keep index 参数。 d) 主键的内容不能被修改。 e) 外键约束一般不在数据库上创建,只表达一个逻辑的概念,由程序控制。 f) 当万不得已必须使用外健的话,必须在外健列创建 INDEX。 命名 a) 主键约束: _pk 结尾,_pk; b) unique 约束:_uk 结尾,__uk; c) check 约束: _ck 结尾,__ck; d) 外键约束: _fk 结尾,以 pri 连接本表与主表,_pri__fk; 4. SEQUENCE 命名 a) seq_ 5. 触发器 命名 a) _A(After)B(Before)I(Insert)U(Update)D(Delete)_trg。 b) 若是用于同步的触发器以 sync 作为前缀:sync__trg。 6. 过程、函数、包 命名 a) 过程以 proc_开头,函数以 func_开头,包以 pkg_开头。 b) 变量命名约定:本地变量以 v_为前缀,参数以 p_为前缀,可以带_I(输入),_O(输出)、 _IO(输入输出)表示参数的输入输出类型。  SQL 开发规范 一. 编码规范 1. 在代码中不允许出现任何 DDL 语句 a) DDL 语句一律由 DBA 编写并统一执行 2. 写 SQL 的时侯一定要使用绑定变量 a) 对于极少数情况下不使用绑定变量提高性能,使用之前一定要和 DBA 沟通 3. 写 SQL 的时候一定要给每个字段指定表名做前缀 a) 比如 select a.id,a.name from test a; 好处是一来带来性能的提升,二来可以避免一些错 误的发生。 4. 在 sqlmap 中的变量,要用#号,而不要用$符号 a) 如#appid#。因为$name$ 是字面意义的替换,这种形式会有 SQL 注入的漏洞,而 #name# 是带类型的替换,不存在 SQL 注入的风险。 5. 请不要写 select * 这样的代码,指定需要的字段名 6. 避免在 where 子句中对字段施加函数 a) 通常,不允许在字段上添加函数或者表达式,这样将导致索引失效,如: 错误的写法: select * from iw_account_log where to_char ( trans_dt, 'yyyy-mm-dd') = '2007-04-04'; select qty from product where p_id + 12 = 168; 正确的写法: select * from iw_account_log where trans_dt >= to_date ( '2007-04-04', 'yyyy-mm-dd') and trans_dt < to_date ( '2007-04-05', 'yyyy-mm-dd'); select qty from product where p_id = 168 - 12; b) 如果是业务要求的除外,但需要在编写时咨询 DBA c) 特别注意,当表连接时,用于连接的两个表的字段如果数据类型不一致,则必须在 一边加上类型转换的函数,如 错误的写法(a.id 是 number 类型,而 b.operator_number 是 char 类型): select count from adm_user a, adm_action_log b where a.id = b.operator_number and a.username = '小钗'; 正确的写法: select count from adm_user a, adm_action_log b where to_char(a.id) = b.operator_number and a.username = '小钗'; select count from adm_user a, adm_action_log b where a.id = to_number(b.operator_number) and a.username = '小钗'; 上面两种写法哪个正确?遇到这种情况时必须咨询 DBA! 7. 严格要求使用正确类型的变量,杜绝 oracle 做隐式类型转换的情况 a) 推荐在 sqlmap 的变量中指定变量的数据类型,如:select * from iw_user where iw_user_id = #userid:VARCHAR# b) 其中,对于时间类型的字段,必须使用 TO_DATE 进行赋值(当前时间可直接用 sysdate 表示),不允许下列这些错误用法: 错误的写法(使用 date 类型的变量): select * from iw_account_log where trans_account = #transaccount:varchar# and trans_dt >= #dateBegin:date# and trans_dt < #dateEnd:date# 错误的写法(将 to_date 函数和数字进行算术运算): select * from iw_account_log where trans_account = #transaccount:varchar# and trans_dt >= to_date(#dateBegin:varchar#, 'yyyy-mm-dd hh24:mi:ss') and trans_dt < to_date(#dateBegin:varchar#, 'yyyy-mm-dd hh24:mi:ss') + 1 正确的写法: select * from iw_account_log where trans_account = #transaccount:varchar# and trans_dt >= to_date(#dateBegin:varchar#, 'yyyy-mm-dd hh24:mi:ss') and trans_dt < to_date(#dateEnd:varchar#, 'yyyy-mm-dd hh24:mi:ss') /*或 trans_dt < sysdate */ c) 3、对于变量数据类型错误导致 SQL 严重性能问题的,按严重的编码错误 Bug 处理! 8. 全模糊查询无法使用 INDEX,应当尽可能避免 a) 比如:select * from table where name like '%jacky%'; 9. 外连接的写法 a) 不推荐使用 ANSI 连接,如 inner join、left join、right join、full outer join,而推荐使 用(+)来表示外连接 不推荐的写法: select a.*, b.goods_title from iw_account_log a left join beyond_trade_base b on a.TRANS_OUT_ORDER_NO = b.trade_no where a.trans_code = '6003' and a.trans_account = #transacnt:varchar# and a.trans_dt > to_date(...) 推荐的写法: select a.*, b.goods_title from iw_account_log a, beyond_trade_base b where a.TRANS_OUT_ORDER_NO = b.trade_no(+) and a.trans_code = '6003' and a.trans_account = #transacnt:varchar# and a.trans_dt > to_date(...) 10. 表连接分页查询的使用 a) 包含排序逻辑的分页查询写法,必须是三层 select 嵌套: 错误的写法: SELECT t1.* FROM (SELECT t.*, ROWNUM rnum FROM beyond_trade_base t WHERE seller_account = :1 AND gmt_create >= TO_DATE (:2, 'yyyy-mm-dd') AND gmt_create < TO_DATE (:3, 'yyyy-mm-dd') ORDER BY gmt_create DESC) t1 WHERE rnum >= :4 AND rnum < :5 正确的写法: SELECT t2.* FROM (SELECT t1.*, ROWNUM rnum FROM (SELECT t.* FROM beyond_trade_base t WHERE seller_account = :1 AND gmt_create >= TO_DATE (:2, 'yyyy-mm-dd') AND gmt_create < TO_DATE (:3, 'yyyy-mm-dd') ORDER BY gmt_create DESC) t1 WHERE ROWNUM <= :4) t2 WHERE rnum >= :5 b) 不包含排序逻辑的分页查询写法,则是两层 select 嵌套,但对 rownum 的范围指定仍 然必须在不同的查询层次指定: 错误的写法: SELECT t1.* FROM (SELECT t.*, ROWNUM rnum FROM beyond_trade_base t WHERE seller_account = :1 AND gmt_create >= TO_DATE (:2, 'yyyy-mm-dd') AND gmt_create < TO_DATE (:3, 'yyyy-mm-dd')) t1 WHERE rnum >= :4 AND rnum <= :5 正确的写法: SELECT t1.* FROM (SELECT t.*, ROWNUM rnum FROM beyond_trade_base t WHERE seller_account = :1 AND gmt_create >= TO_DATE (:2, 'yyyy-mm-dd') AND gmt_create < TO_DATE (:3, 'yyyy-mm-dd') AND ROWNUM <= :4) t1 WHERE rnum >= :5 c) 注意下面两种写法的逻辑含义是不同的: 按创建时间排序(倒序),然后再取前 10 条: SELECT t2.* FROM (SELECT t1.*, ROWNUM rnum FROM (SELECT t.* FROM sell_offer t WHERE owner_member_id = :1 AND gmt_create >= TO_DATE (:2, 'yyyy-mm-dd') AND gmt_create < TO_DATE (:3, 'yyyy-mm-dd') ORDER BY gmt_create DESC) t1 WHERE ROWNUM <= 10) t2 WHERE rnum >= 1 随机取 10 条,然后在这 10 条中按照交易创建时间排序(倒序): SELECT t1.* FROM (SELECT t.*, ROWNUM rnum FROM beyond_trade_base t WHERE seller_account = :1 AND gmt_create >= TO_DATE (:2, 'yyyy-mm-dd') AND gmt_create < TO_DATE (:3, 'yyyy-mm-dd') AND ROWNUM <= 10 ORDER BY gmt_create DESC) t1 WHERE rnum >= 1 d) 先连接后分页与先分页后连接 性能较差: SELECT t2.* FROM (SELECT t1.*, ROWNUM rnum FROM (SELECT a.*, b.receive_fee FROM beyond_trade_base a, beyond_trade_process b WHERE a.trade_no = b.trade_no AND a.seller_account = :1 AND a.gmt_create >= TO_DATE (:2, 'yyyy-mm-dd') AND a.gmt_create < TO_DATE (:3, 'yyyy-mm-dd') ORDER BY a.gmt_create DESC) t1 WHERE ROWNUM <= :4) t2 WHERE rnum >= :5 性能较好: SELECT /*+ ordered use_nl(a,b) */ a.*, b.receive_fee FROM (SELECT t2.* FROM (SELECT t1.*, ROWNUM rnum FROM (SELECT t.* FROM beyond_trade_base t WHERE seller_account = :1 AND gmt_create >= TO_DATE (:2, 'yyyy-mm-dd') AND gmt_create < TO_DATE (:3, 'yyyy-mm-dd') ORDER BY gmt_create DESC) t1 WHERE ROWNUM <= :4) t2 WHERE rnum >= :5) a, beyond_trade_process b WHERE a.trade_no = b.trade_no 后面这种写法的适用情况: 1)where 子句中的查询条件都是针对 beyond_trade_base 表的(否则得到的结果将 不相同) 2)关联 beyond_trade_process 表时,用的是该表的主键或者唯一键字段(否则将 改变结果集的条数) 11. Hint 的使用 a) sql 中的/*+ ordered use_nl(member offer)*/是 hint,用来确定 SQL 的执行计划,请在 DBA 确认后使用。 12. "<>"、"!="、"not in"、"exsits"和"not exists"的使用规范 a) 原则上一般禁止使用"<>"、"!="和"not in",而应该转换成相应的"="和"in"查询条件 错误的写法: select a.id,a.subject,a.create_type from product where status <> 'new' and owner_member_id = :1 正确的写法: select a.id,a.subject,a.create_type from product where status in ('auditing','modified','service-delete','tbd','user-delete','wait-for-audit') and owner_member_id = :1 错误的写法: select a.id,a.subject,a.create_type from product where create_type not in ('new_order','vip_add') and owner_member_id = :1 正确的写法: select a.id,a.subject,a.create_type from product where create_type = 'cust_add' and owner_member_id = :1 b) 原则上不允许使用"exsits"和"not exists"查询,应转换成相应的"等连接"和外连接来查 询 错误的写法: select a.id from company a where not exsits (select 1 from av_info_new b where a.id = b.company_id ) 正确的写法: select a.id from company a,av_info_draft b where a.id = b.company_id and b.company_id is null 错误的写法: select count from company a where exsits (select 1 from av_info_new b where a.id = b.company_id ) 正确的写法: select count from company a,av_info_draft b where a.id = b.company_id 注:在通过等连接替换 exsits 的时候有一点需要注意,只有在一对一的时候两者才 能较容易替换,如果是一对多的关系,直接替换后两者的结果会出现不一致情况。 因为 exsits 是实现是否存在,他不 care 存在一条还是多条,而等连接时返回所关联 上的所有数据。 c) 如有特殊需要无法完成相应的转换,必须在 DBA 允许的情况下使用"<>"、"!="、"not in"、"exsits"和"not exists" 13. 其它编写规范 a) 对表的记录进行更新的时候,必须包含对 gmt_modified 字段的更新,并且不要使用 dynamic 标记,如: 错误的写法: update BD_CONTACTINFO ...... GMT_MODIFIED = #gmtModified:TIMESTAMP# where ID = #id# 正确的写法(当然,这里更推荐直接更新为 sysdate): update BD_CONTACTINFO set GMT_MODIFIED = #gmtModified:TIMESTAMP# ...... where ID = #id# b) 不允许在 where 后添加 1=1 这样的无用条件,where 可以写在 prepend 属性里,如: 错误的写法: select count from BD_CONTRACT t where 1=1 ...... 正确的写法: select count from BD_CONTRACT t ...... c) 对大表进行查询时,在 SQLMAP 中需要加上对空条件的判断语句,具体可在遇到时 咨询 DBA,如: 性能上不保险的写法: select count from iw_user usr usr.iw_user_id = #userId:varchar# usr.email = #email:varchar# usr.cert_type = #certType:varchar# usr.cert_no = #certNo:varchar# 性能上较保险的写法(防止那些能保证查询性能的关键条件都为空): select count from iw_user usr usr.iw_user_id = #userId:varchar# usr.email = #email:varchar# usr.cert_type = #certType:varchar# usr.cert_no = #certNo:varchar# query not allowed 另外,对查询表单的查询控制建议使用 web 层进行控制而不是客户端脚本 (JAVASCRIPT/VBSCRIPT) d) 聚合函数常见问题 1) 不要使用 count(1)代替 count(*) 2) count(column_name)计算该列不为 NULL 的记录条数 3) count(distinct column_name)计算该列不为 NULL 的不重复值数量 4) count()函数不会返回 NULL,但 sum()函数可能返回 NULL,可以使用 nvl(sum(qty),0)来避免返回 NULL e) NULL 的使用 1) 理解 NULL 的含义,是"不确定",而不是"空" 2) 查询时,使用 is null 或者 is not null 3) 更新时,使用等于号,如:update tablename set column_name = null f) STR2NUMLIST、STR2VARLIST 函数的使用: 1) 适用情况:使用唯一值(或者接近唯一值)批量取数据时 2) 编写规范:a 表必须放在 from list 的第一位,并且必须在 select 后加上下面的 hint 正确的写法: select /+ ordered use_nl(a,b) */ b. from TABLE(CAST(str2varlist(:1) as vartabletype)) a, beyond_trade_base b where a.column_value = b.trade_no; 二. 格式规范 1.注释说明 a) 本注释说明主要用于 PL/SQL 程序及其它 SQL 文件,其它可作参考; b) SQLPLUS 接受的注释有三种: ―― 这儿是注释 /* 这儿是注释 */ REM 这儿是注释 c) 开始注释,类似 JAVAK 中的开始注释,主要列出文件名,编写日期,版权说明,程 序功能以及修改记录: REM REM $Header: filename, version, created date,auther REM REM Copyright REM REM FUNCTION REM function explanation REM REM NOTES REM REM MODIFIED (yy/mm/dd) REM who when - for what, recently goes first d) 块注释,如表注释,PROCEDURE 注释等,同 JAVA: /* * This table is for TrustPass * mainly store the information * of TrustPass members */ 注意: 在“/*”后应当另起一行,或与其后描述有间隔,否则在 SQLPLUS 中会有问题。 e) 单行注释,如列注释: login_id VARCHAR2(32) NOT NULL, -- 会员标识 2.缩进 低级别语句在高级别语句后的,一般缩进 4 个空格: DECLARE v_MemberId VARCHAR2(32), BEGIN SELECT admin_member_id INTO v_MemberId FROM company WHERE id = 10; DBMS_OUTPUT.PUT_LINE(v_MemberId); END; 同一语句不同部分的缩进,如果为 sub statement,则通常为 2 个空格,如果与上一句某 部分有密切联系的,则缩至与其对齐: BEGIN FOR v_TmpRec IN (SELECT login_id, gmt_created, -- here indented as column above satus FROM member -- sub statement WHERE site = 'china' AND country='cn' ) LOOP NULL; END LOOP; END; 3.断行 a) 一行最长不能超过 80 字符 b) 同一语句不同字句之间 c) 逗号以后空格 d) 其他分割符前空格 SELECT offer_name ||',' ||offer_count as offer_category, id FROM category WHERE super_category_id_1 = 0;  附录:Oracle 关键字 ACCESS DECIMAL INITIAL ON START ADD NOT INSERT ONLINE SUCCESSFUL ALL DEFAULT INTEGER OPTION SYNONYM ALTER DELETE INTERSECT OR SYSDATE AND DESC INTO ORDER TABLE ANY DISTINCT IS PCTFREE THEN AS DROP LEVEL PRIOR TO ASC ELSE LIKE PRIVILEGES TRIGGER AUDIT EXCLUSIVE LOCK PUBLIC UID BETWEEN EXISTS LONG RAW UNION BY FILE MAXEXTENTS RENAME UNIQUE FROM FLOAT MINUS RESOURCE UPDATE CHAR FOR MLSLABEL REVOKE USER CHECK SHARE MODE ROW VALIDATE CLUSTER GRANT MODIFY ROWID VALUES COLUMN GROUP NOAUDIT ROWNUM VARCHAR COMMENT HAVING NOCOMPRESS ROWS VARCHAR2 COMPRESS IDENTIFIED NOWAIT SELECT VIEW CONNECT IMMEDIATE NULL SESSION WHENEVER CREATE IN NUMBER SET WHERE CURRENT INCREMENT OF SIZE WITH DATE INDEX OFFLINE SMALLINT CHAR VARHCAR VARCHAR2 NUMBER DATE LONG CLOB BLOB BFILE INTEGER DECIMAL SUM COUNT GROUPING AVERAGE TYPE  Oracle 名词解释 a) PK:Primary Key,主键 b) UK:Unique Key,唯一性索引 c) FK:Foreign Key,外键 d) DBLINK:Database Link,数据库链接  Mysql  数据库整体设计规范(必读) 1.设计 1. 一般都使用 INNODB 存储引擎,除非读写比率<1%,才考虑使用 MYISAM 存储引擎;其 他存储引擎请在 DBA 的建议下使用。 2. Stored procedure (包括存储过程,函数,触发器)对于 MYSQL 来说还不是很成熟, 没有完善的出错记录处理,不建议使用。 3. UUID(),USER()这样的 MYSQL INSIDE 函数对于复制来说是很危险的,会导致主备数 据.不一致。所以请不要使用。如果一定要使用 UUID 作为主键,让应用程序来产生。 4. 请不要使用外键约束,如果数据存在外键关系,请在程序层面实现。 5. 如果应用使用的是长连接,应用必须具有自动重连的机制。但请避免每执行一个 SQL 去检查一次 DB 可用性。 6. 如果应用使用的是长连接,应用应该具有连接的 TIMEOUT 检查机制,及时回收长时间 没有使用的连接。 TIMEOUT 时间一般建议为 20min。 7. 我们所有的 MySQL 数据库除历史原因外,都必须采用 UTF8 编码。 8. Mysql 对 DDL 支持很差,表结构推荐设计为 Key-Value 结构。如果是关系型结构的数 据库,请尽量预留一些字段,如 value1 ,value2 ,value3。 9. Mysql 用户名与数据库名字一样。 2.命名 a) 命名应使用富有意义的英文词汇,多个单词组成的,中间以下划线分割。 b) 命名只能使用英文字母,数字和下划线。 c) 命名避免使用 Mysql 的保留字(详见附录 A)和系统关键字。 d) 命名长度以不超过 15 个字符为宜(避免超过 20)。 e) 命名全部采用小写,并且名称前后不能加引号。  数据库对象设计规范 1. 表 设计 a) 在设计时尽量包含两个日期字段:gmt_created(创建日期),gmt_modified(修改日期)且 非空, 对表的记录进行更新的时候,必须包含对 gmt_modified 字段的更新。 b) 必须要有主键,主键尽量用自增字段类型,推荐类型为 INT 或者 BIGINT 类型。 c) 需要多表 join 的字段,数据类型保持绝对一致。 d) Mysql 的表尽量设置成 KV(Key-Value)结构,这样便于扩展和维护。 e) 当表的字段数非常多时,可以将表分成两张表,一张作为条件查询表,一张作为详 细内容表(主要是为了性能考虑)。 f) 当字段的类型为枚举型或布尔型时,建议使用 char(1)类型。 g) 同一表中,所有 varchar 字段的长度加起来,不能大于 65535.如果有这样的需求,请 使用 TEXT/LONGTEXT 类型。 h) 由于 MYSQL 表 DDL 维护成本很高,所以在适当的时候,可以有一定的字段容余。 比如:Value1,Value2,Value3 这样的字段。 命名 a) 同一个模块的表尽可能使用相同的前缀,表名尽可能表达含义,例如: CRM_SAL_FUND_ITEM。 b) 字段命名应尽可能使用表达实际含义的英文单词或缩写, 如,公司 ID,不要使用:corporation_id, 而用:corp_id 即可。 c) 布尔值类型的字段命名为 is+描述。如 member 表上表示是否为 enabled 的会员的字 段命名为 IsEnabled。 常用字段类型 TINYINT 1 个字节, -128 to 127 || 0 to 255 SMALLINT 2 个字节, -32768 to 32767 || 0 to 65535 MEDIUMINT 3 个字节, -8388608 to 8388607 || 0 to 16777215. INT, INTEGER 4 个字节, -2147483648 to 2147483647 || 0 to 4294967295. BIGINT 8 个字节, -9223372036854775808 to 922337203685477580 0 to 18446744073709551615 DECIMAL(P,S) 定点数(以字符串形式存放) 默认:P 为 10,S 为 0,最大 65 位 DATE 范围'1000-01-01'到'9999-12-31' 格式'YYYY-MM-DD' (3 字节) Time 范围'-838:59:59'到'838:59:59' 格式'HH:MM:SS' (3 字节) DATETIME 范围 '1000-01-01 00:00:00' 到 '9999-12-31 23:59:59' 格式 'YYYY-MM-DD HH:MM:SS' (8 字节) TIMESTAMP 范围 '1970-01-01 00:00:00' 到 2037 年 格式'YYYY-MM-DD HH:MM:SS' 宽度固定为 19 个字符(4 字节) 不建议使用。 VARCHAR(n) 变长字符串,65532>n>4, 注意,n 是字符数,而不是字节数 CHAR(n) 定长字符串,n 范围(0,255), 如果不是定长的数据,n<=4 时才使用 TINYBLOB, TINYTEXT 存储 L+1 个字节,其中 L < 2^8 BLOB, TEXT 存储 L+2 个字节,其中 L < 2^16 MEDIUMBLOB, MEDIUMTEXT 存储 L+3 个字节,其中 L < 2^24 LONGBLOB, LONGTEXT 存储 L+4 个字节,其中 L < 2^32 字段注释 a) 标准字段注释由一组"@"开头的标签+空格+文本组成。 以 MD_USER 表的部分字段为例: Name Type Comments PARTY_ID VARCHAR(20) @desc 主键 ID CORP_ID VARCHAR(20) @desc 用户所在公司 ID @fk md_corp_id STATUS VARCHAR(20) @desc 状态 @values disable|enable:未激活状 态|激活状态 IS_PRI_ACCOUNT CHAR(1) @desc 是否为主账号。后台生成 UK 时使用 @values y|n:是帐号,非主帐号 @logic 一个公司内部,有且仅有 一个主账号存在 b) 注释标签说明 标签名 中文含义 必填 备注 @desc 字段中文描述 Yes @fk 字段对应的外键字段 @values 取值范围说明。多个值以"|" 分隔 如此字段的值由系统自动生成,可忽略不书写。 @sample 数据范本 对于复杂数据格式,最好给一个数据范本。 @formula 计算公式 写明该字段由哪些字段以何种公式计算得到。 @logic 数据逻辑 简要写明该字段的数据是在何种业务规则下,如何变 化的。 @redu 标识此字段冗余 @depr 标识此字段已废弃 简要写明:废弃人 废弃日期 废弃原因 2. 索引 设计 a) Bitmap 索引通常不适合我们的环境。 b) 索引根据实际 SQL,由 DBA 创建。 c) 不要创建带约束的索引,所有的约束效果都通过显示创建约束然后再 using index 一 个已经创建好的普通索引来实现。 命名 a) __ind,各部分以下划线(_)分割。 b) 多单词组成的 column name,取前几个单词首字母,加末单词组成 column_name。如: sample 表 member_id 上的索引:sample_mid_ind。 3. 约束 设计 a) 主键最好是无意义的,,统一由 Auto-Increment 字段生成整型数据,不建议使用组合 主键。 b) 若要达到唯一性限制的效果,不要创建 unique index,必须显式创建普通索引和约束 (pk 或 uk),即先创建一个以约束名命名的普通索引,然后创建一个约束,用 using index ...指定索引。 c) 当删除约束的时候,为了确保不影响到 index,最好加上 keep index 参数。 d) 主键的内容不能被修改。 e) 外键约束一般不在数据库上创建,只表达一个逻辑的概念,由程序控制。 f) 当万不得已必须使用外健的话,必须在外健列创建 INDEX。 命名 a) 主键约束: _pk 结尾,_pk; b) unique 约束:_uk 结尾,__uk; c) check 约束: _ck 结尾,__ck; d) 外键约束: _fk 结尾,以 pri 连接本表与主表,_pri__fk; 4. 触发器 命名 a) _A(After)B(Before)I(Insert)U(Update)D(Delete)_trg。 b) 若是用于同步的触发器以 sync 作为前缀:sync__trg。 5. 过程、函数 设计 a) 如果要在 MYSQL 里使用存储过程类的技术,请务必和 DBA 沟通确认。 命名 a) 过程以 proc_开头,函数以 func_开头。 b) 变量命名约定:本地变量以 v_为前缀,参数以 p_为前缀,可以带_I(输入),_O(输出)、 _IO(输入输出)表示参数的输入输出类型。  SQL 开发规范 一.编码规范 1. 使用 SQL 操作数据库前,必须由 use DB_name 开始 Use Test ; Insert into Table_name values ( … ) ; Commit; 2. 如果需要事务的支持,在确认使用了 innodb 存储引擎的前提下,在数据库连接时,先 关闭自动提交 比如,设定 set auto_commit =0 ; 3. 写到应用程序里的 SQL 语句,禁止一切 DDL 操作 例: Create table , Drop table , Create database , Drop database , Alter table ,grant … … 如有特殊需要,必需与 DBA 协商同意方可使用。 4. 获取当前时间请使用 now(),不要用 sysdate()来代替 这对复制来说是很危险的,会导致主从数据不一致的情况; 因为 sysdate,取的是系统主机时间,在 BINLOG 会原文传输, 当在应用时会与主库产生差异。 5. 写 SQL 的时候一定要给每个字段指定表名做前缀 比如: select a.id,a.name from test a; 好处是 一来带来性能的提升, 二来可以避免一些错误的发生。 6. 在 iBatis 的 SqlMap 文件中绑定变量使用 “#var_name#”表示,替代变量使用 “$var_name$” 所有需要动态 Order By 条件的 Query,在使用替代变量过程中,需要将可能传入的内 容以枚举类写死在代码中,禁止接受任何外部传入内容。 7. 请不要写 select * 这样的代码,指定需要的字段名 8. Mysql 对日期(datetime)允许“不严格”语法 任何标点符都可以用做日期部分或时间部分之间的间割符。 例如, '98-12-31 11:30:45'、'98.12.31 11+30+45'、'98/12/31 11*30*45'和'98@12@31 11^30^45' 是等价的。] 我们自己约定一种写法,与 Oracle 相通: '2009-12-31 11:30:45' 9. Mysql 的日期与字符是相同的,所以不需要做另外的转换 例: Select e.username from employee e where e.birthday >=’1998-12-31 11:30:45’ 10. 避免多余的排序。使用 GROUP BY 时,默认会进行排序,当你不需要排序时,可以使用 order by null Select product,count(*) cnt from crm_sale_detail group by product order by null; 11. 避免在 where 子句中对字段施加函数 a) 通常,不允许在字段上添加函数或者表达式,这样将导致索引失效,如: 错误的写法: select * from iw_account_log where substr(username,1,5)=’abcde’ 正确的写法: select * from iw_account_log where username like ’abcde%’ b) 如果是业务要求的除外,但需要在编写时咨询 DBA c) 特别注意,当表连接时,用于连接的两个表的字段如果数据类型不一致,则必须在 一边加上类型转换的函数 12. 严格要求使用正确类型的变量,杜绝 Mysql 做隐式类型转换的情况 13. 全模糊查询无法使用 INDEX,应当尽可能避免 比如:select * from table where name like '%jacky%'; 14. 表连接规范 a) 所有非外连接 SQL(即 INNER JOIN),请把关联表统一写到 FROM 字句中,关 联条件与过滤条件统一写到 WHERE 字句中 b) 出于代码的可读性原因,所有外连接 SQL 语句中,请一律使用 LEFT JOIN,禁用 RIGHT JOIN c) 另外,请注意 LEFT JOIN 字句中,右边位置表的条件书写位置不同的影响: SELECT A.rolename,A.gmt_create,B.nickname FROM gl_role A LEFT JOIN gl_roledetail B ON A.ID=B.roleid AND B.roleID=2; +-------------+---------------------+----------+ | rolename | gmt_create | nickname | +-------------+---------------------+----------+ | 163.com | 0000-00-00 00:00:00 | test2 | | sina.com | 0000-00-00 00:00:00 | NULL | | hotmail.com | 0000-00-00 00:00:00 | NULL | | 126.com | 2009-08-20 18:20:18 | NULL | +-------------+---------------------+----------+ SELECT A.rolename,A.gmt_create,B.nickname FROM gl_role A LEFT JOIN gl_roledetail B ON A.ID=B.roleid WHERE B.roleID=2; +----------+---------------------+----------+ | rolename | gmt_create | nickname | +----------+---------------------+----------+ | 163.com | 0000-00-00 00:00:00 | test2 | +----------+---------------------+----------+ 15. 表连接分页查询的使用 a) 常规分页语句写法(start:起始记录数,page_offset:每页记录数): SELECT ID,username FROM gl_user WHERE username like '%@163.com' ORDER BY M.gmt_create LIMIT start, page_offset; b) 多表 Join 的分页语句,如果过滤条件在单个表上,需要先分页,再 Join: 低性能写法: SELECT M.username,P.rolename FROM gl_user M INNER JOIN gl_role P ON M.ID=P.userid WHERE username like '%@163.com' ORDER BY M.gmt_create LIMIT start, page_offset; 高性能写法: SELECT M.username,P.rolename FROM (SELECT ID,username FROM gl_user WHERE username like '%@163.com' ORDER BY M.gmt_create LIMIT start, page_offset)M,gl_role P WHERE M.ID=P.userid; 这样写的前提是关联的表之间记录一一对应,否则可能会返回的记录数目少于或多余 page_offset 的值。 16. "join"、"in"、"not in"、"exsits"和"not exists"的使用 a) 比较 IN,EXISTS,JOIN 按效率从好到差排序: 字段上有索引 : EXISTS, IN, JOIN 字段上没有索引: JOIN, EXISTS ,IN b) Anti-Joins: NOT IN ,NOT EXISTS, LEFT JOIN 按效率从好到差排序: 字段上有索引 : LEFT JOIN, NOT EXISTS, NOT IN 字段上没有索引: NOT IN, NOT EXISTS, LEFT JOIN 17. 其它编写规范 a) 对表的记录进行更新的时候,必须包含对 gmt_modified 字段的更新; b) 不允许在 where 后添加 1=1 这样的无用条件,where 可以写在 prepend 属性里,如: 错误的写法: select count from BD_CONTRACT t where 1=1 ...... 正确的写法: select count from BD_CONTRACT t ...... c) 对大表进行查询时,在 SQLMAP 中需要加上对空条件的判断语句,具体可在遇到时 咨询 DBA,如: 性能上不保险的写法: select count from iw_user usr usr.iw_user_id = #userId:varchar# usr.email = #email:varchar# usr.cert_type = #certType:varchar# usr.cert_no = #certNo:varchar# 性能上较保险的写法(防止那些能保证查询性能的关键条件都为空): select count from iw_user usr usr.iw_user_id = #userId:varchar# usr.email = #email:varchar# usr.cert_type = #certType:varchar# usr.cert_no = #certNo:varchar# query not allowed 另外,对查询表单的查询控制建议使用 web 层进行控制而不是客户端脚本 (JAVASCRIPT/VBSCRIPT) d) 聚合函数常见问题 1) 不要使用 count(1)代替 count(*) 2) count(column_name)计算该列不为 NULL 的记录条数 3) count(distinct column_name)计算该列不为 NULL 的不重复值数量 4) count()函数不会返回 NULL,但 sum()函数可能返回 NULL,可以使用 ifnull(sum(qty),0)来避免返回 NULL e) NULL 的使用 1) 理解 NULL 的含义,是"不确定",而不是"空" 2) 查询时,使用 is null 或者 is not null 3) 更新时,使用等于号,如:update tablename set column_name = null 二.格式规范 1.注释说明 a) 本注释说明主要用于 Mysql Client 程序及其它 SQL 文件,其它可作参考; b) SQL 接受的注释有三种: -- 这儿是注释 (注意,第 2 个破折号后面至少跟一个空格符) /* 这儿是注释 */ # 这儿是注释 c) 下面的例子显示了 3 种风格的注释: mysql> SELECT 1+1; # This comment continues to the end of line mysql> SELECT 1+1; -- This comment continues to the end of line mysql> SELECT 1 /* this is an in-line comment */ + 1; mysql> SELECT 1+ /* this is a multiple-line comment */ 2.缩进 低级别语句在高级别语句后的,一般缩进 4 个空格: DECLARE v_MemberId VARCHAR(32), BEGIN SELECT admin_member_id INTO v_MemberId FROM company WHERE id = 10; SELECT v_MemberId ; END; 同一语句不同部分的缩进,如果为 sub statement,则通常为 2 个空格,如果与上一句某 部分有密切联系的,则缩至与其对齐: BEGIN FOR v_TmpRec IN (SELECT login_id, gmt_created, -- here indented as column above satus FROM member -- sub statement WHERE site = 'china' AND country='cn' ) LOOP NULL; END LOOP; END; 3.断行 a) 一行最长不能超过 80 字符 b) 同一语句不同字句之间 c) 逗号以后空格 d) 其他分割符前空格 SELECT concat(offer_name,',', offer_count as offer_category, id) FROM category WHERE super_category_id_1 = 0;  附录:Mysql 保留字 ADD DEFAULT INSERT NULL SQL_CALC_FOUN D_ROWS ALL DELAYED INT NUMERIC SQL_SMALL_RES ULT ALTER DELETE INT1 ON SQLEXCEPTION ANALYZE DESC INT2 OPTIMIZE SQLSTATE AND DESCRIBE INT3 OPTION SQLWARNING AS DETERMINISTIC INT4 OPTIONALLY SSL ASC DISTINCT INT8 OR STARTING ASENSITIVE DISTINCTROW INTEGER ORDER STRAIGHT_JOIN BEFORE DIV INTERVAL OUT TABLE BETWEEN DOUBLE INTO OUTER TERMINATED BIGINT DROP IS OUTFILE THEN BINARY DUAL ITERATE PRECISION TINYBLOB BLOB EACH JOIN PRIMARY TINYINT BOTH ELSE KEY PROCEDURE TINYTEXT BY ELSEIF KEYS PURGE TO CALL ENCLOSED KILL RAID0 TRAILING CASCADE ESCAPED LABEL RANGE TRIGGER CASE EXISTS LEADING READ UNDO CHANGE EXIT LEAVE READS UNION CHAR EXPLAIN LEFT REAL UNIQUE CHARACTER FETCH LIKE REFERENCES UNLOCK CHECK FLOAT LIMIT REGEXP UNSIGNED COLLATE FLOAT4 LINEAR RELEASE UPDATE COLUMN FLOAT8 LINES RENAME USAGE CONDITION FOR LOAD REPEAT USE CONNECTION FORCE LOCALTIME REPLACE USING CONSTRAINT FOREIGN LOCALTIMESTAMP REQUIRE UTC_DATE CONTINUE FROM LOCK RESTRICT UTC_TIME CONVERT FULLTEXT LONG RETURN UTC_TIMESTAMP CREATE GOTO LONGBLOB REVOKE VALUES CROSS GRANT LONGTEXT RIGHT VARBINARY CURRENT_DATE GROUP LOOP RLIKE VARCHAR CURRENT_TIME HAVING LOW_PRIORITY SCHEMA VARCHARACTER CURRENT_TIMESTAMP HIGH_PRIORITY MATCH SCHEMAS VARYING CURRENT_USER HOUR_MICROSECOND MEDIUMBLOB SECOND_MICROSECOND WHEN CURSOR HOUR_MINUTE MEDIUMINT SELECT WHERE DATABASE HOUR_SECOND MEDIUMTEXT SENSITIVE WHILE DATABASES IF MIDDLEINT SEPARATOR WITH DAY_HOUR IGNORE MINUTE_MICROSEC OND SET WRITE DAY_MICROSECOND IN MINUTE_SECOND SHOW X509 DAY_MINUTE INDEX MOD SMALLINT XOR DAY_SECOND INFILE MODIFIES SPATIAL YEAR_MONTH DEC INNER NATURAL SPECIFIC ZEROFILL DECIMAL INOUT NO_WRITE_TO_BIN LOG SQL FALSE DECLARE INSENSITIVE NOT SQL_BIG_RESULT TRUE  Mysql 名词解释 a) PK:Primary Key,主键 b) UK:Unique Key,唯一性索引 c) FK:Foreign Key,外键