数据库的逻辑设计、包括表与表之间的关系是优化关系型数据库性能的核心。一个好的逻辑数据库设计可以为优化数据库和应用程序打下良好的基础。
标准化的数据库逻辑设计包括用多的、有相互关系的窄表来代替很多列的长数据表。下面是一些使用标准化表的一些好处。
A: 由于表窄,因此可以使排序和建立索引更为迅速
B: 由于多表,所以多镞的索引成为可能
C: 更窄更紧凑的索引
D: 每个表中可以有少一些的索引,因此可以提高 insert update delete 等的速度,因为这些操作在索引多的情况下会对系统性能产生很大的影响
E: 更少的空值和更少的多余值,增加了数据库的紧凑性由于标准化,所以会增加了在获取数据时引用表的数目和其间的连接关系的复杂性。太多的表和复杂的连接关系会降低服务器的性能,因此在这两者之间需要综合考虑。
定义具有相关关系的主键和外来键时应该注意的事项主要是:用于连接多表的主键和参考的键要有相同的数据类型。
2 索引的设计
A: 尽量避免表扫描
检查你的查询语句的 where 子句,因为这是优化器重要关注的地方。包含在 where 里面的每一列( column) 都是可能的侯选索引,为能达到最优的性能,考虑在下面给出的例子:对于在 where 子句中给出了 column1 这个列。
下面的两个条件可以提高索引的优化查询性能!
第一:在表中的 column1 列上有一个单索引
第二:在表中有多索引,但是 column1 是第一个索引的列
避免定义多索引而 column1 是第二个或后面的索引,这样的索引不能优化服务器性能
例如:下面的例子用了 pubs 数据库。
SELECT au_id, au_lname, au_fname FROM authors
WHERE au_lname = ’White’
按下面几个列上建立的索引将会是对优化器有用的索引
?au_lname
?au_lname, au_fname
而在下面几个列上建立的索引将不会对优化器起到好的作用
?au_address
?au_fname, au_lname
考虑使用窄的索引在一个或两个列上,窄索引比多索引和复合索引更能有效。用窄的索引,在每一页上将会有更多的行和更少的索引级别(相对与多索引和复合索引而言),这将推进系统性能。
对于多列索引, SQL Server 维持一个在所有列的索引上的密度统计(用于联合)和在第一个索引上的 histogram (柱状图)统计。根据统计结果,如果在复合索引上的第一个索引很少被选择使用,那么优化器对很多查询请求将不会使用索引。
有用的索引会提高 select 语句的性能,包括 insert,uodate,delete 。
但是,由于改变一个表的内容,将会影响索引。每一个 insert,update,delete 语句将会使性能下降一些。实验表明,不要在一个单表上用大量的索引,不要在共享的列上(指在多表中用了参考约束)使用重叠的索引。
在某一列上检查唯一的数据的个数,比较它与表中数据的行数做一个比较。这就是数据的选择性,这比较结果将会帮助你决定是否将某一列作为侯选的索引列,如果需要,建哪一种索引。你可以用下面的查询语句返回某一列的不同值的数目。
select count(distinct cloumn_name) from table_name
假设 column_name 是一个 10000 行的表,则看 column_name 返回值来决定是否应该使用,及应该使用什么索引。
Unique values Index
5000 Nonclustered index
20 Clustered index
3 No index
镞索引和非镞索引的选择
<1:> 镞索引是行的物理顺序和索引的顺序是一致的。页级,低层等索引的各个级别上都包含实际的数据页。一个表只能是有一个镞索引。由于 update,delete 语句要求相对多一些的读操作,因此镞索引常常能加速这样的操作。在至少有一个索引的表中,你应该有一个镞索引。
在下面的几个情况下,你可以考虑用镞索引:
例如: 某列包括的不同值的个数是有限的(但是不是极少的)
顾客表的州名列有 50 个左右的不同州名的缩写值,可以使用镞索引。
例如: 对返回一定范围内值的列可以使用镞索引,比如用 between,>,>=,<,<= 等等来对列进行操作的列上。
select * from sales where ord_date between ’5/1/93’ and ’6/1/93’
例如: 对查询时返回大量结果的列可以使用镞索引。
SELECT * FROM phonebook WHERE last_name = ’Smith’
当有大量的行正在被插入表中时,要避免在本表一个自然增长(例如, identity 列)的列上建立镞索引。如果你建立了镞的索引,那么 insert 的性能就会大大降低。因为每一个插入的行必须到表的最后,表的最后一个数据页。
当一个数据正在被插入(这时这个数据页是被锁定的),所有的其他插入行必须等待直到当前的插入已经结束。
一个索引的叶级页中包括实际的数据页,并且在硬盘上的数据页的次序是跟镞索引的逻辑次序一样的。
<2:> 一个非镞的索引就是行的物理次序与索引的次序是不同的。一个非镞索引的叶级包含了指向行数据页的指针。
在一个表中可以有多个非镞索引,你可以在以下几个情况下考虑使用非镞索引。
在有很多不同值的列上可以考虑使用非镞索引
例如:一个 part_id 列在一个 part 表中
select * from employee where emp_id = ’pcm9809f’
查询语句中用 order by 子句的列上可以考虑使用镞索引
3 查询语句的设计
SQL Server 优化器通过分析查询语句,自动对查询进行优化并决定最有效的执行方案。优化器分析查询语句来决定那个子句可以被优化,并针对可以被优化查询的子句来选择有用的索引。最后优化器比较所有可能的执行方案并选择最有效的一个方案出来。
在执行一个查询时,用一个 where 子句来限制必须处理的行数,除非完全需要,否则应该避免在一个表中无限制地读并处理所有的行。
例如下面的例子,
select qty from sales where stor_id=7131
是很有效的比下面这个无限制的查询
select qty from sales
避免给客户的最后数据选择返回大量的结果集。允许 SQL Server 运行满足它目的的函数限制结果集的大小是更有效的。
这能减少网络 I/O 并能提高多用户的相关并发时的应用程序性能。因为优化器关注的焦点就是 where 子句的查询,以利用有用的索引。在表中的每一个索引都可能成为包括在 where 子句中的侯选索引。为了最好的性能可以遵照下面的用于一个给定列 column1 的索引。
第一:在表中的 column1 列上有一个单索引
第二:在表中有多索引,但是 column1 是第一个索引的列不要在 where 子句中使用没有 column1 列索引的查询语句,并避免在 where 子句用一个多索引的非第一个索引的索引。
这时多索引是没有用的。
For example, given a multicolumn index on the au_lname, au_fname columns of the authors table in
the pubs database,
下面这个 query 语句利用了 au_lname 上的索引
SELECT au_id, au_lname, au_fname FROM authors
WHERE au_lname = ’White’
AND au_fname = ’Johnson’
SELECT au_id, au_lname, au_fname FROM authors
WHERE au_lname = ’White’
下面这个查询没有利用索引,因为他使用了多索引的非第一个索引的索引
SELECT au_id, au_lname, au_fname FROM authors
WHERE au_fname = ’Johnson’
游标提供了对特定集合中逐行扫描的手段,一般使用游标来逐行遍历数据,根据取出数据条件的不同进行不同的 * 作。而对于多表和大表中定义的游标(大的数据集合)循环很容易使程序进入一个漫长的等待甚至死机,笔者在某市 “ 住房公积金管理系统 ” 进行日终账户滚积数计息处理时,对一个 10 万个账户的游标处理时导致程序进入了一个无限期的等待(后经测算需 48 小时才能完成) ( 硬件环境: Alpha/4000 128MB RAM ,SCO Unix ,Sybase 11.0) 。经修改程序并改用 UPDATE 语句后,该处理过程得以在 20 分钟之内完成。示例如下:
Declare Mycursor cursor for select count—no from COUNT
Open Mycursor
Fetch Mycursor into @vcount—no
While (@@sqlstatus=0)
Begin
If @vcount—no=′ ′ 条件 1
* 作 1
If @vcount—no=′ ′ 条件 2
* 作 2
...
Fetch Mycursor into @vcount—no
End
...
改为
Update COUNT set * 作 1 for 条件 1
Update COUNT set * 作 2 for 条件 2
...
在某些必须使用游标的场合,可考虑将符合条件的数据行转入临时表中,再对临时表定义游标进行 * 作,这样,可使性能得到明显提高。笔者在某地市 “ 电信收费系统 ” 数据库后台程序设计中,对一个表( 3 万行中符合条件的 30 多行数据)进行游标 * 作 ( 硬件环境: PC 服务器, P Ⅱ 266 64MB RAM ,Windows NT4.0 MS SQL Server 6.5) 。
示例如下:
Create # tmp / * 定义临时表 * /
( 字段 1
字段 2
... )
Insert into # tmp select * from TOTAL where 条件
Declare Mycursor cursor for select * from # tmp / *对临时表定义游标* /
...
索引 (Index) 的使用技巧
创建索引一般有两个目的:维护被索引列的惟一性和提供快速访问表中数据的策略。大型数据库有两种索引,即簇索引和非簇索引,一个没有簇索引的表是按堆结构存储数据,所有的数据均添加在表的尾部;而建立了簇索引的表,其数据在物理上会按照簇索引键的顺序存储,一个表只允许有一个簇索引,因此,根据 B 树结构,可以理解添加任何一种索引均能提高按索引列查询的速度,但与此同时会降低插入、更新、删除 * 作的性能,尤其是当填充因子( Fill Factor )较大时。所以对索引较多的表进行频繁的插入、更新、删除 * 作时,建表和索引时应设置较小的填充因子,以便在各数据页中留下较多的自由空间,减少页分割及重新组织的工作。
数据的一致性和完整性
为了保证数据库的一致性和完整性,设计人员往往会设计过多的表间关联( Relation ),尽可能地降低数据冗余。表间关联是一种强制性措施,建立后,对父表( Parent Table )和子表 (Child Table) 的插入、更新、删除 * 作均要占用系统的开销,另外,最好不要用 Identify 属性字段作为主键与子表关联。如果数据冗余低,数据的完整性容易得到保证,但增加了表间连接查询的 * 作。为了提高系统的响应时间,合理的数据冗余也是必要的。使用规则( Rule )和约束( Check )来防止系统 * 作人员误输入造成数据的错误是,设计人员的另一种常用手段,但是,不必要的规则和约束也会占用系统的不必要开销,需要注意的是,约束对数据的有效性验证要比规则快。所有这些,设计人员在设计阶段应根据系统 * 作的类型、频度加以均衡考虑。
事务的陷阱
事务是在一次性完成的一组 * 作。虽然这些 * 作是单个的 * 作, SQL Server 能够保证这组 * 作要么全部都完成,要么一点儿都不做。正是大型数据库的这一特性,使得数据的完整性得到了极大的保证。
众所周知, SQL Server 为每个独立的 SQL 语句都提供了隐含的事务控制,使得每个 DML 的数据 * 作得以完整提交或回滚,但是 SQL Server 还提供了显式事务控制语句,如:
BEGIN TRANSACTION 开始一个事务
COMMIT TRANSACTION 提交一个事务
ROLLBACK TRANSACTION 回滚一个事务
事务可以嵌套,可以通过全局变量 @@trancount 检索到连接的事务处理嵌套层次。要特别注意的是,每个显示或隐含的事物开始都使得该变量加 1 ,每个事务的提交使该变量减 1 ,每个事务的回滚都会使得该变量置 0 ,而只有当该变量为 0 时的事务提交(最后一个提交语句时),才把物理数据写入磁盘。
数据类型的选择
数据类型的合理选择对于数据库的性能和 * 作具有很大的影响,有关这方面的书籍也有不少的阐述,笔者这里主要介绍几点经验:
1. Identify 字段不要作为表的主键与其它表关联,这将会影响到该表的数据迁移。
2. Text 和 Image 字段属指针型数据,主要用来存放二进制大型对象( BLOB )。这类数据的 * 作相比其它数据类型较慢,因此要避开使用。
3. 日期型字段的优点是有众多的日期函数支持,因此,在日期的大小比较、加减 * 作上非常简单。但是,在按照日期作为条件的查询 * 作也要用函数,相比其它数据类型速度上就慢许多 , 因为用函数作为查询的条件时,服务器无法用先进的性能策略来优化查询而只能进行表扫描遍历每行。
第 1 部分 - 设计数据库之前
1. 考察现有环境
在设计一个新数据库时,你不但应该仔细研究业务需求而且还要考察现有的系统。大多数数据库项目都不是从头开始建立的;通常,机构内总会存在用来满足特定需求的现有系统(可能没有实现自动计算)。显然,现有系统并不完美,否则你就不必再建立新系统了。但是对旧系统的研究可以让你发现一些可能会忽略的细微问题。一般来说,考察现有系统对你绝对有好处。
2. 定义标准的对象命名规范
一定要定义数据库对象的命名规范。对数据库表来说,从项目一开始就要确定表名是采用复数还是单数形式。此外还要给表的别名定义简单规则(比方说,如果表名是一个单词,别名就取单词的前 4 个字母;如果表名是两个单词,就各取两个单词的前两个字母组成 4 个字母长的别名;如果表的名字由 3 个单词组成,你不妨从头两个单词中各取一个然后从最后一个单词中再取出两个字母,结果还是组成 4 字母长的别名,其余依次类推)对工作用表来说,表名可以加上前缀 WORK_ 后面附上采用该表的应用程序的名字。表内的列[字段]要针对键采用一整套设计规则。比如,如果键是数字类型,你可以用 _N 作为后缀;如果是字符类型则可以采用 _C 后缀。对列[字段]名应该采用标准的前缀和后缀。再如,假如你的表里有好多“money”字段,你不妨给每个列[字段]增加一个 _M 后缀。还有,日期列[字段]最好以 D_ 作为名字打头。
检查表名、报表名和查询名之间的命名规范。你可能会很快就被这些不同的数据库要素的名称搞糊涂了。假如你坚持统一地命名这些数据库的不同组成部分,至少你应该在这些对象名字的开头用 Table、Query 或者 Report 等前缀加以区别。
如果采用了 Microsoft Access,你可以用 qry、rpt、tbl 和 mod 等符号来标识对象(比如 tbl_Employees)。我在和 SQL Server 打交道的时候还用过 tbl 来索引表,但我用 sp_company (现在用 sp_feft_)标识存储过程,因为在有的时候如果我发现了更好的处理办法往往会保存好几个拷贝。我在实现 SQL Server 2000 时用 udf_ (或者类似的标记)标识我编写的函数。
3. 工欲利其器
采用理想的数据库设计工具,比如:SyBase 公司的 PowerDesign,她支持 PB、VB、Delphe 等语言,通过 ODBC 可以连接市面上流行的 30 多个数据库,包括 dBase、FoxPro、VFP、SQL Server 等,今后有机会我将着重介绍 PowerDesign 的使用。
4. 获取数据模式资源手册
正在寻求示例模式的人可以阅读《数据模式资源手册》一书,该书由 Len Silverston、W. H. Inmon 和 Kent Graziano 编写,是一本值得拥有的最佳数据建模图书。该书包括的章节涵盖多种数据领域,比如人员、机构和工作效能等。其他的你还可以参考:[1]萨师煊 王珊著 数据库系统概论(第二版)高等教育出版社 1991、[2][美] Steven M.Bobrowski 著 Oracle 7 与客户/服务器计算技术从入门到精通 刘建元等译 电子工业出版社,1996、[3]周中元 信息系统建模方法(下) 电子与信息化 1999年第3期,1999
5. 畅想未来,但不可忘了过去的教训
我发现询问用户如何看待未来需求变化非常有用。这样做可以达到两个目的:首先,你可以清楚地了解应用设计在哪个地方应该更具灵活性以及如何避免性能瓶颈;其次,你知道发生事先没有确定的需求变更时用户将和你一样感到吃惊。
一定要记住过去的经验教训!我们开发人员还应该通过分享自己的体会和经验互相帮助。即使用户认为他们再也不需要什么支持了,我们也应该对他们进行这方面的教育,我们都曾经面临过这样的时刻“当初要是这么做了该多好..”。
6. 在物理实践之前进行逻辑设计
在深入物理设计之前要先进行逻辑设计。随着大量的 CASE 工具不断涌现出来,你的设计也可以达到相当高的逻辑水准,你通常可以从整体上更好地了解数据库设计所需要的方方面面。
7. 了解你的业务
在你百分百地确定系统从客户角度满足其需求之前不要在你的 ER(实体关系)模式中加入哪怕一个数据表(怎么,你还没有模式?那请你参看技巧 9)。了解你的企业业务可以在以后的开发阶段节约大量的时间。一旦你明确了业务需求,你就可以自己做出许多决策了。
一旦你认为你已经明确了业务内容,你最好同客户进行一次系统的交流。采用客户的术语并且向他们解释你所想到的和你所听到的。同时还应该用可能、将会和必须等词汇表达出系统的关系基数。这样你就可以让你的客户纠正你自己的理解然后做好下一步的 ER 设计。
8. 创建数据字典和 ER 图表
一定要花点时间创建 ER 图表和数据字典。其中至少应该包含每个字段的数据类型和在每个表内的主外键。创建 ER 图表和数据字典确实有点费时但对其他开发人员要了解整个设计却是完全必要的。越早创建越能有助于避免今后面临的可能混乱,从而可以让任何了解数据库的人都明确如何从数据库中获得数据。
有一份诸如 ER 图表等最新文档其重要性如何强调都不过分,这对表明表之间关系很有用,而数据字典则说明了每个字段的用途以及任何可能存在的别名。对 SQL 表达式的文档化来说这是完全必要的。
9. 创建模式
一张图表胜过千言万语:开发人员不仅要阅读和实现它,而且还要用它来帮助自己和用户对话。模式有助于提高协作效能,这样在先期的数据库设计中几乎不可能出现大的问题。模式不必弄的很复杂;甚至可以简单到手写在一张纸上就可以了。只是要保证其上的逻辑关系今后能产生效益。
10. 从输入输出下手
在定义数据库表和字段需求(输入)时,首先应检查现有的或者已经设计出的报表、查询和视图(输出)以决定为了支持这些输出哪些是必要的表和字段。举个简单的例子:假如客户需要一个报表按照邮政编码排序、分段和求和,你要保证其中包括了单独的邮政编码字段而不要把邮政编码糅进地址字段里。
11. 报表技巧
要了解用户通常是如何报告数据的:批处理还是在线提交报表?时间间隔是每天、每周、每月、每个季度还是每年?如果需要的话还可以考虑创建总结表。系统生成的主键在报表中很难管理。用户在具有系统生成主键的表内用副键进行检索往往会返回许多重复数据。这样的检索性能比较低而且容易引起混乱。
12. 理解客户需求
看起来这应该是显而易见的事,但需求就是来自客户(这里要从内部和外部客户的角度考虑)。不要依赖用户写下来的需求,真正的需求在客户的脑袋里。你要让客户解释其需求,而且随着开发的继续,还要经常询问客户保证其需求仍然在开发的目的之中。一个不变的真理是:“只有我看见了我才知道我想要的是什么”必然会导致大量的返工,因为数据库没有达到客户从来没有写下来的需求标准。而更糟的是你对他们需求的解释只属于你自己,而且可能是完全错误的。
第 2 部分 - 设计表和字段
<script language=JavaScript> document.write("");ad_dst = ad_dst+1; </script>
1. 检查各种变化
我在设计数据库的时候会考虑到哪些数据字段将来可能会发生变更。比方说,姓氏就是如此(注意是西方人的姓氏,比如女性结婚后从夫姓等)。所以,在建立系统存储客户信息时,我倾向于在单独的一个数据表里存储姓氏字段,而且还附加起始日和终止日等字段,这样就可以跟踪这一数据条目的变化。
2. 采用有意义的字段名
有一回我参加开发过一个项目,其中有从其他程序员那里继承的程序,那个程序员喜欢用屏幕上显示数据指示用语命名字段,这也不赖,但不幸的是,她还喜欢用一些奇怪的命名法,其命名采用了匈牙利命名和控制序号的组合形式,比如 cbo1、txt2、txt2_b 等等。
除非你在使用只面向你的缩写字段名的系统,否则请尽可能地把字段描述的清楚些。当然,也别做过头了,比如 Customer_Shipping_Address_Street_Line_1,虽然很富有说明性,但没人愿意键入这么长的名字,具体尺度就在你的把握中。
3. 采用前缀命名
如果多个表里有好多同一类型的字段(比如 FirstName),你不妨用特定表的前缀(比如 CusLastName)来帮助你标识字段。
时效性数据应包括“最近更新日期/时间”字段。时间标记对查找数据问题的原因、按日期重新处理/重载数据和清除旧数据特别有用。
5. 标准化和数据驱动
数据的标准化不仅方便了自己而且也方便了其他人。比方说,假如你的用户界面要访问外部数据源(文件、XML 文档、其他数据库等),你不妨把相应的连接和路径信息存储在用户界面支持表里。还有,如果用户界面执行工作流之类的任务(发送邮件、打印信笺、修改记录状态等),那么产生工作流的数据也可以存放在数据库里。预先安排总需要付出努力,但如果这些过程采用数据驱动而非硬编码的方式,那么策略变更和维护都会方便得多。事实上,如果过程是数据驱动的,你就可以把相当大的责任推给用户,由用户来维护自己的工作流过程。
6. 标准化不能过头
对那些不熟悉标准化一词(normalization)的人而言,标准化可以保证表内的字段都是最基础的要素,而这一措施有助于消除数据库中的数据冗余。标准化有好几种形式,但 Third Normal Form(3NF)通常被认为在性能、扩展性和数据完整性方面达到了最好平衡。简单来说,3NF 规定:
* 表内的每一个值都只能被表达一次。
* 表内的每一行都应该被唯一的标识(有唯一键)。
* 表内不应该存储依赖于其他键的非键信息。
遵守 3NF 标准的数据库具有以下特点:有一组表专门存放通过键连接起来的关联数据。比方说,某个存放客户及其有关定单的 3NF 数据库就可能有两个表:Customer 和 Order。Order 表不包含定单关联客户的任何信息,但表内会存放一个键值,该键指向 Customer 表里包含该客户信息的那一行。
更高层次的标准化也有,但更标准是否就一定更好呢?答案是不一定。事实上,对某些项目来说,甚至就连 3NF 都可能给数据库引入太高的复杂性。
为了效率的缘故,对表不进行标准化有时也是必要的,这样的例子很多。曾经有个开发餐饮分析软件的活就是用非标准化表把查询时间从平均 40 秒降低到了两秒左右。虽然我不得不这么做,但我绝不把数据表的非标准化当作当然的设计理念。而具体的操作不过是一种派生。所以如果表出了问题重新产生非标准化的表是完全可能的。
7. Microsoft Visual FoxPro 报表技巧
如果你正在使用 Microsoft Visual FoxPro,你可以用对用户友好的字段名来代替编号的名称:比如用 Customer Name 代替 txtCNaM。这样,当你用向导程序 [Wizards,台湾人称为‘精灵’] 创建表单和报表时,其名字会让那些不是程序员的人更容易阅读。
8. 不活跃或者不采用的指示符
增加一个字段表示所在记录是否在业务中不再活跃挺有用的。不管是客户、员工还是其他什么人,这样做都能有助于再运行查询的时候过滤活跃或者不活跃状态。同时还消除了新用户在采用数据时所面临的一些问题,比如,某些记录可能不再为他们所用,再删除的时候可以起到一定的防范作用。
9. 使用角色实体定义属于某类别的列[字段]
在需要对属于特定类别或者具有特定角色的事物做定义时,可以用角色实体来创建特定的时间关联关系,从而可以实现自我文档化。
这里的含义不是让 PERSON 实体带有 Title 字段,而是说,为什么不用 PERSON 实体和 PERSON_TYPE 实体来描述人员呢?比方说,当 John Smith, Engineer 提升为 John Smith, Director 乃至最后爬到 John Smith, CIO 的高位,而所有你要做的不过是改变两个表 PERSON 和 PERSON_TYPE 之间关系的键值,同时增加一个日期/时间字段来知道变化是何时发生的。这样,你的 PERSON_TYPE 表就包含了所有 PERSON 的可能类型,比如 Associate、Engineer、Director、CIO 或者 CEO 等。
还有个替代办法就是改变 PERSON 记录来反映新头衔的变化,不过这样一来在时间上无法跟踪个人所处位置的具体时间。
10. 采用常用实体命名机构数据
组织数据的最简单办法就是采用常用名字,比如:PERSON、ORGANIZATION、ADDRESS 和 PHONE 等等。当你把这些常用的一般名字组合起来或者创建特定的相应副实体时,你就得到了自己用的特殊版本。开始的时候采用一般术语的主要原因在于所有的具体用户都能对抽象事物具体化。
有了这些抽象表示,你就可以在第 2 级标识中采用自己的特殊名称,比如,PERSON 可能是 Employee、Spouse、Patient、Client、Customer、Vendor 或者 Teacher 等。同样的,ORGANIZATION 也可能是 MyCompany、MyDepartment、Competitor、Hospital、Warehouse、Government 等。最后 ADDRESS 可以具体为 Site、Location、Home、Work、Client、Vendor、Corporate 和 FieldOffice 等。
采用一般抽象术语来标识“事物”的类别可以让你在关联数据以满足业务要求方面获得巨大的灵活性,同时这样做还可以显著降低数据存储所需的冗余量。
11. 用户来自世界各地
在设计用到网络或者具有其他国际特性的数据库时,一定要记住大多数国家都有不同的字段格式,比如邮政编码等,有些国家,比如新西兰就没有邮政编码一说。
12. 数据重复需要采用分立的数据表
如果你发现自己在重复输入数据,请创建新表和新的关系。
13. 每个表中都应该添加的 3 个有用的字段
* dRecordCreationDate,在 VB 下默认是 Now(),而在 SQL Server 下默认为 GETDATE()
* sRecordCreator,在 SQL Server 下默认为 NOT NULL DEFAULT USER
* nRecordVersion,记录的版本标记;有助于准确说明记录中出现 null 数据或者丢失数据的原因
14. 对地址和电话采用多个字段
描述街道地址就短短一行记录是不够的。Address_Line1、Address_Line2 和 Address_Line3 可以提供更大的灵活性。还有,电话号码和邮件地址最好拥有自己的数据表,其间具有自身的类型和标记类别。
过分标准化可要小心,这样做可能会导致性能上出现问题。虽然地址和电话表分离通常可以达到最佳状态,但是如果需要经常访问这类信息,或许在其父表中存放“首选”信息(比如 Customer 等)更为妥当些。非标准化和加速访问之间的妥协是有一定意义的。
15. 使用多个名称字段
我觉得很吃惊,许多人在数据库里就给 name 留一个字段。我觉得只有刚入门的开发人员才会这么做,但实际上网上这种做法非常普遍。我建议应该把姓氏和名字当作两个字段来处理,然后在查询的时候再把他们组合起来。
我最常用的是在同一表中创建一个计算列[字段],通过它可以自动地连接标准化后的字段,这样数据变动的时候它也跟着变。不过,这样做在采用建模软件时得很机灵才行。总之,采用连接字段的方式可以有效的隔离用户应用和开发人员界面。
6. 提防大小写混用的对象名和特殊字符
过去最令我恼火的事情之一就是数据库里有大小写混用的对象名,比如 CustomerData。这一问题从 Access 到 Oracle 数据库都存在。我不喜欢采用这种大小写混用的对象命名方法,结果还不得不手工修改名字。想想看,这种数据库/应用程序能混到采用更强大数据库的那一天吗?采用全部大写而且包含下划符的名字具有更好的可读性(CUSTOMER_DATA),绝对不要在对象名的字符之间留空格。
17. 小心保留词
要保证你的字段名没有和保留词、数据库系统或者常用访问方法冲突,比如,最近我编写的一个 ODBC 连接程序里有个表,其中就用了 DESC 作为说明字段名。后果可想而知!DESC 是 DESCENDING 缩写后的保留词。表里的一个 SELECT * 语句倒是能用,但我得到的却是一大堆毫无用处的信息。
18. 保持字段名和类型的一致性
在命名字段并为其指定数据类型的时候一定要保证一致性。假如字段在某个表中叫做“agreement_number”,你就别在另一个表里把名字改成“ref1”。假如数据类型在一个表里是整数,那在另一个表里可就别变成字符型了。记住,你干完自己的活了,其他人还要用你的数据库呢。
19. 仔细选择数字类型
在 SQL 中使用 smallint 和 tinyint 类型要特别小心,比如,假如你想看看月销售总额,你的总额字段类型是 smallint,那么,如果总额超过了 $32,767 你就不能进行计算操作了。
20. 删除标记
在表中包含一个“删除标记”字段,这样就可以把行标记为删除。在关系数据库里不要单独删除某一行;最好采用清除数据程序而且要仔细维护索引整体性。
21. 避免使用触发器
触发器的功能通常可以用其他方式实现。在调试程序时触发器可能成为干扰。假如你确实需要采用触发器,你最好集中对它文档化。
22. 包含版本机制
建议你在数据库中引入版本控制机制来确定使用中的数据库的版本。无论如何你都要实现这一要求。时间一长,用户的需求总是会改变的。最终可能会要求修改数据库结构。虽然你可以通过检查新字段或者索引来确定数据库结构的版本,但我发现把版本信息直接存放到数据库中不更为方便吗?。
23. 给文本字段留足余量
ID 类型的文本字段,比如客户 ID 或定单号等等都应该设置得比一般想象更大,因为时间不长你多半就会因为要添加额外的字符而难堪不已。比方说,假设你的客户 ID 为 10 位数长。那你应该把数据库表字段的长度设为 12 或者 13 个字符长。这算浪费空间吗?是有一点,但也没你想象的那么多:一个字段加长 3 个字符在有 1 百万条记录,再加上一点索引的情况下才不过让整个数据库多占据 3MB 的空间。但这额外占据的空间却无需将来重构整个数据库就可以实现数据库规模的增长了。身份证的号码从 15 位变成 18 位就是最好和最惨痛的例子。
24. 列[字段]命名技巧
我们发现,假如你给每个表的列[字段]名都采用统一的前缀,那么在编写 SQL 表达式的时候会得到大大的简化。这样做也确实有缺点,比如破坏了自动表连接工具的作用,后者把公共列[字段]名同某些数据库联系起来,不过就连这些工具有时不也连接错误嘛。举个简单的例子,假设有两个表:
Customer 和 Order。Customer 表的前缀是 cu_,所以该表内的子段名如下:cu_name_id、cu_surname、cu_initials 和cu_address 等。Order 表的前缀是 or_,所以子段名是:
or_order_id、or_cust_name_id、or_quantity 和 or_description 等。
这样从数据库中选出全部数据的 SQL 语句可以写成如下所示:
Select * From Customer, Order Where cu_surname = "MYNAME" ;
and cu_name_id = or_cust_name_id and or_quantity = 1
在没有这些前缀的情况下则写成这个样子(用别名来区分):
Select * From Customer, Order Where Customer.surname = "MYNAME" ;
and Customer.name_id = Order.cust_name_id and Order.quantity = 1
第 1 个 SQL 语句没少键入多少字符。但如果查询涉及到 5 个表乃至更多的列[字段]你就知道这个技巧多有用了。
Available Mbytes: 可用物理内存数 . 如果 Available Mbytes 的值很小( 4 MB 或更小),则说明计算机上总的内存可能不足,或某程序没有释放内存。
page/sec: 表明由于硬件页面错误而从磁盘取出的页面数,或由于页面错误而写入磁盘以释放工作集空间的页面数。一般如果 pages/sec 持续高于几百,那么您应该进一步研究页交换活动。有可能需要增加内存,以减少换页的需求(你可以把这个数字乘以 4k 就得到由此引起的硬盘数据流量)。 Pages/sec 的值很大不一定表明内存有问题,而可能是运行使用内存映射文件的程序所致。
page read/sec: 页的硬故障, page/sec 的子集,为了解析对内存的引用,必须读取页文件的次数。阈值为 >5. 越低越好。大数值表示磁盘读而不是缓存读。
由于过多的页交换要使用大量的硬盘空间,因此有可能将导致将页交换内存不足与导致页交换的磁盘瓶径混淆。因此,在研究内存不足不太明显的页交换的原因时,您必须跟踪如下的磁盘使用情况计数器和内存计数器:
Physical Disk/ % Disk Time
Physical Disk/ Avg.Disk Queue Length
例如,包括 Page Reads/sec 和 % Disk Time 及 Avg.Disk Queue Length 。如果页面读取操作速率很低,同时 % Disk Time 和 Avg.Disk Queue Length 的值很高,则可能有磁盘瓶径。但是,如果队列长度增加的同时页面读取速率并未降低,则内存不足。
要确定过多的页交换对磁盘活动的影响,请将 Physical Disk/ Avg.Disk sec/Transfer 和 Memory/ Pages/sec 计数器的值增大数倍。如果这些计数器的计数结果超过了 0.1 ,那么页交换将花费百分之十以上的磁盘访问时间。如果长时间发生这种情况,那么您可能需要更多的内存。
Page Faults/sec: 每秒软性页面失效的数目(包括有些可以直接在内存中满足而有些需要从硬盘读取)较 page/sec 只表明数据不能在内存的指定工作集中立即使用。
Cache Bytes :文件系统缓存( File System Cache ),默认情况下为 50% 的可用物理内存。如 IIS5.0 运行内存不够时,它会自动整理缓存。需要关注该计数器的趋势变化
如果您怀疑有内存泄露,请监视 Memory/ Available Bytes 和 Memory/ Committed Bytes ,以观察内存行为,并监视您认为可能在泄露内存的进程的 Process/Private Bytes 、 Process/Working Set 和 Process/Handle Count 。如果您怀疑是内核模式进程导致了泄露,则还应该监视 Memory/Pool Nonpaged Bytes 、 Memory/ Pool Nonpaged Allocs 和 Process(process_name)/ Pool Nonpaged Bytes 。
Pages per second : 每秒钟检索的页数。该数字应少于每秒一页。
%Processor Time: 被处理器消耗的处理器时间数量。如果服务器专用于 sql server, 可接受的最大上限是 80-85%
Page Faults/sec: 将进程产生的页故障与系统产生的相比较,以判断这个进程对系统页故障产生的影响。
Work set: 处理线程最近使用的内存页,反映了每一个进程使用的内存页的数量。如果服务器有足够的空闲内存,页就会被留在工作集中,当自由内存少于一个特定的阈值时,页就会被清除出工作集。
Inetinfo:Private Bytes: 此进程所分配的无法与其它进程共享的当前字节数量。如果系统性能随着时间而降低,则此计数器可以是内存泄漏的最佳指示器。
%Processor Time: 如果该值持续超过 95% ,表明瓶颈是 CPU 。可以考虑增加一个处理器或换一个更快的处理器。
%User Time: 表示耗费 CPU 的数据库操作,如排序,执行 aggregate functions 等。如果该值很高,可考虑增加索引,尽量使用简单的表联接,水平分割大表格等方法来降低该值。
%Privileged Time :( CPU 内核时间)是在特权模式下处理线程执行代码所花时间的百分比。如果该参数值和 "Physical Disk" 参数值一直很高,表明 I/O 有问题。可考虑更换更快的硬盘系统。另外设置 Tempdb in RAM ,减低 "max async IO" , "max lazy writer IO" 等措施都会降低该值。
此外,跟踪计算机的服务器工作队列当前长度的 Server Work Queues/ Queue Length 计数器会显示出处理器瓶颈。队列长度持续大于 4 则表示可能出现处理器拥塞。此计数器是特定时间的值,而不是一段时间的平均值。
% DPC Time: 越低越好。在多处理器系统中,如果这个值大于 50% 并且 Processor:% Processor Time 非常高,加入一个网卡可能会提高性能,提供的网络已经不饱和。
ContextSwitches/sec: ( 实例化 inetinfo 和 dllhost 进程 ) 如果你决定要增加线程字节池的大小,你应该监视这三个计数器(包括上面的一个)。增加线程数可能会增加上下文切换次数,这样性能不会上升反而会下降。如果十个实例的上下文切换值非常高,就应该减小线程字节池的大小。
%Disk Time %: 指所选磁盘驱动器忙于为读或写入请求提供服务所用的时间的百分比。如果三个计数器都比较大,那么硬盘不是瓶颈。如果只有 %Disk Time 比较大,另外两个都比较适中,硬盘可能会是瓶颈。在记录该计数器之前,请在 Windows 2000 的命令行窗口中运行 diskperf -yD 。若数值持续超过 80% ,则可能是内存泄漏。
Avg.Disk Queue Length: 指读取和写入请求 ( 为所选磁盘在实例间隔中列队的 ) 的平均数。该值应不超过磁盘数的 1.5~2 倍。要提高性能,可增加磁盘。注意:一个 Raid Disk 实际有多个磁盘。
Average Disk Read/Write Queue Length: 指读取 ( 写入 ) 请求 ( 列队 ) 的平均数。
Disk Reads(Writes)/s: 物理磁盘上每秒钟磁盘读、写的次数。两者相加,应小于磁盘设备最大容量。
Average Disksec/Read: 指以秒计算的在此盘上读取数据的所需平均时间。
Average Disk sec/Transfer: 指以秒计算的在此盘上写入数据的所需平均时间。
Network Interface :
Bytes Total/sec : 为发送和接收字节的速率,包括帧字符在内。判断网络连接速度是否是瓶颈,可以用该计数器的值和目前网络的带宽比较
Access Methods( 访问方法 ) 用于监视访问数据库中的逻辑页的方法。
. Full Scans/sec( 全表扫描 / 秒 ) 每秒不受限的完全扫描数。可以是基本表扫描或全索引扫描。如果这个计数器显示的值比 1 或 2 高,应该分析你的查询以确定是否确实需要全表扫描,以及 S Q L 查询是否可以被优化。
. Page splits/sec( 页分割 / 秒 ) 由于数据更新操作引起的每秒页分割的数量。
Buffer Manager( 缓冲器管理器 ) :监视 Microsoft® SQL Server? 如何使用: 内存存储数据页、内部数据结构和过程高速缓存;计数器在 SQL Server 从磁盘读取数据库页和将数据库页写入磁盘时监视物理 I/O 。 监视 SQL Server 所使用的内存和计数器有助于确定: 是否由于缺少可用物理内存存储高速缓存中经常访问的数据而导致瓶颈存在。如果是这样, SQL Server 必须从磁盘检索数据。 是否可通过添加更多内存或使更多内存可用于数据高速缓存或 SQL Server 内部结构来提高查询性能。
SQL Server 需要从磁盘读取数据的频率。与其它操作相比,例如内存访问,物理 I/O 会耗费大量时间。尽可能减少物理 I/O 可以提高查询性能。
.Page Reads/sec :每秒发出的物理数据库页读取数。这一统计信息显示的是在所有数据库间的物理页读取总数。由于物理 I/O 的开销大,可以通过使用更大的数据高速缓存、智能索引、更高效的查询或者改变数据库设计等方法,使开销减到最小。
.Page Writes/sec (. 写的页 / 秒 ) 每秒执行的物理数据库写的页数。
.Buffer Cache Hit Ratio. 在 “ 缓冲池 ” ( Buffer Cache/Buffer Pool )中没有被读过的页占整个缓冲池中所有页的比率。可在高速缓存中找到而不需要从磁盘中读取的页的百分比。这一比率是高速缓存命中总数除以自 SQL Server 实例启动后对高速缓存的查找总数。经过很长时间后,这一比率的变化很小。由于从高速缓存中读数据比从磁盘中读数据的开销要小得多,一般希望这一数值高一些。通常,可以通过增加 SQL Server 可用的内存数量来提高高速缓存命中率。计数器值依应用程序而定,但比率最好为 90% 或更高。增加内存直到这一数值持续高于 90% ,表示 90% 以上的数据请求可以从数据缓冲区中获得所需数据。
. Lazy Writes/sec( 惰性写 / 秒 ) 惰性写进程每秒写的缓冲区的数量。值最好为 0 。
Cache Manager( 高速缓存管理器 ) 对象提供计数器,用于监视 Microsoft® SQL Server? 如何使用内存存储对象,如存储过程、特殊和准备好的 Transact-SQL 语句以及触发器。
. Cache Hit Ratio( 高速缓存命中率,所有 Cache” 的命中率。在 SQL Server 中, Cache 可以包括 Log Cache , Buffer Cache 以及 Procedure Cache ,是一个总体的比率。 ) 高速缓存命中次数和查找次数的比率。对于查看 SQL Server 高速缓存对于你的系统如何有效,这是一个非常好的计数器。如果这个值很低,持续低于 80% ,就需要增加更多的内存。
Latches( 闩 ) 用于监视称为闩锁的内部 SQL Server 资源锁。监视闩锁以明确用户活动和资源使用情况,有助于查明性能瓶颈。
. Average Latch Wait Ti m e ( m s ) ( 平均闩等待时间 ( 毫秒 )) 一个 SQL Server 线程必须等待一个闩的平均时间,以毫秒为单位。如果这个值很高,你可能正经历严重的竞争问题。
. Latch Waits/sec ( 闩等待 / 秒 ) 在闩上每秒的等待数量。如果这个值很高,表明你正经历对资源的大量竞争。
Locks( 锁 ) 提供有关个别资源类型上的 SQL Server 锁的信息。锁加在 SQL Server 资源上(如在一个事务中进行的行读取或修改),以防止多个事务并发使用资源。例如,如果一个排它 (X) 锁被一个事务加在某一表的某一行上,在这个锁被释放前,其它事务都不可以修改这一行。尽可能少使用锁可提高并发性,从而改善性能。可以同时监视 Locks 对象的多个实例,每个实例代表一个资源类型上的一个锁。
. Number of Deadlocks/sec( 死锁的数量 / 秒 ) 导致死锁的锁请求的数量
. Average Wait Time(ms) ( 平均等待时间 ( 毫秒 )) 线程等待某种类型的锁的平均等待时间
. Lock Requests/sec( 锁请求 / 秒 ) 每秒钟某种类型的锁请求的数量。
Memory manager: 用于监视总体的服务器内存使用情况,以估计用户活动和资源使用,有助于查明性能瓶颈。监视 SQL Server 实例所使用的内存有助于确定:
是否由于缺少可用物理内存存储高速缓存中经常访问的数据而导致瓶颈存在。如果是这样, SQL Server 必须从磁盘检索数据。
是否可以通过添加更多内存或使更多内存可用于数据高速缓存或 SQL Server 内部结构来提高查询性能。
Lock blocks: 服务器上锁定块的数量,锁是在页、行或者表这样的资源上。不希望看到一个增长的值。
Total server memory : sql server 服务器当前正在使用的动态内存总量 .
Internet Information Services Global:
File Cache Hits % 、 File CacheFlushes 、 File Cache Hits
File Cache Hits % 是全部缓存请求中缓存命中次数所占的比例,反映了 IIS 的文件缓存设置的工作情况。对于一个大部分是静态网页组成的网站,该值应该保持在 80% 左右。而 File Cache Hits 是文件缓存命中的具体值, File CacheFlushes 是自服务器启动之后文件缓存刷新次数,如果刷新太慢,会浪费内存;如果刷新太快,缓存中的对象会太频繁的丢弃生成,起不到缓存的作用。通过比较 File Cache Hits 和 File Cache Flushes 可得出缓存命中率对缓存清空率的比率。通过观察它两个的值,可以得到一个适当的刷新值(参考 IIS 的设置 ObjectTTL 、 MemCacheSize 、 MaxCacheFileSize )
Web Service:
Bytes Total/sec: 显示 Web 服务器发送和接受的总字节数。低数值表明该 IIS 正在以较低的速度进行数据传输。
Connection Refused :数值越低越好。高数值表明网络适配器或处理器存在瓶颈。
Not Found Errors :显示由于被请求文件无法找到而无法由服务器满足的请求数( HTTP 状态代码 404 )
CVS
使用经验谈
| ||
|
1、扩展关系:插入基用例所未知的附加行为,只有基用例能实例化。
2、概括关系:一般化关系,特殊用例继承和增加了基用例的特征。
3、包含关系:用例可以简单的合并其他的用例,将其作为自身行为的片段。
分类和聚集是表达主体一部分关系的关联
1、试图
用例视图(use case view),强调从用户的角度看到的或需要的系统功能,是被称为参与者的外部用户所能观察到的系统功能的模型图;
逻辑视图(logical view),展现系统的静态或结构组成及特征,也称为结构模型视图(structural model view)或静态视图(static view);
并发视图(concurrent view),体现了系统的动态或行为特征,也称为行为模型视图(behavioral model view)、动态视图(dynamic view);
组件视图(component view),体现了系统实现的结构和行为特征,也称为实现模型视图(implementation model view) ;
配置视图(deployment view),体现了系统实现环境的结构和行为特征,也称为环境模型视图(environment model view)或物理视图(physical view)。
用例图use case diagram,描述系统功能
类图class diagram,描述系统的静态结构
对象图object diagram,描述系统在某个时刻的静态结构
序列图sequence diagram,按时间顺序描述系统元素间的交互
协作图Collaboration diagram,按照时间和空间顺序描述系统元素间的交互和它们之间的关系
状态图state diagram,描述了系统元素的状态条件和响应
活动图activity diagram,描述了系统元素的活动
组件图component diagram,描述了实现系统的元素的组织
配置图deployment diagram,描述了环境元素的配置,并把实现系统的元素映射到配置上
用户模型视图:用例图
结构模型视图:类图、对象图
行为模型视图:序列图、协作图、状态图、活动图(动态图)
实现模型视图:组件图
环境模型视图:配置图
用例模型--用用例图、顺序图、协作图、状态图和活 动图描述。
分析、设计模型--用类图和对象图、顺序图、协作图、状态图和活动图描述。
实现模型--可用组件图、顺序图和协作图描述。
实施模型--配置图
测试模型--测试模型引用了所有其他模型,所以它使用他们对应的所有图。
1、业务需求反映了组织机构或客户对系统、产品高层次的目标要求,通常在项目定义与范围文档中予以说明。
2、用户需求描述了用户使用产品必须要完成的任务,应在使用实例或方案脚本中予以说明。
3、功能需求定义了开发人员必须实现的软件功能,使用户利用系统能够完成他们的任务,从而满足业务需求。
4、非功能性的需求描述了系统展现给用户的行为和执行的操作等,它包括产品必须遵从的标准、规范和约束,操作界面的具体细节和构造上的限制等。
5、需求分析报告所说明的功能需求充分描述了软件系统所应具有的外部行为,在开发、测试、质量保证、项目管理以及相关项目功能中起着重要作用。
单元测试(模块测试):针对每个模块进行的测试,可从程序的内部结构出发设计测试用例,多个模块可以平行地对立地测试。通常在编码阶段进行,必要的时候要制作驱动模块和桩模块。
集成测试:在单元测试的基础上,将所有模块按照设计要求组装成为系统,必须精心计划,应提交集成测试计划、集成测试规格说明和集成测试分析报告。
确认测试:验证软件的功能和性能及其它特性是否与用户的要求一致。
系统测试:将软件放在整个计算机环境下,包括软硬件平台、某些支持软件、数据和人员等,在实际运行环境下进行一系列的测试。
回归测试:即全部或部分地重复已做过的测试。
黑盒测试也称功能测试或数据驱动测试,它是在已知产品所应具有的功能,通过测试来检测每个功能是否都能正常使用,在测试时,把程序看作一个不能打开的黑盆子,在完全不考虑程序内部结构和内部特性的情况下,测试者在程序接口进行测试,它只检查程序功能是否按照需求规格说明书的规定正常使用,程序是否能适当地接收输入数锯而产生正确的输出信息,并且保持外部信息(如数据库或文件)的完整性。黑盒测试方法主要有等价类划分、边值分析、因—果图、错误推测等,主要用于软件确认测试。 “黑盒”法着眼于程序外部结构、不考虑内部逻辑结构、针对软件界面和软件功能进行测试。“黑盒”法是穷举输入测试,只有把所有可能的输入都作为测试情况使用,才能以这种方法查出程序中所有的错误。实际上测试情况有无穷多个,人们不仅要测试所有合法的输入,而且还要对那些不合法但是可能的输入进行测试。
白盒测试也称结构测试或逻辑驱动测试,它是知道产品内部工作过程,可通过测试来检测产品内部动作是否按照规格说明书的规定正常进行,按照程序内部的结构测试程序,检验程序中的每条通路是否都有能按预定要求正确工作,而不顾它的功能,白盒测试的主要方法有逻辑驱动、基路测试等,主要用于软件验证。“白盒”法全面了解程序内部逻辑结构、对所有逻辑路径进行测试。“白盒”法是穷举路径测试。在使用这一方案时,测试者必须检查程序的内部结构,从检查程序的逻辑着手,得出测试数据。贯穿程序的独立路径数是天文数字。但即使每条路径都测试了仍然可能有错误。第一,穷举路径测试决不能查出程序违反了设计规范,即程序本身是个错误的程序。第二,穷举路径测试不可能查出程序中因遗漏路径而出错。第三,穷举路径测试可能发现不了一些与数据相关的错误
$PATH 执行命令时所搜寻的目录
$TZ 时区
$MAILCHECK 每隔多少秒检查是否有新的信件
$PS1 在命令列时的提示号
$PS2 当命令尚未打完时,Shell 要求再输入时的提示号
$MANPATH man 指令的搜寻路径
$0 这个程式的执行名字
$n 这个程式的第n个参数值,n=1..9 $* 这个程式的所有参数
$# 这个程式的参数个数
$$ 这个程式的PID $! 执行上一个背景指令的PID
$? 执行上一个指令的返回值
MIDI ,即乐器数字化接口,是一种用于计算机与电子乐器之间进行数据交换的通信标准。 MIDI 文件(通常以 .mid 为文件扩展名)记录了用于合成 MIDI 音乐的各种控制指令,包括发声乐器、所用通道、音量大小等。由于 MIDI 文件本身不包含任何数字音频信号,因而所占的贮存空间比 wav 文件要小得多。 MIDI 文件回放需要通过声卡的 MIDI 合成器合成为不同的声音,而合成的方式有 FM (调频)与 Wave table (波表)两种。
早期的声卡及目前大多数廉价的声卡都采用的 FM 合成方式。 FM 合成是通过振荡器产生正弦波,然后再叠加成各种乐器的波形。由于振荡器成本较高,即使是 OPL3 这类高档的 FM 合成器也只提供了 4 个振荡器,仅能产生 20 种复音。因此 MIDI 音乐听起来生硬呆板,带有明显的人工合成色彩,即所谓的电子声。为了使 MIDI 合成
与 FM 合成不同,波表合成是采用真实的声音样本进行回放。声音样本记录了各种真实乐器的波形采样,并保存在声卡上的 ROM 或 RAM 中。因此,要分辨一块声卡是否波表声卡,只需看卡上有没有 ROM 或 RAM 存储器即可。创通的 Sound Blaster AWE32 是第一块广为流行的波表声卡。该卡采用了 EMU8000 波表处理芯片,提供 16 位 MIDI 通道和 32 位的复音效果。板上自带 2M ROM ,并可通过增购波表子卡扩充至 28M ,其 MIDI 回放效果绝非 SB16 可比。无可否认,通过波表合成的声音比 FM 合成的声音更为丰富和真实,但由于需要额外的存储器贮存音色库,因此成本也较高。而且音色库越大,所需的存储器就越多,相应地成本也就越高。那么,对于购买普通的 FM 声卡的朋友,能否体会到波表合成的美妙音色呢?可以!如果你的声卡上有波表接口的话,如“同维二号”等,你可以通过购买专用的波表子卡实现波表合成。如果没有子卡接口,你也可以使用“软波表”这类模拟软件,在 FM 声卡上实现波表回放效果。
WinGroov 、 VSC-88 、 YXG-50 是时下最为流行的软波表,其原理跟硬波表一样,都是采用了真实的声音样本进行回放。只是硬波表的音色库是存放在声卡的 ROM 或 RAM 中,而软波表的音色库则以文件的形式存放在硬盘里,需要时再通过 CPU 进行调用。由于软波表是通过 CPU 的实时运算来回放 MIDI 音效,因此软波表对系统要求较高,通常要在 Pentium 133/16M RAM 以上才能获得较为满意的效果。
1 .确定建立数据库的目的和收集数据
数据库设计过程的第一个阶段是确定建立数据库的目的和收集数据。通常,我们也把确定建立数据库的目的称为需求分析。需求分析的任务就是通过详细调查要处理的对象来明确用户的各种需求。并且通过调查、收集和分析信息,以了解在数据库中需要存储哪些数据,要完成什么样的数据处理功能。这一过程是数据库设计的起点,它将直接影响到后面各个阶段的设计,并影响到设计结果是否合理和实用。
确定目的之后就需要根据目的收集有用的数据。在着手收集数据之前最重要的就是要调查用户的实际需求,然后分析与表达这些需求。调查用户需求的方法有很多,如查阅记录、访谈、开调查会、设计调查表请用户填写或回答相关问题等。其中比较有效的方法是访谈,我们可以借助一些设计合理的调查表来与用户直接交流。通过充分交流,可以了解他们平时是如何使用数据库的,以及对当前信息的要求,进而设计满足用户需求的字段,并根据设计的字段收集数据。
2 .建立概念模型
确定建立数据库的目的以及完成数据收集后,就进入数据库设计过程的第二阶段 —— 建立概念模型。这一阶段是整个数据库设计的关键。设计时,一般先根据应用的需求,画出能反映每个应用需求的 E-R 图,其中包括确定实体、属性和联系的类型。然后优化初始的 E-R 图,消除冗余和可能存在的矛盾。概念模型是对用户需求的客观反映,并不涉及具体的计算机软、硬件环境。因此,在这一阶段中我们必须将注意力集中在怎样表达出用户对信息的需求,而不考虑具体实现问题。
3 .建立数据模型
完成上一阶段后,我们得到了一个与具体计算机软、硬件无关的概念模型。接着我们就可以着手建立数据库模型了,这是数据库设计过程的第三个阶段。在这一阶段中我们要将概念模型中得到的 E-R 图转换成具体的数据模型。通过前面的学习,我们已经了解到数据模型一般分为层次、网状、关系和面向对象模型等。目前比较常用的是关系数据模型,我们通常将 E-R 图转换成关系数据模型,实际上就是要将实体、实体的属性和实体之间的联系转换为关系模式。
4 .实施与维护数据库
最后一个阶段是实施与维护数据库。完成数据模型的建立后,我们就必须对字段进行命名,确定字段的类型和宽度,并利用数据库管理系统或数据库语言创建数据库结构、输入数据和运行等,因此数据库的实施是数据库设计过程的 “ 最终实现 ” 。如果数据库运行很成功,则表明数据库设计任务基本结束,以后的重点就是数据库的维护工作,包括做好备份工作、数据库的安全性和完整性调整、改善数据库性能等。
数据库的设计在数据库应用系统的开发中占有很重要的地位。只有设计出合理的数据库,才能为建立在数据库上的应用提供方便。不过数据库的设计过程从来都不会有真正的结束,因为随着用户需求和具体应用的变化和扩大,数据库的结构也可能会随之变化。
数据库基本的功能:
信息浏览和查询;
信息的修改、添加和删除;
信息的统计、汇总等。
设计数据库时要注意保留以下内容:
设计文档、内容操作说明,实例数据库、帮助及过程性文件(如下载的资源、工作日志)等。