赵丽红的空间

我们一直在努力....

数据库设计规范(五)

标签: 数据库命名 数据库优化 SQL编码规范

1.数据库命名规范

1.1.命名规范

  • 命名对象包括文件、类、过程、函数、模块、变量。
  • 尽量使用英文。在英文不好表达其含义时,可使用汉语拼音。
  • 名称应能反映所代表对象的实际内容和作用。可使用多个词组合,但不要使用人名、项目组名。
  • 只可使用字母、数字和连字符‘_’,但第一个字符必须是字母。
  • 多个词组合较长时可使用缩写,但尽可能使用标准的缩写。在一个系统中要建立缩语表以规范缩写的使用。
  • 命名时请考虑名字的唯一性和含义的准确性,不得使用非常相近的名字表示几个不同含义的对象。
  • 名称中字母的大小写在实施细则中规定,在同一个项目中必须统一。

1.2.相关对象标识

与特定内容相关的一组数据库对象,赋予一个指定的3字符的英文标识,便于识别和查找。如与客户信息CustomerInfo相关的数据库对象,则用mci标识;如与账户费用AccountExpense相关数据库对象,则用eae标识。

1.3.内容标识

与对象表示的内容相关的名称。尽量使用描述性的、完整的、能够描述每个对象的用途的名字;如果单词很长,可使用缩写。
用英文单词或多个单词组合(尽量避免使用汉语拼音,拒绝使用汉字)命名,每单词首字母大写。
用缩写时要使用标准的或通用的写法,同时要建立缩写词典以便标准化。
对于专业词汇,要建立词汇词典,以便统一和识别。

1.4.命名规则

数据库对象 前缀 相关对象标识 内容标识 后缀
数据库     <内容标识>  
数据表 t_ <相关对象标识>_ <内容标识> _[<后缀>]
字段     <内容标识>  
视图 v_ <相关对象标识>_ <内容标识>  
函数 f[t]_ <相关对象标识>_ <内容标识>  
存储过程 p_ <相关对象标识>_ <内容标识>  
触发器 tr[i][d][u]_ <表名> <内容标识>  
索引 Ix_ <表名> <内容标识>  
主键 pk_ <表名> <内容标识>  
外键 fk_ <表名> <内容标识>  
注:
① 只在需要时使用,用以表示同名表间的差别。如主表_m,明细表_d,年度_2000/_2010。
②f:返回值的函数;ft:返回列表的函数。
③i:插入触发;d:删除触发;u:更新触发
④ 不要使用数据库系统的保留词。对于可能在多个表中出现的,或常用的名称,要建立字段名词典,统一数据类型和长度,以便标准化。

2.数据库编码规范

2.1.编码规范总则

通过建立软件编码规范,形成代码约定,提高程序的可读性、一致性、可维护性,共享性和继承性,保证程序质量和可靠性,延长程序的生命周期。

2.1.1.文件结构

(1).每个程序文件由标题、主体内容和附加说明三部分组成。
(2).标题:文件最前面的注释说明,其内容主要包括:程序名、作者、程序目的/功能、版本信息、版本号、生成日期、修改日志、依赖性等。必要时应有更详尽的说明。
(3).主体内容:为文件源代码部分基本上由预处理语句、类型定义、变量定义、实现等部分组成。
(4).附加说明:文件末尾的补充说明,如参考资料等,若内容不多也可放在标题部分的最后。

2.1.2.排版

(1). 要采用缩进风格编写,缩进使用半角空格键,每级缩进的空格数为4个。
(2).  每个程序块或语句块的起止符(如C/C++语言的大括号“{”和“}”,SQL子查询的“(”和“)”)应各独占一行并且与包含的语句有相同的缩进。
(3).  函数或过程的开始、结构的定义及循环、判断等语句中的代码都要采用缩进风格。
(4).  较长的语句要分成多行书写,一般每行为120字符。长语句要在低优先级操作符处划分新行,操作符放在新行之首。划分出的新行相对首行要缩进。不得在单词或词组中间分行。
(5).  独立的程序块之间、变量说明之后、或是程序说明跟主体之间必须加空行。
(6).   一行只写一条语句,不允许把多个短语句写在一行中。

2.1.3.注释

为什么要使用注释:
  • 方便代码的阅读和维护(修改),但不会增加可执行文件的大小和处理时间;
  • 我们在维护代码时,多数时间不是编写或修改代码,而是在理解原有代码(有相当部分是自己编写的)。而好的注释可以大大减少理解代码的时间。
 
(1)注释的原则是有助于对程序的阅读理解,注释不宜太多也不能太少,注释语言必须易懂、简洁、含义准确,防止注释二义性。
(2).  函数或程序的头部应进行注释,列出:函数或程序名、功能说明、参数说明、作者、创建时间、修改人、修改日期。
(3).   边写代码边注释,修改代码同时修改相应的注释,以保证注释与代码的一致性。没有用的注释要及时删除。
(4).  避免在注释中使用缩写,特别是非常用缩写。如果使用缩写,应对缩写进行必要的说明。
(5).  注释应与其描述的代码邻近,可放在多行代码的上方或单行代码的行尾,不可放在下方;与被解释的代码之间不得有空行;如放于上方则需与其上面的代码用空行隔开。
(6).  注释与所描述内容使用相同的缩进。
(7).  避免在一行代码或表达式的中间插入注释。
(8).  当代码段较长,特别是多重嵌套时,可在程序块的结束行右方加注释标记,以表明某程序块的结束。
(9).  使用中文注释。

2.2.SQL开发规范

规范sql的编写格式,提高SQL语句的可读性,共享性和执行效率。
大部分业务系统需要与数据库进行交互,与数据库交互的主要方式就是SQL语句,编写规范的SQL语句不但利于阅读,而且被数据库重复使用的几率也较大,执行效率相对较高,编写的好的SQL与编写的差的SQL在执行性能上可能会差几倍甚至几千几万倍,因此养成好的SQL编写规范对于提高项目质量及提高开发人员自身素质有着潜在的极大的影响。

2.2.1.书写

SQL书写遵守如下规范:
  • 在同一个项目中,为了最大限度实现SQL的共享,要求书写sql语句时大小写要一致,为了阅读方便和统一起见,所有SQL语句全部小写(如SQL谓词,字段名,表名等),常量除外,常量可以按需要书写。
举例:下面两个相同的语句除常量外都要统一起来。
1)selectnamefromemp;
2)select'NAME' fromempwhereemp_no='QD001'?    
  • SQL语句尽可能放在一行,若SQL太长放在一行中影响阅读时可分多行,但要保持缩进一致,缩进可用TAB或者空格,但TAB数和空格数最好一致。
 
  • SQL语句中,各谓词之间以空格分割的,尽量保持空格数量一致,即若用一个空格分割,则全部都用一个空格分割,便于数据库能够共享。
       
  • 能使用绑定变量的,尽量使用绑定变量,尤其是在前台程序中.
 
  • 对下面列出的情况,慎重使用绑定变量:
1)  列值倾斜严重,如:某一状态列大部分值是‘1’,只有极少数值为’2’,这种情况不宜用绑定变量,而应该用常量,便于数据库使用柱状图统计信息。
2)  日期时间列。
 
总之:书写SQL的目标是若sql的用途是一样的,则sql应该完全一致,包括空格,大小写。下面的语句由于写法不完全相同,数据库会理解为4条不同的语句从而导致重复编译,降低了性能。
1)selectnamefromempwhereemp_no='111'
2)SelectnameFromempWhereemp_no='111'
3)selectNamefromEMPwhereemp_no='111'
4)selectnamefromempwhereEMP_NO='111'
 
下面的语句,由于语句规范,可以只编译一次。
1)selectnamefromempwhereemp_no='111'
2)selectnamefromempwhereemp_no='222'
3)selectnamefromempwhereemp_no='333'
4)selectnamefromempwhereemp_no=:b1

2.2.2.     注释

(1).开头注释
所有的过程、函数、触发器、包都应该在开头有注释,
注释中要列出对象名称;完成功能简述;调用模块,调用时机;
创建日期;作者信息;历次修改日期;历次修改人;历次修改原因和其它作者认为重要的内容,在每次修改的注释之间建一空行。
 
举例如下:
/*************************************************************
名称:PRO_WO_MULTI_REPAIR_JOB
功能:多次维修判定程序
调用:自动统计月结前,后台调用
作者:xxx
时间:2009-02-20
 
修改人:xxx
修改时间:2009-03-08
修改内容:重写部分SQL优化性能
 
修改人:xxx
修改日期:2010-03-23
修改内容:增加判断条件
 
*************************************************************/
 
(2).块注释
对于复杂的语句块,必须提供块注释,清晰描述该语句块的功能逻辑、数据结构以及算法;块注释应该和它们所描述的代码具有一样的缩进格式;块注释之首应该有一个空行,用于把块注释和代码分割开来;块注释结构如下所示:
/*
    计算配送能力系数:
    派工系数=a*b+c*d+e*f
    a:考评成绩,取xx的考评成绩
    c:承担度度,根据当前遗留量和总服务能力计算所得,
    c=当前遗留配送量/总配送能力
     ...
*/
 
(3).行注释
对于复杂的SQL语句,必须提供行注释,清晰描述该SQL语句功能以及目的;行注释结构如下所示:
--计算动态能力得分,当负荷率超高时,可能出现负值,注意处理。
 
添加注释时要注意:注释中包含GO 命令时会生成一个错误消息。

2.2.3.     存储过程和函数

(1).命名规范
存储过程/函数的命名(代码)使用以下方式:
存储过程名=  p_ + 模块代码+ 存储过程代码
函数名=  f_ + 模块代码+ 函数代码
存储过程以表现其功能的简洁语言进行命名,定义好后,放置于每一个Diagram的特定区域(一般置于右边部分的上边)。
 
(2).存储过程/函数头定义规范
包括过程/函数名称定义、参数定义、注释说明、变量定义四部分。
  • 参数定义
所有的参数必须显示指出变量类型,对于返回参数,必须指出其方向;
参数变量定义格式为:“a“+参数类型简写+“_”+参数名称,其中a是单词argument(参数)的第一个字母,参数类型如下:
文本型:s
日期时间型:d
数字型:n
整形:i
布尔型:b
举例:as_name 表示文本型
      ad_birthdate 表示日期时间型
      an_age   表示数字型
 
  • 注释说明
见前面描述。
 
(3).变量定义规范
1).变量的命名
除游标变量以外,所有的变量命名采用此方式:
变量名= “v_”+ 变量名称
游标变量采用:
变量名= “cur”+ 变量名(第一个字母大写)。
其中变量名称由代表变量确切含意的单词代码组合而成,每一个单词的首字母根据阅读的方便性决定是否需要大写。
举例:
变量名:v_EmpName
游标名:curNetlist
 
2).变量类型
定义变量的类型时,尽量采用显示定义的方式。尽量将变量的定义分开来,并进行格式化,以便程序代码的阅读。例如:
v_UserName   CHAR(10)
v_SEX        CHAR(1)
 
(4).变量注释
原则上要求对于一般变量定义时必须加上注释,如变量有特定的值范围,则必须显示描述各确切值及其含意说明;
 
(5).存储过程/函数体定义规范
在过程/函数的定义中,最重要的是过程代码的严谨性和可读性,主要包括以下的注意事项:
1).如无特殊需要,SQL中涉及的所有内容都要小写。
2).原则上要求所有的SQL语句必须在其前面加上注释,对于IF/CASE等流程控制语句,必须在语句前/后说明控制处理和可能的流程方向;
3).所有的赋值语句要求变量与运算符之间要有空格。如:v_Count := v_Count + 1,并保持适当的对齐;
4).尽量避免复杂SQL,尤其是关联多个大表的SQL,对于需要关联多个大表的SQL最好分解成小的SQL分步处理,避免出现性能问题,对于复杂的语句块之间,要求中间加入空行;
5).所有可能的返回结果,必须在过程体中显示定义和说明,并在注释中说明。在其它的过程、代码中调用本过程/函数时,必须在其代码中处理所有的各种可能的返回结果;
6).锁定数据时,尽可能只锁定要操作的数据行,避免锁定整个表,限制使用local table等DDL语句,若要对数据表、数据行加锁时,需要考虑由此导致的并发操作失败的处理;
7).所有的过程/函数代码在编写之前,必须要有设计原型及其说明;
 
(6).  存储过程/函数错误定义
 
1).所有的DML语句必须考虑死锁、并发、主关键字不唯一等的出错异常处理,应该对所有可能出现的异常进行捕获、编码处理,并注明异常的内容;
2).异常处理有两种需要考虑的问题,一是事务的完整性;二是错误的出错日志及返回处理。所有异常的根据其类型和等级进行以下的处理:
类型 内容 处理  
系统级 数据库级发出的底层的异常,如主关键字不唯一冲突等; 返回定义错误代码为其它错误,完全回退事务,登录异常日志;  
应用级 应用系统定义并发出的异常,如指定记录不存在; 定义应用级错误代码,根据实际情况处理事务;  
 
(7). 存储过程/函数使用原则
1).存储过程功能尽量保持独立、复用,但尽量避免嵌套调用,充分考虑其性能体现;
2).使用前台代码调用存储过程时,必须考虑返回传入参数的有效性、代码的处理及展示、事务完整性处理;
3).直接使用参数作为变量处理,不要在存储过程中定义参数同样含义的变量使用,避免由于值的不同导致性能问题。

2.2.4.触发器

(1).命名规范
触发器不同类型的命名结构如下
操作 时机 命名结构
Insert After ai_表名
Update After au_表名
Delete After ad_表名
 
(2).触发器编写规范
请参见存储过程/函数编写规范。
 
(3).异常
所有过程和函数处理必须考虑可能出现的错误,一是数据库或操作系统底层错误;二是由于业务逻辑造成的错误;三是无反应或超时;四是输入参数不在指定范围内。
设计所有的过程时,必须考虑出错后的恢复。

2.3.优化

2.3.1.SQL语句原则

  • Where子句尽量避免使用函数;
  • 避免在ORDER BY子句中使用表达式;
  • 限制在GROUP BY子句中使用表达式;
  • 慎用游标;
  • 大小写规范统一,变量绑定统一,避免重复编译;
  • 尽可能少的返回结果集行的数量
  • 避免使用select * 语句;
  • 减少结果集中的列的数量;
  • 视图嵌套使用不能超过3层;
  • 不要使用没有意义的列作为聚集索引列,例如,加1自增列;
  • 避免隐式类型转换,例如字符型一定要用’’,数字型一定不要使用’’;
  • 查询语句一定要有范围的限定,避免全表扫描操作;
  • 合理对大表进行分区;
  • 慎用DISTINCT关键字;
  • 慎用UNION关键字,可以用OR替代;
  • 使用top 1替count(*)来判断是否存在记录;

2.3.2.索引创建原则

  • 同一索引中的组成列最好不要超过3列。
  • 把经常一起出现的字段组合在一起,组成组合索引,组合索引的字段顺序与主键一样,也需要把最常用的字段放在前面,把重复率低的字段放在前面。
  • 根据使用频率决定哪些字段需要建立索引,选择经常作为连接条件、筛选条件、聚合查询、排序的字段作为索引的候选字段。
  • 根据数据量决定哪些表需要增加索引,数据量小的可以只有主键。
  • 若某列中有大量的值是空值,可以建立索引。
  • 要对值分布较宽的列建立索引。
  • 若表主要用来查询,则可按需要建立索引,若对表操作主要是UPDATE,则尽可能少建索引。
  • 不要对值较窄的列建立索引,如性别
  • 不要索引较小的表(如表不足1000行)
  • 若某列的值大部分是a,少数是别的值(如b,c,d…),且经常以该列的其它值(如b,c,d…)为查询条件,则可以将值a设为空值,并在此列上创建索引。

2.3.3.充分利用索引

(1).函数、表达式使用
在where语句中,尽量避免在运算符左边对列进行函数或者表达式操作,容易引起全表扫描,要尽可能将操作移至运算符右边。
 
(2).IN/OR子句使用
IN、OR、NOT IN Sql Server2005数据库可以分析出应该根据索引查找。属于2005版本的新特性。
 
(3). !=或<>操作符子句使用
!=或<>操作符可以用INDEXSEEK查找的,可以正常使用。
 
(4). 不要对索引字段进行运算
例如:
SELECTIDFROMTWHERENUM/2=100
应改为:
SELECTIDFROMTWHERENUM=100*2
 
SELECTIDFROMTWHERENUM/2=NUM1
如果NUM有索引应改为:
SELECTIDFROMTWHERENUM=NUM1*2
如果NUM1有索引则不应该改。
 
 
 
(5). 不要对索引字段进行格式转换
日期字段的例子:
WHERECONVERT(VARCHAR(10),日期字段,120)='2008-08-15'
应该改为
WHERE日期字段〉='2008-08-15'  AND日期字段<'2008-08-16'
 
ISNULL转换的例子:
WHEREISNULL(字段,'')<>''应改为:WHERE字段<>''
WHEREISNULL(字段,'')=''不应修改
WHEREISNULL(字段,'F')='T'应改为:WHERE字段='T'
WHEREISNULL(字段,'F')<>'T'不应修改
 
(6).不要对索引字段使用函数
WHERELEFT(NAME,3)='ABC'或者WHERESUBSTRING(NAME,1,3)='ABC'
应改为:
WHERENAMELIKE'ABC%'
 
日期查询的例子:
WHEREDATEDIFF(DAY,日期,'2005-11-30')=0
应改为:WHERE日期>='2005-11-30'AND日期<'2005-12-1'
 
WHEREDATEDIFF(DAY,日期,'2005-11-30')>0
应改为:WHERE日期<'2005-11-30'
 
WHEREDATEDIFF(DAY,日期,'2005-11-30')>=0
应改为:WHERE日期<'2005-12-01'
 
WHEREDATEDIFF(DAY,日期,'2005-11-30')<0
应改为:WHERE日期>='2005-12-01'
 
WHEREDATEDIFF(DAY,日期,'2005-11-30')<=0
应改为:WHERE日期>='2005-11-30'
 
(7).不要对索引字段进行多字段连接
例如:
WHEREFAME+'.'+LNAME='H.Y'
应改为:
WHEREFNAME='H'ANDLNAME='Y'

2.3.4.Like的使用

对索引列避免使用like ‘%xx’, 应该使用like ‘xx%’。设计数据结构时就应该考虑这个问题,不要出现必须要采用like ‘%xx’才能满足业务需要的情形。

2.4.编码阶段

2.4.1.尽可能少的范围数据

A、 横向来看,不要写SELECT *的语句,而是选择你需要的字段。
B、 纵向来看,合理写WHERE子句,不要写没有WHERE的SQL语句。
C、 注意SELECT INTO后的WHERE子句,因为SELECT INTO把数据插入到临时表,这个过程会锁定一些系统表,如果这个WHERE子句返回的数据过多或者速度太慢,会造成系统表长期锁定,诸塞其他进程。
D、对于聚合查询,可以用HAVING子句进一步限定返回的行。

2.4.2.尽可能少的重复操作

A、控制同一语句的多次执行,特别是一些基础数据的多次执行是很多程序员很少注意的。
B、减少多次的数据转换,也许需要数据转换是设计的问题,但是减少次数是程序员可以做到的。
C、杜绝不必要的子查询和连接表,子查询在执行计划一般解释成外连接,多余的连接表带来额外的开销。
D、合并对同一表同一条件的多次UPDATE,比如
UPDATEEMPLOYEESETFNAME='H'WHEREEMP_ID=' F'
UPDATEEMPLOYEESETLNAME='Y'WHEREEMP_ID=' F'
这两个语句应该合并成以下一个语句
UPDATEEMPLOYEESETFNAME='H',LNAME='Y'WHEREEMP_ID=' F'
 
E、UPDATE操作不要拆成DELETE操作+INSERT操作的形式,虽然功能相同,但是性能差别是很大的。
F、不要写一些没有意义的查询,比如
SELECT*FROMEMPLOYEEWHERE1=2

2.4.3.注意事务和锁

事务是数据库应用中和重要的工具,它有原子性、一致性、隔离性、持久性这四个属性,很多操作我们都需要利用事务来保证数据的正确性。在使用事务中我们需要做到尽量避免死锁、尽量减少阻塞。具体以下方面需要特别注意:
A、事务操作过程要尽量小,能拆分的事务要拆分开来。
B、 事务操作过程不应该有交互,因为交互等待的时候,事务并未结束,可能锁定了很多资源。
C、 事务操作过程要按同一顺序访问对象。
D、提高事务中每个语句的效率,利用索引和其他方法提高每个语句的效率可以有效地减少整个事务的执行时间。
E、 尽量不要指定锁类型和索引,SQL SERVER允许我们自己指定语句使用的锁类型和索引,但是一般情况下,SQL SERVER优化器选择的锁类型和索引是在当前数据量和查询条件下是最优的,我们指定的可能只是在目前情况下更有,但是数据量和数据分布在将来是会变化的。

2.4.4.注意临时表和表变量的用法

在复杂系统中,临时表和表变量很难避免,关于临时表和表变量的用法,需要注意:
A、如果语句很复杂,连接太多,可以考虑用临时表和表变量分步完成。
B、如果需要多次用到一个大表的同一部分数据,考虑用临时表和表变量暂存这部分数据。
C、如果需要综合多个表的数据,形成一个结果,可以考虑用临时表和表变量分步汇总这多个表的数据。
D、其他情况下,应该控制临时表和表变量的使用。
E、关于临时表和表变量的选择,很多说法是表变量在内存,速度快,应该首选表变量,但是在实际使用中发现,这个选择主要考虑需要放在临时表的数据量,在数据量较多的情况下,临时表的速度反而更快。
F、关于临时表产生使用SELECTINTO和CREATETABLE+INSERTINTO的选择,我们做过测试,一般情况下,SELECTINTO会比CREATETABLE+INSERTINTO的方法快很多,但是SELECTINTO会锁定TEMPDB的系统表SYSOBJECTS、SYSINDEXES、SYSCOLUMNS,在多用户并发环境下,容易阻塞其他进程,所以我的建议是,在并发系统中,尽量使用CREATETABLE+INSERTINTO,而大数据量的单个语句使用中,使用SELECTINTO。
G、注意排序规则,用CREATETABLE建立的临时表,如果不指定字段的排序规则,会选择TEMPDB的默认排序规则,而不是当前数据库的排序规则。如果当前数据库的排序规则和TEMPDB的排序规则不同,连接的时候就会出现排序规则的冲突错误。一般可以在CREATETABLE建立临时表时指定字段的排序规则为DATABASE_DEFAULT来避免上述问题。

2.4.5.注意子查询的用法

子查询是一个 SELECT查询,它嵌套在 SELECT、INSERT、UPDATE、DELETE语句或其它子查询中。任何允许使用表达式的地方都可以使用子查询。
子查询可以使我们的编程灵活多样,可以用来实现一些特殊的功能。但是在性能上,往往一个不合适的子查询用法会形成一个性能瓶颈。
如果子查询的条件中使用了其外层的表的字段,这种子查询就叫作相关子查询。相关子查询可以用IN、NOTIN、EXISTS、NOTEXISTS引入。
 
关于相关子查询,应该注意:
A、NOTIN、NOTEXISTS的相关子查询可以改用LEFTJOIN代替写法。比如:
 SELECTPUB_NAME  FROM  PUBLISHERS WHEREPUB_IDNOTIN
   (SELECTPUB_IDFROMTITLES  WHERETYPE='BUSINESS')
 可以改写成:
SELECTA.PUB_NAME  FROMPUBLISHERSA
LEFTJOINTITLESBON  B.TYPE='BUSINESS'AND  A.PUB_ID=B.PUB_ID
 
SELECTTITLEFROMTITLES
WHERENOTEXISTS(SELECTTITLE_IDFROMSALESWHERETITLE_ID=TITLES.TITLE_ID)
可以改写成:
SELECTTITLE 
FROMTITLESLEFTJOINSALESNSALES.TITLE_ID=TITLES.TITLE_ID
WHERESALES.TITLE_IDISNULL
 
B、如果保证子查询没有重复,IN、EXISTS的相关子查询可以用INNERJOIN代替。比如:
SELECTPUB_NAMEFROMPUBLISHERS  WHEREPUB_IDIN(SELECTPUB_IDFROMTITLESWHERETYPE='BUSINESS')
可以改写成:
SELECTDISTINCTA.PUB_NAME  
FROMPUBLISHERSA
INNERJOINTITLESBON  B.TYPE='BUSINESS'ANDA.PUB_ID=B.PUB_ID
 
C、不要用COUNT(*)的子查询判断是否存在记录,最好用LEFTJOIN或者EXISTS,比如有人写这样的语句:
SELECTJOB_DESCFROMJOBS
WHERE (SELECTCOUNT(*)FROMEMPLOYEEWHEREJOB_ID=JOBS.JOB_ID)=0
应该改成:
SELECTJOBS.JOB_DESCFROMJOBS
LEFTJOINEMPLOYEEONEMPLOYEE.JOB_ID=JOBS.JOB_ID
WHEREEMPLOYEE.EMP_IDISNULL
 
SELECTJOB_DESCFROMJOBS
WHERE (SELECTCOUNT(*)FROMEMPLOYEEWHEREJOB_ID=JOBS.JOB_ID)<>0
应该改成:
SELECTJOB_DESCFROMJOBS
WHEREEXISTS(SELECT1 FROMEMPLOYEEWHEREJOB_ID=JOBS.JOB_ID)

2.4.6.注意连接条件的用法

多表连接的连接条件对索引的选择有着重要的意义,所以我们在写连接条件的时候需要特别的注意。
A、多表连接的时候,连接条件必须写全,宁可重复,不要缺漏。
B、 连接条件尽量使用聚集索引
C、 注意ON部分条件和WHERE部分条件的区别

2.4.7.查看执行计划及成本

养成良好的编码习惯,每写完一个sql语句,就查看执行计划及成本分析。在大表上尽可能避免全表扫描及聚集索引扫描。

2.4.8.索引的使用及维护

在良好的数据库设计基础上,能有效地使用索引是SQL Server取得高性能的基础,SQL Server采用基于代价的优化模型,它对每一个提交的有关表的查询,决定是否使用索引或用哪一个索引。因为查询执行的大部分开销是磁盘I/O,使用索引提高性能的一个主要目标是避免全表扫描,因为全表扫描需要从磁盘上读表的每一个数据页,如果有索引指向数据值,则查询只需读几次磁盘就可以了。所以如果建立了合理的索引,优化器就能利用索引加速数据的查询过程。但是,索引并不总是提高系统的性能,在增、删、改操作中索引的存在会增加一定的工作量,因此,在适当的地方增加适当的索引并从不合理的地方删除次优的索引,将有助于优化那些性能较差的SQL Server应用。
 
(1). 聚簇索引(clustered indexes)的使用
聚簇索引是一种对磁盘上实际数据重新组织以按指定的一个或多个列的值排序。由于聚簇索引的索引页面指针指向数据页面,所以使用聚簇索引查找数据几乎总是比使用非聚簇索引快。每张表只能建一个聚簇索引,并且建聚簇索引需要至少相当该表120%的附加空间,以存放该表的副本和索引中间页。建立聚簇索引的思想是:
1、大多数表都应该有聚簇索引或使用分区来降低对表尾页的竞争,在一个高事务的环境中,对最后一页的封锁严重影响系统的吞吐量。
2、在聚簇索引下,数据在物理上按顺序排在数据页上,重复值也排在一起,因而在那些包含范围检查(between、<、<=、>、>=)或使用group by或order by的查询时,一旦找到具有范围中第一个键值的行,具有后续索引值的行保证物理上毗连在一起而不必进一步搜索,避免了大范围扫描,可以大大提高查询速度。
3、在一个频繁发生插入操作的表上建立聚簇索引时,不要建在具有单调上升值的列(如IDENTITY)上,否则会经常引起封锁冲突。
4、在聚簇索引中不要包含经常修改的列,因为码值修改后,数据行必须移动到新的位置。
5、选择聚簇索引应基于where子句和连接操作的类型。
 
聚簇索引的侯选列是:
1、主键列,该列在where子句中使用并且插入是随机的。
2、按范围存取的列,如pri_order > 100 and pri_order < 200。
3、在groupby或orderby中使用的列。
4、不经常修改的列。
5、在连接操作中使用的列
 
(2). 非聚簇索引(nonclustered indexes)的使用
SQL Server缺省情况下建立的索引是非聚簇索引,由于非聚簇索引不重新组织表中的数据,而是对每一行存储索引列值并用一个指针指向数据所在的页面。换句话说非聚簇索引具有在索引结构和数据本身之间的一个额外级。一个表如果没有聚簇索引时,可有250个非聚簇索引。每个非聚簇索引提供访问数据的不同排序顺序。在建立非聚簇索引时,要权衡索引对查询速度的加快与降低修改速度之间的利弊。另外,还要考虑这些问题:
1、索引需要使用多少空间。
2、合适的列是否稳定。
3、索引键是如何选择的,扫描效果是否更佳。
4、是否有许多重复值。
对更新频繁的表来说,表上的非聚簇索引比聚簇索引和根本没有索引需要更多的额外开销。对移到新页的每一行而言,指向该数据的每个非聚簇索引的页级行也必须更新,有时可能还需要索引页的分理。从一个页面删除数据的进程也会有类似的开销,另外,删除进程还必须把数据移到页面上部,以保证数据的连续性。所以,建立非聚簇索引要非常慎重。非聚簇索引常被用在以下情况:
1、某列常用于集合函数(如Sum,....)。
2、某列常用于join,order by,group by。
3、查寻出的数据不超过表中数据量的20%。
 
(3).  覆盖索引(covering indexes)的使用
覆盖索引是指那些索引项中包含查寻所需要的全部信息的非聚簇索引,这种索引之所以比较快也正是因为索引页中包含了查寻所必须的数据,不需去访问数据页。如果非聚簇索引中包含结果数据,那么它的查询速度将快于聚簇索引。 但是由于覆盖索引的索引项比较多,要占用比较大的空间。而且update操作会引起索引值改变。所以如果潜在的覆盖查询并不常用或不太关键,则覆盖索引的增加反而会降低性能。

2.4.9.索引的选择技术

索引的有无,建立方式的不同将会导致不同的查询效果,选择什么样的索引基于用户对数据的查询条件,这些条件体现于where从句和join表达式中。一般来说建立索引的思路是:
 (1)、主键时常作为where子句的条件,应在表的主键列上建立聚簇索引,尤其当经常用它作为连接的时候。
 (2)、有大量重复值且经常有范围查询和排序、分组发生的列,或者非常频繁地被访问的列,可考虑建立聚簇索引。
 (3)、经常同时存取多列,且每列都含有重复值可考虑建立复合索引来覆盖一个或一组查询,并把查询引用最频繁的列作为前导列,如果可能尽量使关键查询形成覆盖查询。
 (4)、如果知道索引键的所有值都是唯一的,那么确保把索引定义成唯一索引。
 (5)、在一个经常做插入操作的表上建索引时,使用fillfactor(填充因子)来减少页分裂,同时提高并发度降低死锁的发生。如果在只读表上建索引,则可以把fillfactor置为100。
 (6)、在选择索引键时,设法选择那些采用小数据类型的列作为键以使每个索引页能够容纳尽可能多的索引键和指针,通过这种方式,可使一个查询必须遍历的索引页面降到最小。此外,尽可能地使用整数为键值,因为它能够提供比任何数据类型都快的访问速度。

2.4.10.索引的维护

某些不合适的索引影响到SQL Server的性能,随着应用系统的运行,数据不断地发生变化,当数据变化达到某一个程度时将会影响到索引的使用。这时需要用户自己来维护索引。索引的维护包括:
1、重建索引
随着数据行的插入、删除和数据页的分裂,有些索引页可能只包含几页数据,另外应用在执行大块I/O的时候,重建非聚簇索引可以降低分片,维护大块I/O的效率。重建索引实际上是重新组织B-树空间。在下面情况下需要重建索引:
 (1)、数据和使用模式大幅度变化。
 (2)、排序的顺序发生改变。
 (3)、要进行大量插入操作或已经完成。
 (4)、使用大块I/O的查询的磁盘读次数比预料的要多。
 (5)、由于大量数据修改,使得数据页和索引页没有充分使用而导致空间的使用超出估算。
 (6)、dbcc检查出索引有问题。
当重建聚簇索引时,这张表的所有非聚簇索引将被重建。
2、索引统计信息的更新
当在一个包含数据的表上创建索引的时候,SQL Server会创建分布数据页来存放有关索引的两种统计信息:分布表和密度表。优化器利用这个页来判断该索引对某个特定查询是否有用。但这个统计信息并不动态地重新计算。这意味着,当表的数据改变之后,统计信息有可能是过时的,从而影响优化器追求最有工作的目标。因此,在下面情况下应该运行update statistics命令:
 (1)、数据行的插入和删除修改了数据的分布。
 (2)、对用truncate table删除数据的表上增加数据行。
 (3)、修改索引列的值。
 
 

    评分: 请先登录再投票,同一篇博客一月只能投票一次!
    无人投票

相关博客:


评论

游客 2010-11-29 16:28:09  
嗯,规范很重要。

发表评论

关注此文的人们还关注