﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-weibogao-随笔分类-database development</title><link>http://www.blogjava.net/weibogao/category/19122.html</link><description>my second lifespace</description><language>zh-cn</language><lastBuildDate>Wed, 28 Feb 2007 02:12:30 GMT</lastBuildDate><pubDate>Wed, 28 Feb 2007 02:12:30 GMT</pubDate><ttl>60</ttl><item><title>Oracle的物化视图</title><link>http://www.blogjava.net/weibogao/archive/2007/01/15/93914.html</link><dc:creator>weibogao</dc:creator><author>weibogao</author><pubDate>Mon, 15 Jan 2007 04:05:00 GMT</pubDate><guid>http://www.blogjava.net/weibogao/archive/2007/01/15/93914.html</guid><wfw:comment>http://www.blogjava.net/weibogao/comments/93914.html</wfw:comment><comments>http://www.blogjava.net/weibogao/archive/2007/01/15/93914.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/weibogao/comments/commentRss/93914.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/weibogao/services/trackbacks/93914.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: Oracle				的物化视图提供了强大的功能，可以用在不同的环境中。在不同的环境中，物化视图的作用也不相同。																																		数据仓库中的物化视图主要用于预先计算并保存表连接或聚集等耗时较多的操作的结果，这样，在执行查询时，就可以避免进行这些耗时的操作，而从快速的得到结果。在数据仓库中，还经常使用查询重写（...&nbsp;&nbsp;<a href='http://www.blogjava.net/weibogao/archive/2007/01/15/93914.html'>阅读全文</a><img src ="http://www.blogjava.net/weibogao/aggbug/93914.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/weibogao/" target="_blank">weibogao</a> 2007-01-15 12:05 <a href="http://www.blogjava.net/weibogao/archive/2007/01/15/93914.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Effective SQL</title><link>http://www.blogjava.net/weibogao/archive/2007/01/12/93392.html</link><dc:creator>weibogao</dc:creator><author>weibogao</author><pubDate>Fri, 12 Jan 2007 04:56:00 GMT</pubDate><guid>http://www.blogjava.net/weibogao/archive/2007/01/12/93392.html</guid><wfw:comment>http://www.blogjava.net/weibogao/comments/93392.html</wfw:comment><comments>http://www.blogjava.net/weibogao/archive/2007/01/12/93392.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/weibogao/comments/commentRss/93392.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/weibogao/services/trackbacks/93392.html</trackback:ping><description><![CDATA[
		<p>一.名词解释： <br />0。SQL 结构化查询语言(Structured Query Language) <br /><br />1。非关系型数据库系统 <br />做为第一代数据库系统的总称，其包括2种类型：“层次”数据库与“网状”数据库 <br /><br />“层次”数据库管理系统 eg:IBM&amp;IMS (Information  Management System) <br />特点:数据按层次模型组织 <br /><br />"网状"数据库 <br />特点：数据按网状模型组织 <br /><br />2。关系型数据库系统 <br />关系性数据库管理系统 (RDBMS) <br />eg:SQL/DS , DB2, Oracle ,Informix ,Unity,dBASE等 <br />特点：数据按二维的表格组织。 <br /><br />3。数据库(DataBase) <br />按一定结构存储在计算机中相互关联的数据的集合。 <br /><br />4。数据库管理系统DBMS(Database Management System) <br />一个通用的软件系统。就是让你怎么管理你的数据库。其中包括存储，安全，完整性管理<br />等。 <br /><br />5。数据库应用系统DBAS （Database Application System） <br />数据库应用程序系统，建立在DBMS基础之上的。就是一个面向用户的软件系统。 <br /><br />6。ANSI标准 （American National Standards Institute）美国国家标准委员会 <br />因为1999年第2次更新SQL，所以SQL又称为SQL99或SQL3（第3版，前2个版本分别为1986年<br />的sql ,1992 年的sql2/sql92）。 <br /><br />7。SQL语句的3种类型 <br />数据操作语句(Data Manipulation Language ) DML 关于数据操作命令的  eg:select,in<br />sert,update,delete <br />数据定义语句(Data Definition Language ) DDL     关于数据对象访问的  eg:create，<br /> drop <br />数据控制语句(Data Control Language) DCL         关于权限的  eg:grant ，revoke <br /><br /><br />8。PL/SQL Procedural Language/sql <br />用于oracle的语言 <br /><br />9.T-SQL  transact-sql <br />用于 microsoft sql server 和sybase adaptive server <br /><br />10。E.F.Codd关于关系型数据库12条检验原则（MYSQL，不支持视图和原子事物处理，所以<br />排除） <br />内容：暂略 <br /><br />11。数据库设计之新奥尔良方法。 <br />需求分析==》概念设计==》逻辑设计==》物理设计. <br />4个步骤的具体中以需求分析最重要. <br />需求分析的内容:暂略 <br />概念设计的内容:暂略 <br />逻辑设计的内容:暂略 <br />物理设计的内容:暂略 <br /><br /><br />二.数据库优化方案 <br />1.索引 <br />一 概述 <br /><br />  可以利用索引快速访问数据库表中的特定信息。索引是对数据库表中一个或多个列的值<br />进行排序的结构。 <br />  索引提供指针以指向存储在表中指定列的数据值，然后根据指定的排序次序排列这些指<br />针。 <br />  数据库使用索引的方式与使用书的目录很相似：通过搜索索引找到特定的值， <br />  然后跟随指针到达包含该值的行 <br /><br />索引是一个单独的、物理的数据库结构，它是某个表中一列或若干列值的集合和相应的指<br />向表中物理标识这些值的数据页的逻辑指针清单。 <br /><br />一个表的存储是由两部分组成的，一部分用来存放表的数据页面，另一部分存放索引页面<br />。索引就存放在索引页面上 <br /><br />二 索引的两种类型： <br /><br />聚集索引=簇集索引 <br /><br />聚集索引基于数据行的键值在表内排序和存储这些数据行。由于数据行按基于聚集索引键<br />的排序次序存储， <br />因此聚集索引对查找行很有效。每个表只能有一个聚集索引，因为数据行本身只能按一个<br />顺序存储。 <br />数据行本身构成聚集索引的最低级别。 <br /><br />只有当表包含聚集索引时，表内的数据行才按排序次序存储。如果表没有聚集索引， <br />则其数据行按堆集方式存储。 <br /><br />聚集索引对于那些经常要搜索范围值的列特别有效。使用聚集索引找到包含第一个值的行<br />后， <br />便可以确保包含后续索引值的行在物理相邻。例如，如果应用程序执行的一个查询经常检<br />索某一日期范围 <br />内的记录，则使用聚集索引可以迅速找到包含开始日期的行，然后检索表中所有相邻的行<br />， <br />直到到达结束日期。这样有助于提高此类查询的性能。同样，如果对从表中检索的数据进<br />行排序时 <br />经常要用到某一列，则可以将该表在该列上聚集（物理排序），避免每次查询该列时都进<br />行排序， <br />从而节省成本 <br /><br />非聚集索引 <br /><br />非聚集索引具有完全独立于数据行的结构。非聚集索引的最低行包含非聚集索引的键值，<br /><br />并且每个键值项都有指针指向包含该键值的数据行。数据行不按基于非聚集键的次序存储<br />。 <br /><br />在非聚集索引内，从索引行指向数据行的指针称为行定位器。 <br />行定位器的结构取决于数据页的存储方式是堆集还是聚集。对于堆集，行定位器是指向行<br />的指针。 <br />对于有聚集索引的表，行定位器是聚集索引键。 <br />只有在表上创建了聚集索引时，表内的行才按特定的顺序存储。这些行就基于聚集索引键<br />按顺序存储。 <br />如果一个表只有非聚集索引，它的数据行将按无序的堆集方式存储 <br />非聚集索引可以建多个,两者都能改善查询性能 <br /><br />非聚集索引与聚集索引一样有 B 树结构，但是有两个重大差别： <br />数据行不按非聚集索引键的顺序排序和存储。 <br />非聚集索引的叶层不包含数据页。 <br />相反，叶节点包含索引行。每个索引行包含非聚集键值以及一个或多个行定位器， <br />这些行定位器指向有该键值的数据行（如果索引不唯一，则可能是多行）。 <br />非聚集索引可以在有聚集索引的表、堆集或索引视图上定义 <br /><br /><br />聚集索引--&gt;顺序表结构.其物理数据和逻辑排序紧邻. <br />非聚集索引--&gt;单链表结构.起物理和逻辑排序不按顺序排列. <br /><br />打个比方. <br />一本字典,你现在查一个陈字.你有2种方法.首先,你在知道他念chen的情况下去按照拼音字<br />母去查找.他是排在字母A,B <br />于是你很容易的就找到"陈"字.第2种方法则是按编旁查找,先找到耳朵旁,去找到一个临时<br />的编旁表在去找"东"这个字,然后按照给出的 <br />页数找到相应的位置. <br />显然,第一种方法就是聚集索引,按照物理位置根据排序来查找. <br />第2种方法则是非聚集索引,按照一个临时索引来查找. <br /><br />另外 <br />唯一索引 <br /><br />唯一索引可以确保索引列不包含重复的值。在多列唯一索引的情况下，该索引可以确保索<br />引列中每个值组 <br />合都是唯一的。唯一索引既是索引也是约束。 <br /><br />复合索引 <br />索引项是多个的就叫组合索引，也叫复合索引。复合索引使用时需要注意索引项的次序。<br /><br /><br />二 索引的创建 <br /><br />有两种方法可以在 SQL Server 内定义索引: CREATE INDEX 语句和CREATE TABLE 语句 <br /><br /><br />CREATE TABLE支持在创建索引时使用下列约束： <br /><br />PRIMARY KEY 创建唯一索引来强制执行主键 <br />UNIQUE 创建唯一索引 <br />CLUSTERED 创建聚集索引 <br />NONCLUSTERED 创建非聚集索引 <br /><br />注: 1 定义索引时，可以指定每列的数据是按升序还是降序存储。如果不指定，则默认为<br />升序 <br />   2 支持在计算列上创建索引 <br />   3 为索引指定填充因子 <br />     可标识填充因子来指定每个索引页的填满程度。索引页上的空余空间量很重要， <br />     因为当索引页填满时，系统必须花时间拆分它以便为新行腾出空间。 <br /><br /><br />三 索引的维护语句 <br /><br />DBCC DBREINDEX    重建指定数据库中表的一个或多个索引 <br />DBCC INDEXFRAG　　整理指定的表或视图的聚集索引和辅助索引碎片 <br /><br />比较 <br /><br />            速度    兼容性     日志影响      数据访问影响       额外磁盘空间 <br /><br />DBCC        最快      最好     大,但能通过把   操作过程中数据不   需要大 <br />DBREINDEX             可以重   故障还原模型设  能访问，影响大 <br />                     建所有   为简单减少日志    <br />                     有索引 <br /><br />DBCC        慢       但可   必须分   小              数据未被锁定        需要小<br /><br />INDEXDEFRAG          随时终 别指定 <br />                    止执行   <br />                               <br /><br />drop index    中等  必须分   大,但能通过把    仅在操作执行时    中等，操作在  <br />  <br />create index        别指定   故障还原模型设   锁定数据          tempdb中进行 <br /><br />                            为简单减少日志 <br /><br /><br />四 查看索引的方法 <br /><br />sp_indexes        返回指定远程表的索引信息 <br />INDEXKEY_PROPERTY 返回有关索引键的信息 <br />sysindexes系统表  数据库中的每个索引和表在表中各占一行，该表存储在每个数据库中<br /><br /><br /><br />五 可以通过执行计划 <br />  查看sql语句执行时是否建立在索引之上 <br /><br />比如 <br />CREATE TABLE Test <br />(Field_1 int NOT NULL, <br />Field_2 int CONSTRAINT PK_Test <br />PRIMARY KEY CLUSTERED (Field_1)) <br /><br />CREATE index IX_Test ON Test (Field_2) <br /><br />1 SELECT * FROM Test WHERE Field_2 =408 <br /> 执行计划可以看出使用了IX_Test索引 <br />2 SELECT * FROM Test WHERE Field_1 =1 <br /> 执行计划可以看出使用了PK_Test <br />3 但如果是SELECT * FROM Test with (index(IX_Test)) WHERE Field_1 =1 <br /> 则指定使用索引 <br /><br /><br />六 索引的具体使用 <br /><br />1） 索引的设计 <br />A:尽量避免表扫描 <br />检查你的查询语句的where子句，因为这是优化器重要关注的地方。包含在where里面的每<br />一列（column)都是可能的侯选索引，为能达到最优的性能，考虑在下面给出的例子：对于<br />在where子句中给出了column1这个列。 <br />下面的两个条件可以提高索引的优化查询性能！ <br />第一：在表中的column1列上有一个单索引 <br />第二：在表中有多索引，但是column1是第一个索引的列 <br />避免定义多索引而column1是第二个或后面的索引，这样的索引不能优化服务器性能 <br />例如：下面的例子用了pubs数据库。 <br />SELECT au_id, au_lname, au_fname FROM authors <br />WHERE au_lname = ’White’ <br />按下面几个列上建立的索引将会是对优化器有用的索引 <br />?au_lname <br />?au_lname, au_fname <br />而在下面几个列上建立的索引将不会对优化器起到好的作用 <br />?au_address <br />?au_fname, au_lname <br />考虑使用窄的索引在一个或两个列上，窄索引比多索引和复合索引更能有效。用窄的索引<br />，在每一页上 <br />将会有更多的行和更少的索引级别（相对与多索引和复合索引而言），这将推进系统性能<br />。 <br />对于多列索引，SQL Server维持一个在所有列的索引上的密度统计（用于联合）和在第一<br />个索引上的 <br />histogram（柱状图）统计。根据统计结果，如果在复合索引上的第一个索引很少被选择使<br />用，那么优化器对很多查询请求将不会使用索引。 <br />有用的索引会提高select语句的性能，包括insert,uodate,delete。 <br />但是，由于改变一个表的内容，将会影响索引。每一个insert,update,delete语句将会使<br />性能下降一些。实验表明，不要在一个单表上用大量的索引，不要在共享的列上（指在多<br />表中用了参考约束）使用重叠的索引。 <br />在某一列上检查唯一的数据的个数，比较它与表中数据的行数做一个比较。这就是数据的<br />选择性，这比较结果将会帮助你决定是否将某一列作为侯选的索引列，如果需要，建哪一<br />种索引。你可以用下面的查询语句返回某一列的不同值的数目。 <br />select count(distinct cloumn_name) from table_name <br />假设column_name是一个10000行的表，则看column_name返回值来决定是否应该使用，及应<br />该使用什么索引。 <br />Unique values Index <br /><br />5000 Nonclustered index <br />20 Clustered index <br />3 No index <br /><br /><br />2) 镞索引和非镞索引的选择 <br /><br />&lt;1:&gt;镞索引是行的物理顺序和索引的顺序是一致的。页级，低层等索引的各个级别上都包<br />含实际的数据页。一个表只能是有一个镞索引。由于update,delete语句要求相对多一些的<br />读操作，因此镞索引常常能加速这样的操作。在至少有一个索引的表中，你应该有一个镞<br />索引。 <br />在下面的几个情况下，你可以考虑用镞索引： <br />例如： 某列包括的不同值的个数是有限的（但是不是极少的） <br />顾客表的州名列有50个左右的不同州名的缩写值，可以使用镞索引。 <br />例如： 对返回一定范围内值的列可以使用镞索引，比如用between,&gt;,&gt;=,&lt;,&lt;=等等来对列<br />进行操作的列上。 <br />select * from sales where ord_date between ’5/1/93’ and ’6/1/93’ <br />例如： 对查询时返回大量结果的列可以使用镞索引。 <br />SELECT * FROM phonebook WHERE last_name = ’Smith’ <br /><br />当有大量的行正在被插入表中时，要避免在本表一个自然增长（例如，identity列）的列<br />上建立镞索引。如果你建立了镞的索引，那么insert的性能就会大大降低。因为每一个插<br />入的行必须到表的最后，表的最后一个数据页。 <br />当一个数据正在被插入（这时这个数据页是被锁定的），所有的其他插入行必须等待直到<br />当前的插入已经结束。 <br />一个索引的叶级页中包括实际的数据页，并且在硬盘上的数据页的次序是跟镞索引的逻辑<br />次序一样的。 <br /><br />&lt;2:&gt;一个非镞的索引就是行的物理次序与索引的次序是不同的。一个非镞索引的叶级包含<br />了指向行数据页的指针。 <br />在一个表中可以有多个非镞索引，你可以在以下几个情况下考虑使用非镞索引。 <br />在有很多不同值的列上可以考虑使用非镞索引 <br />例如：一个part_id列在一个part表中 <br />select * from employee where emp_id = ’pcm9809f’ <br />查询语句中用order by 子句的列上可以考虑使用镞索引 <br /><br /><br /><br />3) 一个表列如果设为主键(primary key),它会自动生成一个聚簇索引 <br />这时不能直接使用Drop index Table1.Tableindex1语句 <br />必须删除主键约束，用语句:alter table table1 drop constraint 约束名(如pk_xxx) <br /><br /><br /><br />七.全文索引 <br />use pubs <br />　　go <br /><br />　　--打开数据库全文索引的支持 <br /><br />　execute sp_fulltext_database 'enable' <br />　go <br /><br />　　--建立全文目录ft_titles <br /><br />　　execute sp_fulltext_catalog 'ft_titles', 'create' <br />　　go <br /><br />　　--为titles表建立全文索引数据元，UPKCL_titleidind是主键所建立的唯一索引，可<br />由sp_help titles得知 <br /><br />　　execute sp_fulltext_table 'titles','create', 'ft_titles', 'UPKCL_titleidin<br />d' <br />　　go <br /><br />　　--设置全文索引列名 <br /><br />　　exec sp_fulltext_column 'titles', 'title', 'add' <br />　　go <br />　　exec sp_fulltext_column 'titles', 'notes', 'add' <br />　　go <br /><br />　　--建立全文索引 <br /><br />　　exec sp_fulltext_table 'titles', 'activate' <br />　　go <br /><br />　　--填充全文索引目录 <br /><br />　　exec sp_fulltext_catalog 'ft_titles', 'start_full' <br />　　go <br /><br />　　--使用contains和freetext <br /><br />　　select title, notes from titles <br />　　where contains(title, '"computer Cooking"') <br />　　go <br />　　select title, notes from titles <br />　　where freetext(title, 'computer Cooking') <br />　　go <br />　　select title, notes from titles <br />　　where freetext(title, '"computer Cooking"') <br />　　go <br />　　select title, notes from titles <br />　　where contains(title, 'computer') <br />　　go <br />　　select title, notes from titles <br />　　where freetext (*, 'computer') <br />　　go <br /><br />这里提一下google的搜索引擎的原理. <br />他把每个字词都做为单元去查询. <br />打个比方:我在字典里查询,现在我要搜索"树型"这个词,他会把这个树型这个词全文扫描一<br />遍,生成一个二叉树.并记下他的页数. <br />然后当我第2次查找的时候显然这个"记忆"提示,然后"提取".如果你对某一个字段做了全文<br />索引的话，他会全文扫描表一遍,然后纪录下 <br />相应的纪录,生成二叉树. <br />如果我要查找"树叶",同理也可以得出页数.但当我们去查找一下"树型结构"他则会把"树型<br />"和"树型结构"都"纪录"下来. <br /><br />八.巧妙的使用索引. <br />SELECT SUM(quantity) AS quantity FROM test WHERE... <br />1.若WHERE 里用的是字段与常量比较，MSSQL会自动引用该字段上的索引；若用的是变量，<br />MSSQL不会自动引用该字段上的索引而是根据聚集索引进行扫描 <br />2.加上with(index(索引名))指定索引，即： <br />SELECT SUM(quantity) AS quantity FROM with(index(索引名)) test WHERE... <br />指定索引后，WHERE 里不论是常量还是变量，MSSQL都根据指定的索引进行扫描 <br />3.DBCC DBREINDEX执行并不一定能优化MSSQL性能，慎用 <br />4.如果在pub_id上建立索引的话 <br />select * from titles where pub_id-500 &gt;1000   ---------(a) <br />select * from titles where pub_id &gt;1000+500  -----------(b) <br />请选用(b)语句,这样的话，他会利用索引,而(a)的话由于对字段操作了,所以不会利用索引<br />. <br />5.尽量避免用like语句, <br />如果去查找baa%,caa%的话 <br />如果是like '%aa%','_aa%','[m-z]o%'  则根本不会用到索引. <br />替换方法.columns like 'baa%' or  columns like 'caa %' <br />6什么情况下应不建或少建索引 <br />a.表记录太少 .因为索引的话，要对数据库往返2次操作,如果1个表只有几行字段的话，数<br />据库会对他的纪录一次性全部取出来,这样的效率要远远高于索引. <br />b.经常insert,delete,update的表  对一些经常处理的业务表应在查询允许的情况下尽量<br />减少索引 <br />c.数据重复且分布平均的表字段,如:性别字段,各占50%的话，你即使建了,也起不到明显的<br />作用. <br />d.经常和主字段一块查询但主字段索引值比较多的表字段 <br />表经常按收费序号、户标识编号、抄表日期、电费发生年月、操作标志来具体查询某一笔<br />收款的情况，如果将所有的字段都建在一个索引里那将会增加数据的修改、插入、删除时<br />间，从实际上分析一笔收款如果按收费序号索引就已经将记录减少到只有几条，如果再按<br />后面的几个字段索引查询将对性能不产生太大的影响。 <br />e.如果一个表的记录达到100万以上的话，要对其中一个字段建索引可能要花很长的时间，<br />甚至导致服务器数据库死机，因为在建索引的时候 ORACLE要将索引字段所有的内容取出并<br />进行全面排序，数据量大的话可能导致服务器排序内存不足而引用磁盘交换空间进行，这<br />将严重影响服务器数据库的工作。解决方法是增大数据库启动初始化中的排序内存参数，<br />如果要进行大量的索引修改可以设置10M以上的排序内存（ORACLE缺省大小为64K），在索<br />引建立完成后应将参数修改回来，因为在实际OLTP数据库应用中一般不会用到这么大的排<br />序内存。 <br /><br /><br />以下转载 <br />great_domino 的 Blog <br /><br />探讨如何在有着1000万条数据的MS SQL SERVER数据库中实现快速的数据提取和数据分页。<br />以下代码说明了我们实例中数据库的“红头文件”一表的部分数据结构： <br /><br />CREATE TABLE [dbo].[TGongwen] (    --TGongwen是红头文件表名 <br /><br />  [Gid] [int] IDENTITY (1, 1) NOT NULL , <br />--本表的id号，也是主键 <br /><br />  [title] [varchar] (80) COLLATE Chinese_PRC_CI_AS NULL , <br />--红头文件的标题 <br /><br />  [fariqi] [datetime] NULL , <br />--发布日期 <br /><br />  [neibuYonghu] [varchar] (70) COLLATE Chinese_PRC_CI_AS NULL , <br />--发布用户 <br /><br />  [reader] [varchar] (900) COLLATE Chinese_PRC_CI_AS NULL , <br /><br />--需要浏览的用户。每个用户中间用分隔符“,”分开 <br /><br />) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] <br /><br />GO <br /><br /><br />　　下面，我们来往数据库中添加1000万条数据： <br /><br />declare @i int <br /><br />set @i=1 <br /><br />while @i&lt;=250000 <br /><br />begin <br /><br />   insert into Tgongwen(fariqi,neibuyonghu,reader,title) values('2004-2-5','通<br />信科','通信科,办公室,王局长,刘局长,张局长,admin,刑侦支队,特勤支队,交巡警支队,经<br />侦支队, 户政科,治安支队,外事科','这是最先的25万条记录') <br /><br />   set @i=@i+1 <br /><br />end <br /><br />GO <br /><br /><br /><br />declare @i int <br /><br />set @i=1 <br /><br />while @i&lt;=250000 <br /><br />begin <br /><br />   insert into Tgongwen(fariqi,neibuyonghu,reader,title) values('2004-9-16','办<br />公室','办公室,通信科,王局长,刘局长,张局长,admin,刑侦支队,特勤支队,交巡警支队,经<br />侦支队,户政科,外事科','这是中间的25万条记录') <br /><br />   set @i=@i+1 <br /><br />end <br /><br />GO <br /><br /><br /><br />declare @h int <br /><br />set @h=1 <br /><br />while @h&lt;=100 <br /><br />begin <br /><br />declare @i int <br /><br />set @i=2002 <br /><br />while @i&lt;=2003 <br /><br />begin <br /><br />declare @j int <br /><br />       set @j=0 <br /><br />       while @j&lt;50 <br /><br />           begin <br /><br />declare @k int <br /><br />           set @k=0 <br /><br />           while @k&lt;50 <br /><br />           begin <br /><br />   insert into Tgongwen(fariqi,neibuyonghu,reader,title) values(cast(@i as var<br />char(4))+'-8-15 3:'+cast(@j as varchar(2))+':'+cast(@j as varchar(2)),'通信科'<br />,'办公室,通信科,王局长,刘局长,张局长,admin,刑侦支队,特勤支队,交巡警支队,经侦支<br />队,户政科,外事科','这是最后的50万条记录') <br /><br />           set @k=@k+1 <br /><br />           end <br /><br />set @j=@j+1 <br /><br />       end <br /><br />set @i=@i+1 <br /><br />end <br /><br />set @h=@h+1 <br /><br />end <br /><br />GO <br /><br /><br /><br />declare @i int <br /><br />set @i=1 <br /><br />while @i&lt;=9000000 <br /><br />begin <br /><br />   insert into Tgongwen(fariqi,neibuyonghu,reader,title) values('2004-5-5','通<br />信科','通信科,办公室,王局长,刘局长,张局长,admin,刑侦支队,特勤支队,交巡警支队,经<br />侦支队, 户政科,治安支队,外事科','这是最后添加的900万条记录') <br /><br />   set @i=@i+1000000 <br /><br />end <br /><br />GO <br /><br />通过以上语句，我们创建了25万条由于2004年2月5日发布的记录，25万条由办公室于2004<br />年9月6日发布的记录，2002年和2003年各 100个2500条相同日期、不同分秒的记录（共50<br />万条），还有由通信科于2004年5月5日发布的900万条记录，合计1000万条。 <br /><br />何时使用聚集索引或非聚集索引 <br /><br />　　下面的表总结了何时使用聚集索引或非聚集索引（很重要）。 <br /><br />　　动作描述 <br />　　　使用聚集索引 <br />　　　使用非聚集索引 <br /><br />　　列经常被分组排序 <br />　　　应 <br />　　　应 <br /><br />　　返回某范围内的数据 <br />　　　应 <br />　　　不应 <br /><br />　　一个或极少不同值 <br />　　　不应 <br />　　　不应 <br /><br />　　小数目的不同值 <br />　　　应 <br />　　　不应 <br /><br />　　大数目的不同值 <br />　　　不应 <br />　　　应 <br /><br />　　频繁更新的列 <br />　　　不应 <br />　　　应 <br /><br />　　外键列 <br />　　　应 <br />　　　应 <br /><br />　　主键列 <br />　　　应 <br />　　　应 <br /><br />　　频繁修改索引列 <br />　　　不应 <br />　　　应 <br /><br /><br />　　事实上，我们可以通过前面聚集索引和非聚集索引的定义的例子来理解上表。如：返<br />回某范围内的数据一项。比如您的某个表有一个时间列，恰好您把聚合索引建立在了该列<br />，这时您查询2004年1月1日至2004年10月1日之间的全部数据时，这个速度就将是很快的，<br />因为您的这本字典正文是按日期进行排序的，聚类索引只需要找到要检索的所有数据中的<br />开头和结尾数据即可；而不像非聚集索引，必须先查到目录中查到每一项数据对应的页码<br />，然后再根据页码查到具体内容。 <br /><br />（三）结合实际，谈索引使用的误区 <br /><br />　　理论的目的是应用。虽然我们刚才列出了何时应使用聚集索引或非聚集索引，但在实<br />践中以上规则却很容易被忽视或不能根据实际情况进行综合分析。下面我们将根据在实践<br />中遇到的实际问题来谈一下索引使用的误区，以便于大家掌握索引建立的方法。 <br /><br />　　1、主键就是聚集索引 <br /><br />　　这种想法笔者认为是极端错误的，是对聚集索引的一种浪费。虽然SQL SERVER默认是<br />在主键上建立聚集索引的。 <br /><br />　　通常，我们会在每个表中都建立一个ID列，以区分每条数据，并且这个ID列是自动增<br />大的，步长一般为1。我们的这个办公自动化的实例中的列 Gid就是如此。此时，如果我们<br />将这个列设为主键，SQL SERVER会将此列默认为聚集索引。这样做有好处，就是可以让您<br />的数据在数据库中按照ID进行物理排序，但笔者认为这样做意义不大。 <br /><br />　　显而易见，聚集索引的优势是很明显的，而每个表中只能有一个聚集索引的规则，这<br />使得聚集索引变得更加珍贵。 <br /><br />　　从我们前面谈到的聚集索引的定义我们可以看出，使用聚集索引的最大好处就是能够<br />根据查询要求，迅速缩小查询范围，避免全表扫描。在实际应用中，因为ID号是自动生成<br />的，我们并不知道每条记录的ID号，所以我们很难在实践中用ID号来进行查询。这就使让<br />ID号这个主键作为聚集索引成为一种资源浪费。其次，让每个ID号都不同的字段作为聚集<br />索引也不符合“大数目的不同值情况下不应建立聚合索引”规则；当然，这种情况只是针<br />对用户经常修改记录内容，特别是索引项的时候会负作用，但对于查询速度并没有影响。<br /><br /><br />　　在办公自动化系统中，无论是系统首页显示的需要用户签收的文件、会议还是用户进<br />行文件查询等任何情况下进行数据查询都离不开字段的是“日期”还有用户本身的“用户<br />名”。 <br /><br />　　通常，办公自动化的首页会显示每个用户尚未签收的文件或会议。虽然我们的where语<br />句可以仅仅限制当前用户尚未签收的情况，但如果您的系统已建立了很长时间，并且数据<br />量很大，那么，每次每个用户打开首页的时候都进行一次全表扫描，这样做意义是不大的<br />，绝大多数的用户1个月前的文件都已经浏览过了，这样做只能徒增数据库的开销而已。事<br />实上，我们完全可以让用户打开系统首页时，数据库仅仅查询这个用户近3个月来未阅览的<br />文件，通过“日期”这个字段来限制表扫描，提高查询速度。如果您的办公自动化系统已<br />经建立的2年，那么您的首页显示速度理论上将是原来速度8倍，甚至更快。 <br /><br />　　在这里之所以提到“理论上”三字，是因为如果您的聚集索引还是盲目地建在ID这个<br />主键上时，您的查询速度是没有这么高的，即使您在“日期”这个字段上建立的索引（非<br />聚合索引）。下面我们就来看一下在1000万条数据量的情况下各种查询的速度表现（3个月<br />内的数据为25万条）： <br /><br />　　（1）仅在主键上建立聚集索引，并且不划分时间段： <br /><br />Select gid,fariqi,neibuyonghu,title from tgongwen <br /><br />　　用时：128470毫秒（即：128秒） <br /><br />　　（2）在主键上建立聚集索引，在fariq上建立非聚集索引： <br /><br />select gid,fariqi,neibuyonghu,title from Tgongwen <br /><br />where fariqi&gt; dateadd(day,-90,getdate()) <br /><br />　　用时：53763毫秒（54秒） <br /><br />　　（3）将聚合索引建立在日期列（fariqi）上： <br /><br />select gid,fariqi,neibuyonghu,title from Tgongwen <br /><br />where fariqi&gt; dateadd(day,-90,getdate()) <br /><br />　　用时：2423毫秒（2秒） <br /><br />　　虽然每条语句提取出来的都是25万条数据，各种情况的差异却是巨大的，特别是将聚<br />集索引建立在日期列时的差异。事实上，如果您的数据库真的有 1000万容量的话，把主键<br />建立在ID列上，就像以上的第1、2种情况，在网页上的表现就是超时，根本就无法显示。<br />这也是我摒弃ID列作为聚集索引的一个最重要的因素。 <br /><br />　　得出以上速度的方法是：在各个select语句前加：declare @d datetime <br /><br />set @d=getdate() <br /><br />并在select语句后加： <br /><br />select [语句执行花费时间(毫秒)]=datediff(ms,@d,getdate()) <br /><br />　　2、只要建立索引就能显著提高查询速度 <br /><br />　　事实上，我们可以发现上面的例子中，第2、3条语句完全相同，且建立索引的字段也<br />相同；不同的仅是前者在fariqi字段上建立的是非聚合索引，后者在此字段上建立的是聚<br />合索引，但查询速度却有着天壤之别。所以，并非是在任何字段上简单地建立索引就能提<br />高查询速度。 <br /><br />　　从建表的语句中，我们可以看到这个有着1000万数据的表中fariqi字段有5003个不同<br />记录。在此字段上建立聚合索引是再合适不过了。在现实中，我们每天都会发几个文件，<br />这几个文件的发文日期就相同，这完全符合建立聚集索引要求的：“既不能绝大多数都相<br />同，又不能只有极少数相同”的规则。由此看来，我们建立“适当”的聚合索引对于我们<br />提高查询速度是非常重要的。 <br /><br />　　3、把所有需要提高查询速度的字段都加进聚集索引，以提高查询速度 <br /><br />　　上面已经谈到：在进行数据查询时都离不开字段的是“日期”还有用户本身的“用户<br />名”。既然这两个字段都是如此的重要，我们可以把他们合并起来，建立一个复合索引（<br />compound index）。 <br /><br />　　很多人认为只要把任何字段加进聚集索引，就能提高查询速度，也有人感到迷惑：如<br />果把复合的聚集索引字段分开查询，那么查询速度会减慢吗？带着这个问题，我们来看一<br />下以下的查询速度（结果集都是25万条数据）：（日期列fariqi首先排在复合聚集索引的<br />起始列，用户名neibuyonghu排在后列） <br /><br />　　（1）select gid,fariqi,neibuyonghu,title from Tgongwen where fariqi&gt;'2004-<br />5-5' <br /><br />　　查询速度：2513毫秒 <br /><br />　　（2）select gid,fariqi,neibuyonghu,title from Tgongwen where fariqi&gt;'2004-<br />5-5' and neibuyonghu='办公室' <br /><br />　　查询速度：2516毫秒 <br /><br />　　（3）select gid,fariqi,neibuyonghu,title from Tgongwen where neibuyonghu='<br />办公室' <br /><br />　　查询速度：60280毫秒 <br /><br />　　从以上试验中，我们可以看到如果仅用聚集索引的起始列作为查询条件和同时用到复<br />合聚集索引的全部列的查询速度是几乎一样的，甚至比用上全部的复合索引列还要略快（<br />在查询结果集数目一样的情况下）；而如果仅用复合聚集索引的非起始列作为查询条件的<br />话，这个索引是不起任何作用的。当然，语句1、2的查询速度一样是因为查询的条目数一<br />样，如果复合索引的所有列都用上，而且查询结果少的话，这样就会形成“索引覆盖”，<br />因而性能可以达到最优。同时，请记住：无论您是否经常使用聚合索引的其他列，但其前<br />导列一定要是使用最频繁的列。 <br /><br />（四）其他书上没有的索引使用经验总结 <br /><br />　　1、用聚合索引比用不是聚合索引的主键速度快 <br /><br />　　下面是实例语句：（都是提取25万条数据） <br /><br />select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi='2004-9-<br />16' <br /><br />　　使用时间：3326毫秒 <br /><br />select gid,fariqi,neibuyonghu,reader,title from Tgongwen where gid&lt;=250000 <br /><br />　　使用时间：4470毫秒 <br /><br />　　这里，用聚合索引比用不是聚合索引的主键速度快了近1/4。 <br /><br />　　2、用聚合索引比用一般的主键作order by时速度快，特别是在小数据量情况下 <br /><br />select gid,fariqi,neibuyonghu,reader,title from Tgongwen order by fariqi <br /><br />　　用时：12936 <br /><br />select gid,fariqi,neibuyonghu,reader,title from Tgongwen order by gid <br /><br />　　用时：18843 <br /><br />　　这里，用聚合索引比用一般的主键作order by时，速度快了3/10。事实上，如果数据<br />量很小的话，用聚集索引作为排序列要比使用非聚集索引速度快得明显的多；而数据量如<br />果很大的话，如10万以上，则二者的速度差别不明显。 <br /><br />　　3、使用聚合索引内的时间段，搜索时间会按数据占整个数据表的百分比成比例减少，<br />而无论聚合索引使用了多少个 <br /><br />select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi&gt;'2004-1-<br />1' <br /><br />　　用时：6343毫秒（提取100万条） <br /><br />select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi&gt;'2004-6-<br />6' <br /><br />　　用时：3170毫秒（提取50万条） <br /><br />select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi='2004-9-<br />16' <br /><br />　　用时：3326毫秒（和上句的结果一模一样。如果采集的数量一样，那么用大于号和等<br />于号是一样的） <br /><br />select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi&gt;'2004-1-<br />1' and fariqi&lt;'2004-6-6' <br /><br />　　用时：3280毫秒 <br /><br />　　4 、日期列不会因为有分秒的输入而减慢查询速度 <br /><br />　　下面的例子中，共有100万条数据，2004年1月1日以后的数据有50万条，但只有两个不<br />同的日期，日期精确到日；之前有数据50万条，有5000个不同的日期，日期精确到秒。 <br /><br /><br />select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi&gt;'2004-1-<br />1' order by fariqi <br /><br />　　用时：6390毫秒 <br /><br />select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi&lt;'2004-1-<br />1' order by fariqi <br /><br />　　用时：6453毫秒 <br /><br />　　（五）其他注意事项 <br /><br />　　“水可载舟，亦可覆舟”，索引也一样。索引有助于提高检索性能，但过多或不当的<br />索引也会导致系统低效。因为用户在表中每加进一个索引，数据库就要做更多的工作。过<br />多的索引甚至会导致索引碎片。 <br /><br />　　所以说，我们要建立一个“适当”的索引体系，特别是对聚合索引的创建，更应精益<br />求精，以使您的数据库能得到高性能的发挥。 <br /><br />　　当然，在实践中，作为一个尽职的数据库管理员，您还要多测试一些方案，找出哪种<br />方案效率最高、最为有效。 <br /><br />二、改善SQL语句 <br /><br />　　很多人不知道SQL语句在SQL SERVER中是如何执行的，他们担心自己所写的SQL语句会<br />被SQL SERVER误解。比如： <br /><br />select * from table1 where name='zhangsan' and tID &gt; 10000 <br /><br />　　和执行: <br /><br />select * from table1 where tID &gt; 10000 and name='zhangsan' <br /><br />　　一些人不知道以上两条语句的执行效率是否一样，因为如果简单的从语句先后上看，<br />这两个语句的确是不一样，如果tID是一个聚合索引，那么后一句仅仅从表的10000条以后<br />的记录中查找就行了；而前一句则要先从全表中查找看有几个name='zhangsan'的，而后再<br />根据限制条件条件 tID&gt;10000来提出查询结果。 <br /><br />　　事实上，这样的担心是不必要的。SQL SERVER中有一个“查询分析优化器”，它可以<br />计算出where子句中的搜索条件并确定哪个索引能缩小表扫描的搜索空间，也就是说，它能<br />实现自动优化。 <br /><br />　　虽然查询优化器可以根据where子句自动的进行查询优化，但大家仍然有必要了解一下<br />“查询优化器”的工作原理，如非这样，有时查询优化器就会不按照您的本意进行快速查<br />询。 <br /><br />　　在查询分析阶段，查询优化器查看查询的每个阶段并决定限制需要扫描的数据量是否<br />有用。如果一个阶段可以被用作一个扫描参数（SARG），那么就称之为可优化的，并且可<br />以利用索引快速获得所需数据。 <br /><br />　　SARG的定义：用于限制搜索的一个操作，因为它通常是指一个特定的匹配，一个值得<br />范围内的匹配或者两个以上条件的AND连接。形式如下： <br /><br />列名 操作符 &lt;常数 或 变量&gt; <br /><br />或 <br /><br />&lt;常数 或 变量&gt; 操作符列名 <br /><br />　　列名可以出现在操作符的一边，而常数或变量出现在操作符的另一边。如： <br /><br />Name=’张三’ <br /><br />价格&gt;5000 <br /><br />5000&lt;价格 <br /><br />Name=’张三’ and 价格&gt;5000 <br /><br />　　如果一个表达式不能满足SARG的形式，那它就无法限制搜索的范围了，也就是SQL SE<br />RVER必须对每一行都判断它是否满足WHERE子句中的所有条件。所以一个索引对于不满足S<br />ARG形式的表达式来说是无用的。 <br /><br />　　介绍完SARG后，我们来总结一下使用SARG以及在实践中遇到的和某些资料上结论不同<br />的经验： <br /><br />　　1、Like语句是否属于SARG取决于所使用的通配符的类型 <br /><br />　　如：name like ‘张%’ ，这就属于SARG <br /><br />　　而：name like ‘%张’ ,就不属于SARG。 <br /><br />　　原因是通配符%在字符串的开通使得索引无法使用。 <br /><br />　　2、or 会引起全表扫描 <br /><br />Name=’张三’ and 价格&gt;5000 符号SARG，而：Name=’张三’ or 价格&gt;5000 则不符合S<br />ARG。使用or会引起全表扫描。 <br /><br />　　3、非操作符、函数引起的不满足SARG形式的语句 <br /><br />　　不满足SARG形式的语句最典型的情况就是包括非操作符的语句，如：NOT、!=、&lt;&gt;、!<br />&lt;、!&gt;、NOT EXISTS、NOT IN、NOT LIKE等，另外还有函数。下面就是几个不满足SARG形式<br />的例子： <br /><br />ABS(价格)&lt;5000 <br /><br />Name like ‘%三’ <br /><br />　　有些表达式，如： <br /><br />WHERE 价格*2&gt;5000 <br /><br />　　SQL SERVER也会认为是SARG，SQL SERVER会将此式转化为： <br /><br />WHERE 价格&gt;2500/2 <br /><br />　　但我们不推荐这样使用，因为有时SQL SERVER不能保证这种转化与原始表达式是完全<br />等价的。 <br /><br />　　4、IN 的作用相当与OR <br /><br />　　语句： <br /><br />Select * from table1 where tid in (2,3) <br /><br />　　和 <br /><br />Select * from table1 where tid=2 or tid=3 <br /><br />　　是一样的，都会引起全表扫描，如果tid上有索引，其索引也会失效。 <br /><br />　　5、尽量少用NOT <br /><br />　　6、exists 和 in 的执行效率是一样的 <br /><br />　　很多资料上都显示说，exists要比in的执行效率要高，同时应尽可能的用not exists<br />来代替not in。但事实上，我试验了一下，发现二者无论是前面带不带not，二者之间的执<br />行效率都是一样的。因为涉及子查询，我们试验这次用SQL SERVER自带的pubs数据库。运<br />行前我们可以把SQL SERVER的statistics I/O状态打开。 <br /><br />　　（1）select title,price from titles where title_id in (select title_id fro<br />m sales where qty&gt;30) <br /><br />　　该句的执行结果为： <br /><br />　　表 'sales'。扫描计数 18，逻辑读 56 次，物理读 0 次，预读 0 次。 <br /><br />　　表 'titles'。扫描计数 1，逻辑读 2 次，物理读 0 次，预读 0 次。 <br /><br />　　（2）select title,price from titles where exists (select * from sales wher<br />e sales.title_id=titles.title_id and qty&gt;30) <br /><br />　　第二句的执行结果为： <br /><br />　　表 'sales'。扫描计数 18，逻辑读 56 次，物理读 0 次，预读 0 次。 <br /><br />　　表 'titles'。扫描计数 1，逻辑读 2 次，物理读 0 次，预读 0 次。 <br /><br />　　我们从此可以看到用exists和用in的执行效率是一样的。 <br /><br />　　7、用函数charindex()和前面加通配符%的LIKE执行效率一样 <br /><br />　　前面，我们谈到，如果在LIKE前面加上通配符%，那么将会引起全表扫描，所以其执行<br />效率是低下的。但有的资料介绍说，用函数charindex()来代替LIKE速度会有大的提升，经<br />我试验，发现这种说明也是错误的： <br /><br />select gid,title,fariqi,reader from tgongwen where charindex('刑侦支队',reader<br />)&gt;0 and fariqi&gt;'2004-5-5' <br /><br />　　用时：7秒，另外：扫描计数 4，逻辑读 7155 次，物理读 0 次，预读 0 次。 <br /><br />select gid,title,fariqi,reader from tgongwen where reader like '%' + '刑侦支队<br />' + '%' and fariqi&gt;'2004-5-5' <br /><br />　　用时：7秒，另外：扫描计数 4，逻辑读 7155 次，物理读 0 次，预读 0 次。 <br /><br />　　8、union并不绝对比or的执行效率高 <br /><br />　　我们前面已经谈到了在where子句中使用or会引起全表扫描，一般的，我所见过的资料<br />都是推荐这里用union来代替or。事实证明，这种说法对于大部分都是适用的。 <br /><br />select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi='2004-9-<br />16' or gid&gt;9990000 <br /><br />　　用时：68秒。扫描计数 1，逻辑读 404008 次，物理读 283 次，预读 392163 次。 <br /><br /><br />select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi='2004-9-<br />16' <br /><br />union <br /><br />select gid,fariqi,neibuyonghu,reader,title from Tgongwen where gid&gt;9990000 <br /><br />　　用时：9秒。扫描计数 8，逻辑读 67489 次，物理读 216 次，预读 7499 次。 <br /><br />　　看来，用union在通常情况下比用or的效率要高的多。 <br /><br />　　但经过试验，笔者发现如果or两边的查询列是一样的话，那么用union则反倒和用or的<br />执行速度差很多，虽然这里union扫描的是索引，而or扫描的是全表。 <br /><br />select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi='2004-9-<br />16' or fariqi='2004-2-5' <br /><br />　　用时：6423毫秒。扫描计数 2，逻辑读 14726 次，物理读 1 次，预读 7176 次。 <br /><br /><br />select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi='2004-9-<br />16' <br /><br />union <br /><br />select gid,fariqi,neibuyonghu,reader,title from Tgongwen where  fariqi='2004-2<br />-5' <br /><br />　　用时：11640毫秒。扫描计数 8，逻辑读 14806 次，物理读 108 次，预读 1144 次。<br /><br /><br />　　9、字段提取要按照“需多少、提多少”的原则，避免“select *” <br /><br />　　我们来做一个试验： <br /><br />select top 10000 gid,fariqi,reader,title from tgongwen order by gid desc <br /><br />　　用时：4673毫秒 <br /><br />select top 10000 gid,fariqi,title from tgongwen order by gid desc <br /><br />　　用时：1376毫秒 <br /><br />select top 10000 gid,fariqi from tgongwen order by gid desc <br /><br />　　用时：80毫秒 <br /><br />　　由此看来，我们每少提取一个字段，数据的提取速度就会有相应的提升。提升的速度<br />还要看您舍弃的字段的大小来判断。 <br /><br />　　10、count(*)不比count(字段)慢 <br /><br />　　某些资料上说：用*会统计所有列，显然要比一个世界的列名效率低。这种说法其实是<br />没有根据的。我们来看： <br /><br />select count(*) from Tgongwen <br /><br />　　用时：1500毫秒 <br /><br />select count(gid) from Tgongwen <br /><br />　　用时：1483毫秒 <br /><br />select count(fariqi) from Tgongwen <br /><br />　　用时：3140毫秒 <br /><br />select count(title) from Tgongwen <br /><br />　　用时：52050毫秒 <br /><br />　　从以上可以看出，如果用count(*)和用count(主键)的速度是相当的，而count(*)却比<br />其他任何除主键以外的字段汇总速度要快，而且字段越长，汇总的速度就越慢。我想，如<br />果用count(*)， SQL SERVER可能会自动查找最小字段来汇总的。当然，如果您直接写cou<br />nt(主键)将会来的更直接些。 <br /><br />　　11、order by按聚集索引列排序效率最高 <br /><br />　　我们来看：（gid是主键，fariqi是聚合索引列） <br /><br />select top 10000 gid,fariqi,reader,title from tgongwen <br /><br />　　用时：196 毫秒。 扫描计数 1，逻辑读 289 次，物理读 1 次，预读 1527 次。 <br /><br />select top 10000 gid,fariqi,reader,title from tgongwen order by gid asc <br /><br />　　用时：4720毫秒。 扫描计数 1，逻辑读 41956 次，物理读 0 次，预读 1287 次。 <br /><br /><br />select top 10000 gid,fariqi,reader,title from tgongwen order by gid desc <br /><br />　　用时：4736毫秒。 扫描计数 1，逻辑读 55350 次，物理读 10 次，预读 775 次。 <br /><br /><br />select top 10000 gid,fariqi,reader,title from tgongwen order by fariqi asc <br /><br />　　用时：173毫秒。 扫描计数 1，逻辑读 290 次，物理读 0 次，预读 0 次。 <br /><br />select top 10000 gid,fariqi,reader,title from tgongwen order by fariqi desc <br /><br />　　用时：156毫秒。 扫描计数 1，逻辑读 289 次，物理读 0 次，预读 0 次。 <br /><br />　　从以上我们可以看出，不排序的速度以及逻辑读次数都是和“order by 聚集索引列”<br /> 的速度是相当的，但这些都比“order by 非聚集索引列”的查询速度是快得多的。 <br /><br />　　同时，按照某个字段进行排序的时候，无论是正序还是倒序，速度是基本相当的。 <br /><br /><br />　　12、高效的TOP <br /><br />　　事实上，在查询和提取超大容量的数据集时，影响数据库响应时间的最大因素不是数<br />据查找，而是物理的I/0操作。如： <br /><br />select top 10 * from ( <br /><br />select top 10000 gid,fariqi,title from tgongwen <br /><br />where neibuyonghu='办公室' <br /><br />order by gid desc) as a <br /><br />order by gid asc <br /><br />　　这条语句，从理论上讲，整条语句的执行时间应该比子句的执行时间长，但事实相反<br />。因为，子句执行后返回的是10000条记录，而整条语句仅返回 10条语句，所以影响数据<br />库响应时间最大的因素是物理I/O操作。而限制物理I/O操作此处的最有效方法之一就是使<br />用TOP关键词了。TOP关键词是 SQL SERVER中经过系统优化过的一个用来提取前几条或前几<br />个百分比数据的词。经笔者在实践中的应用，发现TOP确实很好用，效率也很高。但这个词<br />在另外一个大型数据库ORACLE中却没有，这不能说不是一个遗憾，虽然在ORACLE中可以用<br />其他方法（如：rownumber）来解决。在以后的关于“实现千万级数据的分页显示存储过程<br />”的讨论中，我们就将用到TOP这个关键词。 <br /><br />　　到此为止，我们上面讨论了如何实现从大容量的数据库中快速地查询出您所需要的数<br />据方法。当然，我们介绍的这些方法都是“软”方法，在实践中，我们还要考虑各种“硬<br />”因素，如：网络性能、服务器的性能、操作系统的性能，甚至网卡、交换机等。 <br /><br />三、实现小数据量和海量数据的通用分页显示存储过程 <br /><br />　　建立一个web 应用，分页浏览功能必不可少。这个问题是数据库处理中十分常见的问<br />题。经典的数据分页方法是:ADO 纪录集分页法，也就是利用ADO自带的分页功能（利用游<br />标）来实现分页。但这种分页方法仅适用于较小数据量的情形，因为游标本身有缺点：游<br />标是存放在内存中，很费内存。游标一建立，就将相关的记录锁住，直到取消游标。游标<br />提供了对特定集合中逐行扫描的手段，一般使用游标来逐行遍历数据，根据取出数据条件<br />的不同进行不同的操作。而对于多表和大表中定义的游标（大的数据集合）循环很容易使<br />程序进入一个漫长的等待甚至死机。 <br /><br />　　更重要的是，对于非常大的数据模型而言，分页检索时，如果按照传统的每次都加载<br />整个数据源的方法是非常浪费资源的。现在流行的分页方法一般是检索页面大小的块区的<br />数据，而非检索所有的数据，然后单步执行当前行。 <br /><br />　　最早较好地实现这种根据页面大小和页码来提取数据的方法大概就是“俄罗斯存储过<br />程”。这个存储过程用了游标，由于游标的局限性，所以这个方法并没有得到大家的普遍<br />认可。 <br /><br />　　后来，网上有人改造了此存储过程，下面的存储过程就是结合我们的办公自动化实例<br />写的分页存储过程： <br /><br />CREATE procedure pagination1 <br /><br />(@pagesize int,  --页面大小，如每页存储20条记录 <br /><br />@pageindex int   --当前页码 <br /><br />) <br /><br />as <br /><br />set nocount on <br /><br />begin <br /><br />declare @indextable table(id int identity(1,1),nid int)  --定义表变量 <br /><br />declare @PageLowerBound int  --定义此页的底码 <br /><br />declare @PageUpperBound int  --定义此页的顶码 <br /><br />set @PageLowerBound=(@pageindex-1)*@pagesize <br /><br />set @PageUpperBound=@PageLowerBound+@pagesize <br /><br />set rowcount @PageUpperBound <br /><br />insert into @indextable(nid) select gid from TGongwen where fariqi &gt;dateadd(da<br />y,-365,getdate()) order by fariqi desc <br /><br />select O.gid,O.mid,O.title,O.fadanwei,O.fariqi from TGongwen O,@indextable t w<br />here O.gid=t.nid <br /><br />and t.id&gt;@PageLowerBound and t.id&lt;=@PageUpperBound order by t.id <br /><br />end <br /><br />set nocount off <br /><br />　　以上存储过程运用了SQL SERVER的最新技术――表变量。应该说这个存储过程也是一<br />个非常优秀的分页存储过程。当然，在这个过程中，您也可以把其中的表变量写成临时表<br />： CREATE TABLE #Temp。但很明显，在SQL SERVER中，用临时表是没有用表变量快的。所<br />以笔者刚开始使用这个存储过程时，感觉非常的不错，速度也比原来的ADO的好。但后来，<br />我又发现了比此方法更好的方法。 <br /><br />　　笔者曾在网上看到了一篇小短文《从数据表中取出第n条到第m条的记录的方法》，全<br />文如下： <br /><br />从publish 表中取出第 n 条到第 m 条的记录： <br />SELECT TOP m-n+1 * <br />FROM publish <br />WHERE (id NOT IN <br />　　　　(SELECT TOP n-1 id <br />　　　　 FROM publish)) <br /><br />id 为publish 表的关键字 <br /><br />　　我当时看到这篇文章的时候，真的是精神为之一振，觉得思路非常得好。等到后来，<br />我在作办公自动化系统（ASP.NET+ C#＋SQL SERVER）的时候，忽然想起了这篇文章，我想<br />如果把这个语句改造一下，这就可能是一个非常好的分页存储过程。于是我就满网上找这<br />篇文章，没想到，文章还没找到，却找到了一篇根据此语句写的一个分页存储过程，这个<br />存储过程也是目前较为流行的一种分页存储过程，我很后悔没有争先把这段文字改造成存<br />储过程： <br /><br />CREATE PROCEDURE pagination2 <br />( <br />@SQL nVARCHAR(4000),    --不带排序语句的SQL语句 <br />@Page int,              --页码 <br />@RecsPerPage int,       --每页容纳的记录数 <br />@ID VARCHAR(255),       --需要排序的不重复的ID号 <br />@Sort VARCHAR(255)      --排序字段及规则 <br />) <br />AS <br /><br />DECLARE @Str nVARCHAR(4000) <br /><br />SET @Str='SELECT   TOP '+CAST(@RecsPerPage AS VARCHAR(20))+' * FROM ('+@SQL+')<br /> T WHERE T.'+@ID+'NOT IN <br />(SELECT   TOP '+CAST((@RecsPerPage*(@Page-1)) AS VARCHAR(20))+' '+@ID+' FROM (<br />'+@SQL+') T9 ORDER BY '+@Sort+') ORDER BY '+@Sort <br /><br />PRINT @Str <br /><br />EXEC sp_ExecuteSql @Str <br />GO <br /><br />　　其实，以上语句可以简化为： <br /><br />SELECT TOP 页大小 * <br /><br />FROM Table1 <br /><br />WHERE (ID NOT IN <br /><br />         (SELECT TOP 页大小*页数 id <br /><br />        FROM 表 <br /><br />        ORDER BY id)) <br /><br />ORDER BY ID <br /><br />　　但这个存储过程有一个致命的缺点，就是它含有NOT IN字样。虽然我可以把它改造为<br />： <br /><br />SELECT TOP 页大小 * <br /><br />FROM Table1 <br /><br />WHERE not exists <br /><br />(select * from (select top (页大小*页数) * from table1 order by id) b where b.<br />id=a.id ) <br /><br />order by id <br /><br />　　即，用not exists来代替not in，但我们前面已经谈过了，二者的执行效率实际上是<br />没有区别的。 <br /><br />　　既便如此，用TOP 结合NOT IN的这个方法还是比用游标要来得快一些。 <br /><br />　　虽然用not exists并不能挽救上个存储过程的效率，但使用SQL SERVER中的TOP关键字<br />却是一个非常明智的选择。因为分页优化的最终目的就是避免产生过大的记录集，而我们<br />在前面也已经提到了TOP的优势，通过TOP 即可实现对数据量的控制。 <br /><br />　　在分页算法中，影响我们查询速度的关键因素有两点：TOP和NOT IN。TOP可以提高我<br />们的查询速度，而NOT IN会减慢我们的查询速度，所以要提高我们整个分页算法的速度，<br />就要彻底改造NOT IN，同其他方法来替代它。 <br /><br />　　我们知道，几乎任何字段，我们都可以通过max(字段)或min(字段)来提取某个字段中<br />的最大或最小值，所以如果这个字段不重复，那么就可以利用这些不重复的字段的max或m<br />in作为分水岭，使其成为分页算法中分开每页的参照物。在这里，我们可以用操作符“&gt;”<br />或“&lt;”号来完成这个使命，使查询语句符合SARG形式。如： <br /><br />Select top 10 * from table1 where id&gt;200 <br /><br />　　于是就有了如下分页方案： <br /><br />select top 页大小 * <br /><br />from table1 <br /><br />where id&gt; <br /><br />     (select max (id) from <br /><br />     (select top ((页码-1)*页大小) id from table1 order by id) as T <br /><br />      )     <br /><br /> order by id <br /><br />　　在选择即不重复值，又容易分辨大小的列时，我们通常会选择主键。下表列出了笔者<br />用有着1000万数据的办公自动化系统中的表，在以GID （GID是主键，但并不是聚集索引。<br />）为排序列、提取gid,fariqi,title字段，分别以第1、10、100、500、1000、1万、10 万<br />、25万、50万页为例，测试以上三种分页方案的执行速度：（单位：毫秒） <br /><br />页  码 <br />方案1 <br />方案2 <br />方案3 <br /><br />1 <br />60 <br />30 <br />76 <br /><br />10 <br />46 <br />16 <br />63 <br /><br />100 <br />1076 <br />720 <br />130 <br /><br />500 <br />540 <br />12943 <br />83 <br /><br />1000 <br />17110 <br />470 <br />250 <br /><br />1万 <br />24796 <br />4500 <br />140 <br /><br />10万 <br />38326 <br />42283 <br />1553 <br /><br />25万 <br />28140 <br />128720 <br />2330 <br /><br />50万 <br />121686 <br />127846 <br />7168 <br /><br /><br />　　从上表中，我们可以看出，三种存储过程在执行100页以下的分页命令时，都是可以信<br />任的，速度都很好。但第一种方案在执行分页1000页以上后，速度就降了下来。第二种方<br />案大约是在执行分页1万页以上后速度开始降了下来。而第三种方案却始终没有大的降势，<br />后劲仍然很足。 <br /><br />　　在确定了第三种分页方案后，我们可以据此写一个存储过程。大家知道SQL SERVER的<br />存储过程是事先编译好的SQL语句，它的执行效率要比通过WEB页面传来的SQL语句的执行效<br />率要高。下面的存储过程不仅含有分页方案，还会根据页面传来的参数来确定是否进行数<br />据总数统计。 <br /><br />-- 获取指定页的数据 <br /><br />CREATE PROCEDURE pagination3 <br /><br />@tblName   varchar(255),       -- 表名 <br /><br />@strGetFields varchar(1000) = '*',  -- 需要返回的列 <br /><br />@fldName varchar(255)='',      -- 排序的字段名 <br /><br />@PageSize   int = 10,          -- 页尺寸 <br /><br />@PageIndex  int = 1,           -- 页码 <br /><br />@doCount  bit = 0,   -- 返回记录总数, 非 0 值则返回 <br /><br />@OrderType bit = 0,  -- 设置排序类型, 非 0 值则降序 <br /><br />@strWhere  varchar(1500) = ''  -- 查询条件 (注意: 不要加 where) <br /><br />AS <br /><br />declare @strSQL   varchar(5000)       -- 主语句 <br /><br />declare @strTmp   varchar(110)        -- 临时变量 <br /><br />declare @strOrder varchar(400)        -- 排序类型 <br /><br /><br /><br />if @doCount != 0 <br /><br /> begin <br /><br />   if @strWhere !='' <br /><br />   set @strSQL = "select count(*) as Total from [" + @tblName + "] where "+@st<br />rWhere <br /><br />   else <br /><br />   set @strSQL = "select count(*) as Total from [" + @tblName + "]" <br /><br />end <br /><br />--以上代码的意思是如果@doCount传递过来的不是0，就执行总数统计。以下的所有代码都<br />是@doCount为0的情况 <br /><br />else <br /><br />begin <br /><br /><br /><br />if @OrderType != 0 <br /><br />begin <br /><br />   set @strTmp = "&lt;(select min" <br /><br />set @strOrder = " order by [" + @fldName +"] desc" <br /><br />--如果@OrderType不是0，就执行降序，这句很重要！ <br /><br />end <br /><br />else <br /><br />begin <br /><br />   set @strTmp = "&gt;(select max" <br /><br />   set @strOrder = " order by [" + @fldName +"] asc" <br /><br />end <br /><br /><br /><br />if @PageIndex = 1 <br /><br />begin <br /><br />   if @strWhere != ''   <br /><br />   set @strSQL = "select top " + str(@PageSize) +" "+@strGetFields+ "  from ["<br /> + @tblName + "] where " + @strWhere + " " + @strOrder <br /><br />    else <br /><br />    set @strSQL = "select top " + str(@PageSize) +" "+@strGetFields+ "  from [<br />"+ @tblName + "] "+ @strOrder <br /><br />--如果是第一页就执行以上代码，这样会加快执行速度 <br /><br />end <br /><br />else <br /><br />begin <br /><br />--以下代码赋予了@strSQL以真正执行的SQL代码 <br /><br />set @strSQL = "select top " + str(@PageSize) +" "+@strGetFields+ "  from [" <br /><br />   + @tblName + "] where [" + @fldName + "]" + @strTmp + "(["+ @fldName + "]) <br />from (select top " + str((@PageIndex-1)*@PageSize) + " ["+ @fldName + "] from <br />[" + @tblName + "]" + @strOrder + ") as tblTmp)"+ @strOrder <br /><br /><br /><br />if @strWhere != '' <br /><br />   set @strSQL = "select top " + str(@PageSize) +" "+@strGetFields+ "  from ["<br /><br /><br />       + @tblName + "] where [" + @fldName + "]" + @strTmp + "([" <br /><br />       + @fldName + "]) from (select top " + str((@PageIndex-1)*@PageSize) + "<br /> [" <br /><br />       + @fldName + "] from [" + @tblName + "] where " + @strWhere + " " <br /><br />       + @strOrder + ") as tblTmp) and " + @strWhere + " " + @strOrder <br /><br />end <br /><br />end   <br /><br />exec (@strSQL) <br /><br />GO <br /><br />　　上面的这个存储过程是一个通用的存储过程，其注释已写在其中了。 <br /><br />　　在大数据量的情况下，特别是在查询最后几页的时候，查询时间一般不会超过9秒；而<br />用其他存储过程，在实践中就会导致超时，所以这个存储过程非常适用于大容量数据库的<br />查询。 <br /><br />　　笔者希望能够通过对以上存储过程的解析，能给大家带来一定的启示，并给工作带来<br />一定的效率提升，同时希望同行提出更优秀的实时数据分页算法。 <br /><br />四、聚集索引的重要性和如何选择聚集索引 <br /><br />　　在上一节的标题中，笔者写的是：实现小数据量和海量数据的通用分页显示存储过程<br />。这是因为在将本存储过程应用于“办公自动化”系统的实践中时，笔者发现这第三种存<br />储过程在小数据量的情况下，有如下现象： <br /><br />　　1、分页速度一般维持在1秒和3秒之间。 <br /><br />　　2、在查询最后一页时，速度一般为5秒至8秒，哪怕分页总数只有3页或30万页。 <br /><br />　　虽然在超大容量情况下，这个分页的实现过程是很快的，但在分前几页时，这个1－3<br />秒的速度比起第一种甚至没有经过优化的分页方法速度还要慢，借用户的话说就是“还没<br />有ACCESS数据库速度快”，这个认识足以导致用户放弃使用您开发的系统。 <br /><br />　　笔者就此分析了一下，原来产生这种现象的症结是如此的简单，但又如此的重要：排<br />序的字段不是聚集索引！ <br /><br />　　本篇文章的题目是：“查询优化及分页算法方案”。笔者只所以把“查询优化”和“<br />分页算法”这两个联系不是很大的论题放在一起，就是因为二者都需要一个非常重要的东<br />西――聚集索引。 <br /><br />　　在前面的讨论中我们已经提到了，聚集索引有两个最大的优势： <br /><br />　　1、以最快的速度缩小查询范围。 <br /><br />　　2、以最快的速度进行字段排序。 <br /><br />　　第1条多用在查询优化时，而第2条多用在进行分页时的数据排序。 <br /><br />　　而聚集索引在每个表内又只能建立一个，这使得聚集索引显得更加的重要。聚集索引<br />的挑选可以说是实现“查询优化”和“高效分页”的最关键因素。 <br /><br />　　但要既使聚集索引列既符合查询列的需要，又符合排序列的需要，这通常是一个矛盾<br />。 <br /><br />　　笔者前面“索引”的讨论中，将fariqi，即用户发文日期作为了聚集索引的起始列，<br />日期的精确度为“日”。这种作法的优点，前面已经提到了，在进行划时间段的快速查询<br />中，比用ID主键列有很大的优势。 <br /><br />　　但在分页时，由于这个聚集索引列存在着重复记录，所以无法使用max或min来最为分<br />页的参照物，进而无法实现更为高效的排序。而如果将ID主键列作为聚集索引，那么聚集<br />索引除了用以排序之外，没有任何用处，实际上是浪费了聚集索引这个宝贵的资源。 <br /><br />　　　为解决这个矛盾，笔者后来又添加了一个日期列，其默认值为getdate()。用户在写<br />入记录时，这个列自动写入当时的时间，时间精确到毫秒。即使这样，为了避免可能性很<br />小的重合，还要在此列上创建UNIQUE约束。将此日期列作为聚集索引列。 <br /><br />　　有了这个时间型聚集索引列之后，用户就既可以用这个列查找用户在插入数据时的某<br />个时间段的查询，又可以作为唯一列来实现max或min，成为分页算法的参照物。 <br /><br />　　经过这样的优化，笔者发现，无论是大数据量的情况下还是小数据量的情况下，分页<br />速度一般都是几十毫秒，甚至0毫秒。而用日期段缩小范围的查询速度比原来也没有任何迟<br />钝。 <br /><br />　　聚集索引是如此的重要和珍贵，所以笔者总结了一下，一定要将聚集索引建立在： <br /><br /><br />　　1、您最频繁使用的、用以缩小查询范围的字段上； <br /><br />　　2、您最频繁使用的、需要排序的字段上。 <br /><br />　　结束语： <br /><br />　　希望这篇文章不仅能够给大家的工作带来一定的帮助，也希望能让大家能够体会到分<br />析问题的方法；最重要的是，希望这篇文章能够抛砖引玉，掀起大家的学习和讨论的兴趣<br />，以共同促进。 <br />　　最后需要说明的是，在试验中，发现用户在进行大数据量查询的时候，对数据库速度<br />影响最大的不是内存大小，而是CPU。在我的P4 2.4机器上试验的时候，查看“资源管理器<br />”，CPU经常出现持续到100%的现象，而内存用量却并没有改变或者说没有大的改变。即使<br />在我们的HP ML 350 G3服务器上试验时，CPU峰值也能达到90%，一般持续在70%左右。 <br /><br />　　本文的试验数据都是来自我们的HP ML 350服务器。服务器配置：双Inter Xeon 超线<br />程 CPU 2.4G，内存1G，操作系统Windows Server 2003 Enterprise Edition，数据库SQL<br /> Server 2000 SP3。 <br /><br />转载完毕. <br /><br /><br />作者补充: <br />1.columns in('aa','bb') <br />他等于columns = 'aa' or columns ='bb' 他先去查询columns ='aa'放在一个临时的空间<br />里,然后等columns ='bb'查询完后,做个or查询得出结果. <br />至于效率的话，在columns建立索引的话, columns ='aa' or columns ='bb'要来的效率高<br /><br />语法分析器会将columns in('aa','bb')转化 <br />为columns ='aa' or columns ='bb'来执行。我们期望它会根据每个or子句分别查找，再<br />将结果 <br />相加，这样可以利用columns 上的索引；但实际上（根据showplan）,它却采用了"OR策略<br />" <br />，即先取出满足每个or子句的行，存入临时数据库的工作表中，再建立唯一索引以去掉 <br /><br />重复行，最后从这个临时表中计算结果。因此，实际过程没有利用columns 上索引，并且<br />完 <br />成时间还要受tempdb数据库性能的影响。 <br /><br /><br />2.效率从高到低 count(1)&gt;count(*)&gt;count([id]) <br /><br />3.select max(cols) from table1 的效率&gt;= select top 1 cols from table1 order by<br /> cols desc <br /><br />4.在where 做并列条件句时,where cols1='aa' and cols2='bb' <br />如果cols1 ='aa' 占95% cols2占5%的话，把cols2='bb'放在前面 ,因为他在检索cols ='<br />bb'的时候他只需查那5%,然后条件成立的话，去在这5%的纪录里 <br />去查找cols1 ='aa' <br /><br />5.避免用if条件句,可以用or来替代. <br />declare @vsql varchar(200) <br />set @vsql ='Renaski' <br />select * from titles where  @vsql ='Renaski' or price = 11.9500 <br /><br />如果@vsql为Renaski则把所有的纪录都选出来,如果不是的话，则只查询price = 11.9500<br /> 的纪录. <br /><br />6.任何对列的操作都将导致表扫描，它包括数据库函数、计算表达式等等，查询时 <br />要尽可能将操作移至等号右边。 <br /><br />7.尽量避免使用游标. <br />如果使用了游标，就要尽量避免在游标循环中再进行表连接的操作 <br /><br />8.取一个表的纪录数 <br />Select rows from sysindexes where id=object_id(N'titles') and indid&lt;2 <br />效率比 <br />select count(1) from titles来的高. <br /><br /><br />9.取的一个表的数据信息. <br />SELECT <br />表名=case when a.colorder=1 then d.name else '' end, <br />表说明=case when a.colorder=1 then isnull(f.value,'') else '' end, <br />字段序号=a.colorder, <br />字段名=a.name, <br />标识=case when COLUMNPROPERTY( a.id,a.name,'IsIdentity')=1 then '√'else '' en<br />d, <br />主键=case when exists(SELECT 1 FROM sysobjects where xtype='PK' and name in ( <br /><br />SELECT name FROM sysindexes WHERE indid in( <br />SELECT indid FROM sysindexkeys WHERE id = a.id AND colid=a.colid <br />))) then '√' else '' end, <br />类型=b.name, <br />占用字节数=a.length, <br />长度=COLUMNPROPERTY(a.id,a.name,'PRECISION'), <br />小数位数=isnull(COLUMNPROPERTY(a.id,a.name,'Scale'),0), <br />允许空=case when a.isnullable=1 then '√'else '' end, <br />默认值=isnull(e.text,''), <br />字段说明=isnull(g.[value],''), <br />索引名称=isnull(h.索引名称,''), <br />索引顺序=isnull(h.排序,'') <br />FROM syscolumns a <br />left join systypes b on a.xtype=b.xusertype <br />inner join sysobjects d on a.id=d.id and d.xtype='U' and d.status&gt;=0 <br />left join syscomments e on a.cdefault=e.id <br />left join sysproperties g on a.id=g.id and a.colid=g.smallid <br />left join sysproperties f on d.id=f.id and f.smallid=0 <br />left join(--这部分是索引信息,如果要显示索引与表及字段的对应关系,可以只要此部分<br /><br />select 索引名称=a.name,c.id,d.colid <br />,排序=case indexkey_property(c.id,b.indid,b.keyno,'isdescending') <br />when 1 then '降序' when 0 then '升序' end <br />from sysindexes a <br />join sysindexkeys b on a.id=b.id and a.indid=b.indid <br />join (--这里的作用是有多个索引时,取索引号最小的那个 <br />select id,colid,indid=min(indid) from sysindexkeys <br />group by id,colid) b1 on b.id=b1.id and b.colid=b1.colid and b.indid=b1.indid <br /><br />join sysobjects c on b.id=c.id and c.xtype='U' and c.status&gt;=0 <br />join syscolumns d on b.id=d.id and b.colid=d.colid <br />where a.indid not in(0,255) <br />) h on a.id=h.id and a.colid=h.colid <br />--where d.name='要查询的表' --如果只查询指定表,加上此条件 <br />order by a.id,a.colorder <br /><br /><br />10.创建一个表结构. <br />select * into #b from authors where 1=2; <br />注意: <br />#table1 <br />##table1 <br />@table1 <br /><br />局部临时表 <br />以一个井号（#）开头的那些表名。只有在创建本地临时表的连接上才能看到这些表。 <br /><br />全局临时表 <br />以两个井号（##）开头的那些表名。在所有连接上都能看到全局临时表。如果在创建全局<br />临时表的连接断开前没有显式地除去这些表，那么只要所有其它任务停止引用它们，这些<br />表即被除去。当创建全局临时表的连接断开后，新的任务不能再引用它们。当前的语句一<br />执行完，任务与表之间的关联即被除去；因此通常情况下，只要创建全局临时表的连接断<br />开，全局临时表即被除去。 <br /><br />@和#有和不同：@@在内存，#在硬盘。我的体会是只要方便且数据量不大，使用@@。 <br /><br />11.视图 <br />他只是记住要连接,关联列的信息,他不存放任何物理数据. <br />在调用的时候他还是去取各个表中的数据. <br /><br />12.尽量不要用text属性 <br />系统为他专门开辟一个空间来存放. <br />用t-sql/varchar替代 <br />pl/sql  varchar2 替代. <br /><br />13 <br />GO语句是个命令识别并通过osql和isql和SQL 查询分析器非T-SQL语句进行识别。 <br />如果你使用查询分析器作为你的主开发工具，其他语句和库文件将不会识别GO语句作为一<br />个T-SQL命令 <br /><br />14. <br />用exec 效率来的高. <br />declare @sql nvarchar(300) <br />  set @sql='select * from titles' <br />execute sp_executesql @sql <br /><br />15,注意你的tempdb,使他自动增长. <br /><br />16 使用no_log <br />select * from titles no_logs <br /><br />17去不重复纪录时,尽量用dictinct <br /><br />18.尽量避免反复访问同一张或几张表，尤其是数据量较大的表，可以考虑先根据条件提取<br />数据到临时表中，然后再做连接。 <br /><br /><br />19 尽量使用“&gt;=”，不要使用“&gt;”。 他会找到某个确定的数字进行筛选,而&gt;则没有. <br /><br /><br />20注意表之间连接的数据类型，避免不同类型数据之间的连接。 <br /><br />21.可用ASE调优命令：set statistics io on, set statistics time on , set showpla<br />n on 等,进行优化 <br /><br />22.truncate table 删除数据 <br />而不是delete from table <br /><br /><br />三.死锁 <br /><br />像SQL server一样的关系数据库使用锁来防止用户“互相踩到对方的脚趾头”。也就是说<br />，锁可以防止用户造成修改数据时的碰撞。当一个用户锁住一段代码时候，其它的用户都<br />不能修改这段数据。另外，一个锁阻止了用户观看未被授权的数据修改。用户必须等待到<br />数据修改并保存之后才能够查看它。数据必须使用不同的方法来加锁。SQL Server 2000使<br />用锁来实现多用户同时修改数据库同一数据时的同步控制 <br />如果数据量超过200个数据页面（400k），那么系统将会进行锁升级，页级锁会升级成表级<br />锁。 　　 <br /><br /><br />死锁 <br />一个数据库的死锁是发生在两个或多于两个访问一些资源的数据库会话中的，并且这些会<br />话相互之间有依赖关系。死锁是可以在任意一个多线程的系统成出现的一个情况，不仅仅<br />局限于关系数据库管理系统。一个多线程系统中的线程可能需要一个或多个资源(例如，锁<br />)。如果申请的资源正在被另外一个线程所使用，那么第一个线程就需要等待持有该资源的<br />线程的释放它所需要的资源。假设等待线程持有一个那个正拥有线程所依赖的资源。下面<br />的这一段代码就可以造成死锁异常现象的发生： <br />System.Data.SqlClient.SqlException: Transaction (Process ID 12) was deadlocked<br /> on lock resources with another process and has been chosen as the deadlock vi<br />ctim. Rerun the transaction. <br /><br />当一个SQL Server的调用和另外一个资源发生冲突时就会抛出异常，这个资源持有一个必<br />要的资源。结果是，一个进程就被终止了。当进程的ID号成为系统的唯一标识的时候，这<br />会是一个很平常死锁的消息错误。 <br /><br /><br />锁的类型 <br />一个数据库系统在许多情况下都有可能锁数据项。其可能性包括： <br /><br />Rows—数据库表中的一整行 <br />Pages—行的集合（通常为几kb） <br />Extents—通常是几个页的集合 <br />Table—整个数据库表 <br />Database—被锁的整个数据库表 <br /><br />除非有其它的说明，数据库根据情况自己选择最好的锁方式。不过值得感谢的是，SQL Se<br />rver提供了一种避免默认行为的方法。这是由锁提示来完成的。 <br /><br /><br />提示 <br />或许你有过许多如下的经历：需要重设SQL Server的锁计划，并且加强数据库表中锁范围<br />。Tansact－SQL提供了一系列不同级别的锁提示，你可以在SELECT,INSERT, UPDATE和DEL<br />ETE中使用它们来告诉SQL Server你需要如何通过重设任何的系统或事务级别来锁表格。可<br />以实现的提示包括： <br /><br />FASTFIRSTROW—选取结果集中的第一行，并将其优化 <br />HOLDLOCK—持有一个共享锁直至事务完成 <br />NOLOCK—不允许使用共享锁或独享锁。这可能会造成数据重写或者没有被确认就返回的情<br />况；因此，就有可能使用到脏数据。这个提示只能在SELECT中使用。 <br />PAGLOCK—锁表格 <br />READCOMMITTED—只读取被事务确认的数据。这就是SQL Server的默认行为。 <br />READPAST—跳过被其它进程锁住的行，所以返回的数据可能会忽略行的内容。这也只能在<br />SELECT中使用。 <br />READUNCOMMITTED—等价于NOLOCK. <br />REPEATABLEREAD—在查询语句中，对所有数据使用锁。这可以防止其它的用户更新数据，<br />但是新的行可能被其它的用户插入到数据中，并且被最新访问该数据的用户读取。 <br />ROWLOCK—按照行的级别来对数据上锁。SQL Server通常锁到页或者表级别来修改行，所以<br />当开发者使用单行的时候，通常要重设这个设置。 <br />SERIALIZABLE—等价于HOLDLOCK. <br />TABLOCK—按照表级别上锁。在运行多个有关表级别数据操作的时候，你可能需要使用到这<br />个提示。 <br />UPDLOCK—当读取一个表的时候，使用更新锁来代替共享锁，并且保持一直拥有这个锁直至<br />事务结束。它的好处是，可以允许你在阅读数据的时候可以不需要锁，并且以最快的速度<br />更新数据。 <br />XLOCK—给所有的资源都上独享锁，直至事务结束。 <br /><br />对于数据库死锁，通常可以通过TRACE FLAG 1204、1205、1206，检查ERRORLOG里面的输出<br />，和分析SQLTRACE的执行上下文判断死锁问题的来由。 <br />TRACEON函数的第三个参数设置为-1，表示不单单针对当前connection，而是针对所有包括<br />未来建立的connection。这样，才够完全，否则只是监视当前已经建立的数据库连接了。<br /><br /><br /><br />执行下面的话可以把死锁记录到Errorlog中： <br /><br />dbcc traceon (1204, 3605, -1) <br />go <br />dbcc tracestatus(-1) <br />go <br /><br />  <br /><br />得到的输出为： <br />DBCC 执行完毕。如果 DBCC 输出了错误信息，请与系统管理员联系。 <br />TraceFlag Status <br />--------- ------ <br />1204      1 <br />1205      1 <br />3605      1 <br /><br />（所影响的行数为 3 行） <br /><br />DBCC 执行完毕。如果 DBCC 输出了错误信息，请与系统管理员联系。 <br /><br /><br />此后，你可以查看数据库的例行日志，每隔一段时间，数据库都会检查死锁 <br />2004-01-16 18:34:38.50 spid4     ---------------------------------- <br />2004-01-16 18:34:38.50 spid4     Starting deadlock search 1976 <br /><br /><br /><br />2004-01-16 18:34:38.50 spid4     Target Resource Owner: <br />2004-01-16 18:34:38.50 spid4      ResType:LockOwner Stype:'OR' Mode: U SPID:55<br /> ECID:0 Ec:(0xAA577570) Value:0x4c25cba0 <br />2004-01-16 18:34:38.50 spid4      Node:1  ResType:LockOwner Stype:'OR' Mode: U<br /> SPID:55 ECID:0 Ec:(0xAA577570) Value:0x4c25cba0 <br />2004-01-16 18:34:38.50 spid4      Node:2  ResType:LockOwner Stype:'OR' Mode: U<br /> SPID:71 ECID:0 Ec:(0xABF07570) Value:0x9bd0ba00 <br />2004-01-16 18:34:38.50 spid4     <br />2004-01-16 18:34:38.50 spid4     -- next branch -- <br />2004-01-16 18:34:38.50 spid4      Node:2  ResType:LockOwner Stype:'OR' Mode: U<br /> SPID:71 ECID:0 Ec:(0xABF07570) Value:0x9bd0ba00 <br />2004-01-16 18:34:38.50 spid4     <br />2004-01-16 18:34:38.50 spid4     <br />2004-01-16 18:34:38.50 spid4     End deadlock search 1976 ... a deadlock was n<br />ot found. <br />2004-01-16 18:34:38.50 spid4     ---------------------------------- <br /><br />DBCC TRACEON打开（启用）指定的跟踪标记。 <br /><br />注释跟踪标记用于自定义某些控制 Microsoft? SQL Server? 操作方式的特性。跟踪标记<br />在服务器中一直保持启用状态，直到通过执行 DBCC TRACEOFF 语句对其禁用为止。在发出<br /> DBCC TRACEON 语句之前，连入到服务器的新连接看不到任何跟踪标记。一旦发出该语句<br />，该连接就能看到服务器中当前启用的所有跟踪标记（即使这些标记是由其它连接启用）<br />。 <br />跟踪标记跟踪标记用于临时设置服务器的特定特征或关闭特定行为。如果启动 Microsoft<br />? SQL Server 时设置了跟踪标记 3205，将禁用磁带驱动程序的硬件压缩。跟踪标记经常<br />用于诊断性能问题，或调试存储过程或复杂的计算机系统。 <br />下列跟踪标记在 SQL Server 中可用。跟踪标记 描述 1204 返回参与死锁的锁的类型以及<br />当前受影响的命令。 <br />实际上可以在“错误 1000 -1999”中找到他们： <br />1204 19 SQL Server 此时无法获取 LOCK 资源。请在活动用户数较少时重新运行您的语句<br />，或者请求系统管理员检查 SQL Server 锁和内存配置。 <br />1205 13 事务（进程 ID %1!）与另一个进程已被死锁在资源 {%2!} 上，且该事务已被选<br />作死锁牺牲品。请重新运行该事务。 <br />1206 18 事务管理器已取消了分布式事务。 <br /><br />需要指出的是对锁的升级,完全是由系统自行判断的,而非人为.如果要避免死锁的话，其根<br />本还在与数据库的设计上<br /></p>
<img src ="http://www.blogjava.net/weibogao/aggbug/93392.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/weibogao/" target="_blank">weibogao</a> 2007-01-12 12:56 <a href="http://www.blogjava.net/weibogao/archive/2007/01/12/93392.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>转《数据库设计经验》</title><link>http://www.blogjava.net/weibogao/archive/2007/01/11/93125.html</link><dc:creator>weibogao</dc:creator><author>weibogao</author><pubDate>Thu, 11 Jan 2007 04:09:00 GMT</pubDate><guid>http://www.blogjava.net/weibogao/archive/2007/01/11/93125.html</guid><wfw:comment>http://www.blogjava.net/weibogao/comments/93125.html</wfw:comment><comments>http://www.blogjava.net/weibogao/archive/2007/01/11/93125.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/weibogao/comments/commentRss/93125.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/weibogao/services/trackbacks/93125.html</trackback:ping><description><![CDATA[
		<h3 class="title"> </h3>
		<p>2004-11-24微软技术博客 </p>
		<p>作者： wjk.net(转载)</p>
		<p>一个成功的管理系统，是由：[50% 的业务 + 50% 的软件] 所组成，而 50% 的成功软件又有 [25% 的数据库 + 25%的程序] 所组成，数据库设计的好坏是一个关键。如果把企业的数据比做生命 所必需的血液，那么数据库的设计就是应用中最重要的一部分 。有关数据库设计的材料汗牛充栋，大学学位课程里也有专门的讲述 。不过，就如我们反复强调的那样，再好的老师也比不过经验的教诲 。所以我归纳历年来所走的弯路及体会，并在网上找了些对数据库设 计颇有造诣的专业人士给大家传授一些设计数据库的技巧和经验 。精选了其中的60 个最佳技巧，并把这些技巧编写成了本文，为了方便索引其内容划分 为 5 个部分：</p>
		<p>
				<br />
		</p>
		<p>第 1 部分 - 设计数据库之前 </p>
		<p>这一部分罗列了 12 个基本技巧，包括命名规范和明确业务需求等。 </p>
		<p>第 2 部分 - 设计数据库表 </p>
		<p>总共 24 个指南性技巧，涵盖表内字段设计以及应该避免的常见问题等。 </p>
		<p>第 3 部分 - 选择键 </p>
		<p>怎么选择键呢？这里有 10 个技巧专门涉及系统生成的主键的正确用法，还有何 时以及如何索引字段以获得最佳性能等。 </p>
		<p>第 4 部分 - 保证数据完整性 </p>
		<p>讨论如何保持数据库的清晰和健壮，如何把有害数据降低到最小程度 。 </p>
		<p>第 5 部分 - 各种小技巧 </p>
		<p>不包括在以上 4 个部分中的其他技巧，五花八门，有了它们希望你的数据库开发工作 会更轻松一些。 </p>
		<p>
				<br />第 1 部分 - 设计数据库之前 </p>
		<p>
				<br />考察现有环境 </p>
		<p>在设计一个新数据库时，你不但应该仔细研究业务需求而且还要考察 现有的系统。大多数数据库项目都不是从头开始建立的；通常 ，机构内总会存在用来满足特定需求的现有系统（可能没有实现自动 计算）。显然，现有系统并不完美，否则你就不必再建立新系统了 。但是对旧系统的研究可以让你发现一些可能会忽略的细微问题 。一般来说，考察现有系统对你绝对有好处。 </p>
		<p>定义标准的对象命名规范 </p>
		<p>一定要定义数据库对象的命名规范。对数据库表来说 ，从项目一开始就要确定表名是采用复数还是单数形式 。此外还要给表的别名定义简单规则（比方说，如果表名是一个单词 ，别名就取单词的前4 个字母；如果表名是两个单词，就各取两个单词的前两个字母组成 4 个字母长的别名；如果表的名字由 3个单词组成，你不妨从头两个单词中各取一个然后从最后一个单词中 再取出两个字母，结果还是组成 4字母长的别名，其余依次类推）对工作用表来说，表名可以加上前缀 WORK_后面附上采用该表的应用程序的名字。表内的列[字段 ]要针对键采用一整套设计规则。比如，如果键是数字类型 ，你可以用 _N作为后缀；如果是字符类型则可以采用 _C后缀。对列[字段]名应该采用标准的前缀和后缀。再如 ，假如你的表里有好多"money"字段，你不妨给每个列[字段 ]增加一个 _M后缀。还有，日期列[字段]最好以 D_ 作为名字打头。</p>
		<p>检查表名、报表名和查询名之间的命名规范。你可能会很快就被这些 不同的数据库要素的名称搞糊涂了。假如你坚持统一地命名这些数据 库的不同组成部分，至少你应该在这些对象名字的开头用Table、Query 或者 Report 等前缀加以区别。</p>
		<p>如果采用了 Microsoft Access，你可以用 qry、rpt、tbl 和 mod 等符号来标识对象（比如tbl_Employees）。我在和 SQL Server 打交道的时候还用过 tbl 来索引表，但我用 sp_company （现在用 sp_feft_）标识存储过程，因为在有的时候如果我发现了更 好的处理办法往往会保存好几个拷贝。我在实现 SQL Server 2000 时用udf_ （或者类似的标记）标识我编写的函数。 </p>
		<p>
				<br />工欲善其事, 必先利其器 </p>
		<p>采用理想的数据库设计工具，比如：SyBase 公司的 PowerDesign，她支持 PB、VB、Delphe 等语言，通过 ODBC可以连接市面上流行的 30 多个数据库，包括 dBase、FoxPro、VFP、SQL Server 等，今后有机会我将着重介绍PowerDesign 的使用。 </p>
		<p>获取数据模式资源手册 </p>
		<p>正在寻求示例模式的人可以阅读《数据模式资源手册》一书，该书由 Len Silverston、W. H. Inmon 和 Kent Graziano 编写，是一本值得拥有的最佳数据建模图书。该书包括的章节涵盖多 种数据领域，比如人员、机构和工作效能等。其他的你还可以参考： [1]萨师煊王珊著数据库系统概论(第二版)高等教育出版社 1991、[2][美] Steven M.Bobrowski 著 Oracle 7<br />与客户／服务器计算技术从入门到精通刘建元等译电子工业出版社，1996、[3]周中元信息系统建模方法(下)电子与信息化1999年第3期，1999畅想未来，但不可忘了过去的教训我发现询问用户如何看待未来需求变化非常有用。这样做可以达到两 个目的：首先，你可以清楚地了解应用设计在哪个地方应该更具灵活 性以及如何避免性能瓶颈；其次，你知道发生事先没有确定的需求变 更时用户将和你一样感到吃惊。</p>
		<p>一定要记住过去的经验教训！我们开发人员还应该通过分享自己的体 会和经验互相帮助。即使用户认为他们再也不需要什么支持了 ，我们也应该对他们进行这方面的教育，我们都曾经面临过这样的时 刻"当初要是这么做了该多好.."。 </p>
		<p>在物理实践之前进行逻辑设计 </p>
		<p>在深入物理设计之前要先进行逻辑设计。随着大量的 CASE工具不断涌现出来，你的设计也可以达到相当高的逻辑水准 ，你通常可以从整体上更好地了解数据库设计所需要的方方面面。 </p>
		<p>了解你的业务 </p>
		<p>在你百分百地确定系统从客户角度满足其需求之前不要在你的 ER（实体关系）模式中加入哪怕一个数据表（怎么，你还没有模式 ？那请你参看技巧9）。了解你的企业业务可以在以后的开发阶段节约大量的时间 。一旦你明确了业务需求，你就可以自己做出许多决策了。</p>
		<p>一旦你认为你已经明确了业务内容，你最好同客户进行一次系统的交 流。采用客户的术语并且向他们解释你所想到的和你所听到的 。同时还应该用可能、将会和必须等词汇表达出系统的关系基数 。这样你就可以让你的客户纠正你自己的理解然后做好下一步的ER 设计。 </p>
		<p>创建数据字典和 ER 图表 </p>
		<p>一定要花点时间创建 ER 图表和数据字典。其中至少应该包含每个字段的数据类型和在每个表 内的主外键。创建 ER<br />图表和数据字典确实有点费时但对其他开发人员要了解整个设计却是 完全必要的。越早创建越能有助于避免今后面临的可能混乱 ，从而可以让任何了解数据库的人都明确如何从数据库中获得数据。</p>
		<p>有一份诸如 ER 图表等最新文档其重要性如何强调都不过分，这对表明表之间关系很 有用，而数据字典则说明了每个字段的用途以及任何可能存在的别名 。对SQL 表达式的文档化来说这是完全必要的。 </p>
		<p>创建模式 </p>
		<p>一张图表胜过千言万语：开发人员不仅要阅读和实现它 ，而且还要用它来帮助自己和用户对话。模式有助于提高协作效能 ，这样在先期的数据库设计中几乎不可能出现大的问题 。模式不必弄的很复杂；甚至可以简单到手写在一张纸上就可以了 。只是要保证其上的逻辑关系今后能产生效益。 </p>
		<p>从输入输出下手 </p>
		<p>在定义数据库表和字段需求（输入）时，首先应检查现有的或者已经 设计出的报表、查询和视图（输出）以决定为了支持这些输出哪些是 必要的表和字段。举个简单的例子：假如客户需要一个报表按照邮政 编码排序、分段和求和，你要保证其中包括了单独的邮政编码字段而 不要把邮政编码糅进地址字段里。 </p>
		<p>报表技巧 </p>
		<p>要了解用户通常是如何报告数据的：批处理还是在线提交报表 ？时间间隔是每天、每周、每月、每个季度还是每年 ？如果需要的话还可以考虑创建总结表。系统生成的主键在报表中很 难管理。用户在具有系统生成主键的表内用副键进行检索往往会返回 许多重复数据。这样的检索性能比较低而且容易引起混乱。 </p>
		<p>理解客户需求 </p>
		<p>看起来这应该是显而易见的事，但需求就是来自客户 （这里要从内部和外部客户的角度考虑）。不要依赖用户写下来的需 求，真正的需求在客户的脑袋里。你要让客户解释其需求 ，而且随着开发的继续，还要经常询问客户保证其需求仍然在开发的 目的之中。一个不变的真理是："只有我看见了我才知道我想要的是 什么"必然会导致大量的返工，因为数据库没有达到客户从来没有写 下来的需求标准。而更糟的是你对他们需求的解释只属于你自己 ，而且可能是完全错误的。 </p>
		<p>
				<br />第 2 部分 - 设计表和字段 </p>
		<p>
				<br />检查各种变化 </p>
		<p>我在设计数据库的时候会考虑到哪些数据字段将来可能会发生变更 。比方说，姓氏就是如此（注意是西方人的姓氏，比如女性结婚后从 夫姓等）。所以，在建立系统存储客户信息时，我倾向于在单独的一 个数据表里存储姓氏字段，而且还附加起始日和终止日等字段 ，这样就可以跟踪这一数据条目的变化。 </p>
		<p>采用有意义的字段名 </p>
		<p>有一回我参加开发过一个项目，其中有从其他程序员那里继承的程序 ，那个程序员喜欢用屏幕上显示数据指示用语命名字段，这也不赖 ，但不幸的是，她还喜欢用一些奇怪的命名法，其命名采用了匈牙利 命名和控制序号的组合形式，比如cbo1、txt2、txt2_b 等等。<br />除非你在使用只面向你的缩写字段名的系统，否则请尽可能地把字段 描述的清楚些。当然，也别做过头了，比如Customer_Shipping_Address _Street_Line_1，虽然很富有说明性 ，但没人愿意键入这么长的名字，具体尺度就在你的把握中。<br />采用前缀命名。如果多个表里有好多同一类型的字段（比如 FirstName），你不妨用特定表的前缀（比如 CusLastName）来帮助你标识字段。</p>
		<p>时效性数据应包括"最近更新日期/时间"字段。时间标记对查找数 据问题的原因、按日期重新处理/重载数据和清除旧数据特别有用。<br />标准化和数据驱动数据的标准化不仅方便了自己而且也方便了其他人。比方说 ，假如你的用户界面要访问外部数据源（文件、XML<br />文档、其他数据库等），你不妨把相应的连接和路径信息存储在用户 界面支持表里。还有，如果用户界面执行工作流之类的任务 （发送邮件、打印信笺、修改记录状态等），那么产生工作流的数据 也可以存放在数据库里。预先安排总需要付出努力，但如果这些过程 采用数据驱动而非硬编码的方式，那么策略变更和维护都会方便得多 。事实上，如果过程是数据驱动的，你就可以把相当大的责任推给用 户，由用户来维护自己的工作流过程。 </p>
		<p>标准化不能过头 </p>
		<p>对那些不熟悉标准化一词（normalization）的人而言 ，标准化可以保证表内的字段都是最基础的要素，而这一措施有助于 消除数据库中的数据冗余。标准化有好几种形式，但Third Normal Form（3NF）通常被认为在性能、扩展性和数据完整性方面达 到了最好平衡。简单来说，3NF 规定：<br />* 表内的每一个值都只能被表达一次。<br />* 表内的每一行都应该被唯一的标识（有唯一键）。<br />* 表内不应该存储依赖于其他键的非键信息。<br />遵守 3NF 标准的数据库具有以下特点：有一组表专门存放通过键连接起来的关 联数据。比方说，某个存放客户及其有关定单的 3NF<br />数据库就可能有两个表：Customer 和 Order。Order 表不包含定单关联客户的任何信息，但表内会存放一个键值 ，该键指向Customer 表里包含该客户信息的那一行。<br />更高层次的标准化也有，但更标准是否就一定更好呢？答案是不一定 。事实上，对某些项目来说，甚至就连 3NF 都可能给数据库引入太高的复杂性。</p>
		<p>为了效率的缘故，对表不进行标准化有时也是必要的 ，这样的例子很多。曾经有个开发餐饮分析软件的活就是用非标准化 表把查询时间从平均 40秒降低到了两秒左右。虽然我不得不这么做，但我绝不把数据表的非 标准化当作当然的设计理念。而具体的操作不过是一种派生 。所以如果表出了问题重新产生非标准化的表是完全可能的。 </p>
		<p>Microsoft Visual FoxPro 报表技巧 </p>
		<p>如果你正在使用 Microsoft Visual FoxPro，你可以用对用户友好的字段名来代替编号的名称 ：比如用 Customer Name 代替 txtCNaM。这样，当你用向导程序 [Wizards，台湾人称为'精灵'] 创建表单和报表时，其名字会让那些不是程序员的人更容易阅读。 </p>
		<p>不活跃或者不采用的指示符 </p>
		<p>增加一个字段表示所在记录是否在业务中不再活跃挺有用的 。不管是客户、员工还是其他什么人，这样做都能有助于再运行查询 的时候过滤活跃或者不活跃状态。同时还消除了新用户在采用数据时 所面临的一些问题，比如，某些记录可能不再为他们所用 ，再删除的时候可以起到一定的防范作用。 </p>
		<p>使用角色实体定义属于某类别的列[字段] </p>
		<p>在需要对属于特定类别或者具有特定角色的事物做定义时 ，可以用角色实体来创建特定的时间关联关系，从而可以实现自我文 档化。<br />这里的含义不是让 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 等。<br />还有个替代办法就是改变 PERSON 记录来反映新头衔的变化，不过这样一来在时间上无法跟踪个人所处 位置的具体时间。 </p>
		<p>采用常用实体命名机构数据 </p>
		<p>组织数据的最简单办法就是采用常用名字，比如：PERSON 、ORGANIZATION、ADDRESS 和 PHONE等等。当你把这些常用的一般名字组合起来或者创建特定的相应副实 体时，你就得到了自己用的特殊版本。开始的时候采用一般术语的主 要原因在于所有的具体用户都能对抽象事物具体化。<br />有了这些抽象表示，你就可以在第 2 级标识中采用自己的特殊名称，比如，PERSON 可能是Employee、Spouse、Patient、Client 、Customer、Vendor 或者 Teacher等。同样的，ORGANIZATION 也可能是<br />MyCompany、MyDepartment、Competi tor、Hospital、Warehouse、Governm ent 等。最后ADDRESS 可以具体为 Site、Location、Home、Work、Client 、Vendor、Corporate 和FieldOffice 等。<br />采用一般抽象术语来标识"事物"的类别可以让你在关联数据以满足 业务要求方面获得巨大的灵活性，同时这样做还可以显著降低数据存 储所需的冗余量。 </p>
		<p>用户来自世界各地 </p>
		<p>在设计用到网络或者具有其他国际特性的数据库时，一定要记住大多 数国家都有不同的字段格式，比如邮政编码等，有些国家 ，比如新西兰就没有邮政编码一说。 </p>
		<p>数据重复需要采用分立的数据表 </p>
		<p>如果你发现自己在重复输入数据，请创建新表和新的关系。<br />每个表中都应该添加的 3 个有用的字段<br />* dRecordCreationDate，在 VB 下默认是 Now()，而在 SQL Server 下默认为 GETDATE()<br />* sRecordCreator，在 SQL Server 下默认为 NOT NULL DEFAULT USER<br />* nRecordVersion，记录的版本标记；有助于准确说明 记录中出现 null 数据或者丢失数据的原因<br />对地址和电话采用多个字段<br />描述街道地址就短短一行记录是不够的。Address _Line1、Address_Line2 和 Address_Line3<br />可以提供更大的灵活性。还有，电话号码和邮件地址最好拥有自己的 数据表，其间具有自身的类型和标记类别。</p>
		<p>过分标准化可要小心，这样做可能会导致性能上出现问题 。虽然地址和电话表分离通常可以达到最佳状态，但是如果需要经常 访问这类信息，或许在其父表中存放"首选"信息（比如<br />Customer 等）更为妥当些。非标准化和加速访问之间的妥协是有一定意义的。 </p>
		<p>使用多个名称字段 </p>
		<p>我觉得很吃惊，许多人在数据库里就给 name留一个字段。我觉得只有刚入门的开发人员才会这么做 ，但实际上网上这种做法非常普遍。我建议应该把姓氏和名字当作两 个字段来处理，然后在查询的时候再把他们组合起来。</p>
		<p>我最常用的是在同一表中创建一个计算列[字段]，通过它可以自动 地连接标准化后的字段，这样数据变动的时候它也跟着变。不过 ，这样做在采用建模软件时得很机灵才行。总之，采用连接字段的方 式可以有效的隔离用户应用和开发人员界面。<br />提防大小写混用的对象名和特殊字符过去最令我恼火的事情之一就是数据库里有大小写混用的对象名 ，比如 CustomerData。这一问题从 Access 到 Oracle数据库都存在。我不喜欢采用这种大小写混用的对象命名方法 ，结果还不得不手工修改名字。想想看，这种数据库 /应用程序能混到采用更强大数据库的那一天吗？采用全部大写而且 包含下划符的名字具有更好的可读性（CUSTOMER_DATA ），绝对不要在对象名的字符之间留空格。 </p>
		<p>小心保留词 </p>
		<p>要保证你的字段名没有和保留词、数据库系统或者常用访问方法冲突 ，比如，最近我编写的一个 ODBC 连接程序里有个表，其中就用了 DESC作为说明字段名。后果可想而知！DESC 是 DESCENDING 缩写后的保留词。表里的一个 SELECT *语句倒是能用，但我得到的却是一大堆毫无用处的信息。 </p>
		<p>保持字段名和类型的一致性 </p>
		<p>在命名字段并为其指定数据类型的时候一定要保证一致性 。假如字段在某个表中叫做"agreement_number" ，你就别在另一个表里把名字改成"ref1"。假如数据类型在一 个表里是整数，那在另一个表里可就别变成字符型了。记住 ，你干完自己的活了，其他人还要用你的数据库呢。 </p>
		<p>仔细选择数字类型 </p>
		<p>在 SQL 中使用 smallint 和 tinyint 类型要特别小心，比如，假如你想看看月销售总额，你的总额字段类 型是smallint，那么，如果总额超过了 $32,767 你就不能进行计算操作了。 </p>
		<p>删除标记 </p>
		<p>在表中包含一个"删除标记"字段，这样就可以把行标记为删除 。在关系数据库里不要单独删除某一行；最好采用清除数据程序而且 要仔细维护索引整体性。 </p>
		<p>避免使用触发器 </p>
		<p>触发器的功能通常可以用其他方式实现。在调试程序时触发器可能成 为干扰。假如你确实需要采用触发器，你最好集中对它文档化。<br />包含版本机制建议你在数据库中引入版本控制机制来确定使用中的数据库的版本 。无论如何你都要实现这一要求。时间一长，用户的需求总是会改变 的。最终可能会要求修改数据库结构。虽然你可以通过检查新字段或 者索引来确定数据库结构的版本，但我发现把版本信息直接存放到数 据库中不更为方便吗？ </p>
		<p>给文本字段留足余量 </p>
		<p>ID 类型的文本字段，比如客户 ID或定单号等等都应该设置得比一般想象更大，因为时间不长你多半就 会因为要添加额外的字符而难堪不已。比方说，假设你的客户 ID 为 10位数长。那你应该把数据库表字段的长度设为 12 或者 13 个字符长。这算浪费空间吗？是有一点，但也没你想象的那么多 ：一个字段加长 3个字符在有 1 百万条记录，再加上一点索引的情况下才不过让整个数据库多占据 3MB的空间。但这额外占据的空间却无需将来重构整个数据库就可以实现 数据库规模的增长了。身份证的号码从 15 位变成 18<br />位就是最好和最惨痛的例子。 </p>
		<p>列[字段]命名技巧 </p>
		<p>我们发现，假如你给每个表的列[字段]名都采用统一的前缀 ，那么在编写 SQL表达式的时候会得到大大的简化。这样做也确实有缺点 ，比如破坏了自动表连接工具的作用，后者把公共列[字段 ]名同某些数据库联系起来，不过就连这些工具有时不也连接错误嘛 。举个简单的例子，假设有两个表：<br />Customer 和 Order。Customer 表的前缀是cu_，所以该表内的子段名如下：cu_name_id、cu _surname、cu_initials 和cu_address 等。Order表的前缀是 or_，所以子段名是：<br />or_order_id、or_cust_name_id、or _quantity 和 or_denoscription 等。<br />这样从数据库中选出全部数据的 SQL 语句可以写成如下所示：<br />Select * From Customer, Order Where cu_surname = "MYNAME" ;<br />and cu_name_id = or_cust_name_id and or_quantity = 1<br />在没有这些前缀的情况下则写成这个样子（用别名来区分）：<br />Select * From Customer, Order Where Customer.surname = "MYNAME" ;<br />and Customer.name_id = Order.cust_name_id and Order.quantity = 1<br />第 1 个 SQL 语句没少键入多少字符。但如果查询涉及到 5 个表乃至更多的列[字段]你就知道这个技巧多有用了。 </p>
		<p>第 3 部分 - 选择键和索引 </p>
		<p>
				<br />数据采掘要预先计划 </p>
		<p>我所在的某一客户部门一度要处理 8万多份联系方式，同时填写每个客户的必要数据（这绝对不是小活） 。我从中还要确定出一组客户作为市场目标。当我从最开始设计表和 字段的时候，我试图不在主索引里增加太多的字段以便加快数据库的 运行速度。然后我意识到特定的组查询和信息采掘既不准确速度也不 快。结果只好在主索引中重建而且合并了数据字段。我发现有一个指 示计划相当关键——当我想创建系统类型查找时为什么要采用号码作 为主索引字段呢？我可以用传真号码进行检索，但是它几乎就象系统 类型一样对我来说并不重要。采用后者作为主字段，数据库更新后重 新索引和检索就快多了。</p>
		<p>可操作数据仓库（ODS）和数据仓库（DW）这两种环境下的数据 索引是有差别的。在 DW环境下，你要考虑销售部门是如何组织销售活动的。他们并不是数据 库管理员，但是他们确定表内的键信息。这里设计人员或者数据库工 作人员应该分析数据库结构从而确定出性能和正确输出之间的最佳条 件。 </p>
		<p>使用系统生成的主键 </p>
		<p>这类同技巧 1，但我觉得有必要在这里重复提醒大家。假如你总是在设计数据库 的时候采用系统生成的键作为主键，那么你实际控制了数据库的索引 完整性。这样，数据库和非人工机制就有效地控制了对存储数据中每 一行的访问。<br />采用系统生成键作为主键还有一个优点：当你拥有一致的键结构时 ，找到逻辑缺陷很容易。 </p>
		<p>分解字段用于索引 </p>
		<p>为了分离命名字段和包含字段以支持用户定义的报表 ，请考虑分解其他字段（甚至主键）为其组成要素以便用户可以对其 进行索引。索引将加快 SQL和报表生成器脚本的执行速度。比方说，我通常在必须使用 SQL LIKE 表达式的情况下创建报表，因为 case number 字段无法分解为year、serial number、case type 和 defendant code等要素。性能也会变坏。假如年度和类型字段可以分解为索引字段那 么这些报表运行起来就会快多了。 </p>
		<p>键设计 4 原则 </p>
		<p>* 为关联字段创建外键。<br />* 所有的键都必须唯一。<br />* 避免使用复合键。<br />* 外键总是关联唯一的键字段。 </p>
		<p>别忘了索引 </p>
		<p>索引是从数据库中获取数据的最高效方式之一。95%的数据库性能问题都可以采用索引技术得到解决。作为一条规则 ，我通常对逻辑主键使用唯一的成组索引，对系统键（作为存储过程 ）采用唯一的非成组索引，对任何外键列[字段]采用非成组索引 。不过，索引就象是盐，太多了菜就咸了。你得考虑数据库的空间有 多大，表如何进行访问，还有这些访问是否主要用作读写。</p>
		<p>大多数数据库都索引自动创建的主键字段，但是可别忘了索引外键 ，它们也是经常使用的键，比如运行查询显示主表和所有关联表的某 条记录就用得上。还有，不要索引memo/note 字段，不要索引大型字段（有很多字符），这样作会让索引占用太多 的存储空间。<br />不要索引常用的小型表不要为小型数据表设置任何键，假如它们经常有插入和删除操作就更 别这样作了。对这些插入和删除操作的索引维护可能比扫描表空间消 耗更多的时间。<br />不要把社会保障号码（SSN）或身份证号码（ID）选作键。<br />永远都不要使用 SSN 或 ID 作为数据库的键。除了隐私原因以外，须知政府越来越趋向于不准许 把 SSN 或 ID<br />用作除收入相关以外的其他目的，SSN 或 ID需要手工输入。永远不要使用手工输入的键作为主键 ，因为一旦你输入错误，你唯一能做的就是删除整个记录然后从头开 始。</p>
		<p>我在破解他人的程序时候，我看到很多人把 SSN 或 ID还曾被用做系列号，当然尽管这么做是非法的。而且人们也都知道这 是非法的，但他们已经习惯了。后来，随着盗取身份犯罪案件的增加 ，我现在的同行正痛苦地从一大摊子数据中把 SSN 或 ID 删除。 </p>
		<p>不要用用户的键 </p>
		<p>在确定采用什么字段作为表的键的时候，可一定要小心用户将要编辑 的字段。通常的情况下不要选择用户可编辑的字段作为键 。这样做会迫使你采取以下两个措施：<br />* 在创建记录之后对用户编辑字段的行为施加限制。假如你这么做了 ，你可能会发现你的应用程序在商务需求突然发生变化 ，而用户需要编辑那些不可编辑的字段时缺乏足够的灵活性 。当用户在输入数据之后直到保存记录才发现系统出了问题他们该怎 么想？删除重建？假如记录不可重建是否让用户走开？<br />* 提出一些检测和纠正键冲突的方法。通常，费点精力也就搞定了 ，但是从性能上来看这样做的代价就比较大了。还有 ，键的纠正可能会迫使你突破你的数据和商业/用户界面层之间的隔 离。<br />所以还是重提一句老话：你的设计要适应用户而不是让用户来适应你 的设计。</p>
		<p>不让主键具有可更新性的原因是在关系模式下，主键实现了不同表之 间的关联。比如，Customer 表有一个主键CustomerID，而客户的定单则存放在另一个表里 。Order 表的主键可能是 OrderNo 或者 OrderNo、CustomerID和日期的组合。不管你选择哪种键设置，你都需要在 Order 表中存放 CustomerID 来保证你可以给下定单的用户找到其定单记录。<br />假如你在 Customer 表里修改了 CustomerID，那么你必须找出 Order表中的所有相关记录对其进行修改。否则，有些定单就会不属于任何 客户——数据库的完整性就算完蛋了。<br />如果索引完整性规则施加到表一级，那么在不编写大量代码和附加删 除记录的情况下几乎不可能改变某一条记录的键和数据库内所有关联 的记录。而这一过程往往错误丛生所以应该尽量避免。 </p>
		<p>可选键(候选键)有时可做主键 </p>
		<p>记住，查询数据的不是机器而是人。<br />假如你有可选键，你可能进一步把它用做主键。那样的话 ，你就拥有了建立强大索引的能力。这样可以阻止使用数据库的人不 得不连接数据库从而恰当的过滤数据。在严格控制域表的数据库上 ，这种负载是比较醒目的。如果可选键真正有用，那就是达到了主键 的水准。<br />我的看法是，假如你有可选键，比如国家表内的state_code，你不要在现有不能变动的唯一键上创建后续 的键。你要做的无非是创建毫无价值的数据。如你因为过度使用表的 后续键[别名]建立这种表的关联，操作负载真得需要考虑一下了。 </p>
		<p>别忘了外键 </p>
		<p>大多数数据库索引自动创建的主键字段。但别忘了索引外键字段 ，它们在你想查询主表中的记录及其关联记录时每次都会用到。还有 ，不要索引memo/notes 字段而且不要索引大型文本字段（许多字符），这样做会让你的索引 占据大量的数据库空间。 </p>
		<p>第 4 部分 - 保证数据的完整性 </p>
		<p>用约束而非商务规则强制数据完整性 </p>
		<p>如果你按照商务规则来处理需求，那么你应当检查商务层次 /用户界面：如果商务规则以后发生变化，那么只需要进行更新即可 。假如需求源于维护数据完整性的需要，那么在数据库层面上需要施 加限制条件。如果你在数据层确实采用了约束，你要保证有办法把更 新不能通过约束检查的原因采用用户理解的语言通知用户界面 。除非你的字段命名很冗长，否则字段名本身还不够。</p>
		<p>只要有可能，请采用数据库系统实现数据的完整性。这不但包括通过 标准化实现的完整性而且还包括数据的功能性。在写数据的时候还可 以增加触发器来保证数据的正确性。不要依赖于商务层保证数据完整 性；它不能保证表之间（外键）的完整性所以不能强加于其他完整性 规则之上。 </p>
		<p>分布式数据系统 </p>
		<p>对分布式系统而言，在你决定是否在各个站点复制所有数据还是把数 据保存在一个地方之前应该估计一下未来 5 年或者 10<br />年的数据量。当你把数据传送到其他站点的时候，最好在数据库字段 中设置一些标记。在目的站点收到你的数据之后更新你的标记 。为了进行这种数据传输，请写下你自己的批处理或者调度程序以特 定时间间隔运行而不要让用户在每天的工作后传输数据 。本地拷贝你的维护数据，比如计算常数和利息率等 ，设置版本号保证数据在每个站点都完全一致。 </p>
		<p>强制指示完整性(参照完整性?) </p>
		<p>没有好办法能在有害数据进入数据库之后消除它，所以你应该在它进 入数据库之前将其剔除。激活数据库系统的指示完整性特性 。这样可以保持数据的清洁而能迫使开发人员投入更多的时间处理错误条件。 </p>
		<p>关系 </p>
		<p>如果两个实体之间存在多对一关系，而且还有可能转化为多对多关系 ，那么你最好一开始就设置成多对多关系。从现有的多对一关系转变 为多对多关系比一开始就是多对多关系要难得多。 </p>
		<p>采用视图 </p>
		<p>为了在你的数据库和你的应用程序代码之间提供另一层抽象 ，你可以为你的应用程序建立专门的视图而不必非要应用程序直接访 问数据表。这样做还等于在处理数据库变更时给你提供了更多的自由 。 </p>
		<p>给数据保有和恢复制定计划 </p>
		<p>考虑数据保有策略并包含在设计过程中，预先设计你的数据恢复过程 。采用可以发布给用户/开发人员的数据字典实现方便的数据识别同 时保证对数据源文档化。编写在线更新来"更新查询 "供以后万一数据丢失可以重新处理更新。<br />用存储过程让系统做重活解决了许多麻烦来产生一个具有高度完整性的数据库解决方案之后 ，我决定封装一些关联表的功能组，提供一整套常规的存储过程来访 问各组以便加快速度和简化客户程序代码的开发。数据库不只是一个 存放数据的地方，它也是简化编码之地。 </p>
		<p>使用查找 </p>
		<p>控制数据完整性的最佳方式就是限制用户的选择。只要有可能都应该 提供给用户一个清晰的价值列表供其选择。这样将减少键入代码的错 误和误解同时提供数据的一致性。某些公共数据特别适合查找 ：国家代码、状态代码等。 </p>
		<p>
				<br />第 5 部分 - 各种小技巧 </p>
		<p>文档、文档、文档 </p>
		<p>对所有的快捷方式、命名规范、限制和函数都要编制文档。</p>
		<p>采用给表、列[字段]、触发器等加注释的数据库工具。是的 ，这有点费事，但从长远来看，这样做对开发、支持和跟踪修改非常 有用。</p>
		<p>取决于你使用的数据库系统，可能有一些软件会给你一些供你很快上 手的文档。你可能希望先开始在说，然后获得越来越多的细节 。或者你可能希望周期性的预排，在输入新数据同时随着你的进展对 每一部分细节化。不管你选择哪种方式，总要对你的数据库文档化 ，或者在数据库自身的内部或者单独建立文档。这样 ，当你过了一年多时间后再回过头来做第2 个版本，你犯错的机会将大大减少。 </p>
		<p>使用常用英语（或者其他任何语言）而不要使用编码 </p>
		<p>为什么我们经常采用编码（比如 9935A 可能是'青岛啤酒'的供应代码，4XF788-Q可能是帐目编码）？理由很多。但是用户通常都用英语进行思考而不 是编码。工作 5 年的会计或许知道 4XF788-Q是什么东西，但新来的可就不一定了。在创建下拉菜单、列表 、报表时最好按照英语名排序。假如你需要编码，那你可以在编码旁 附上用户知道的英语。 </p>
		<p>保存常用信息 </p>
		<p>让一个表专门存放一般数据库信息非常有用。我常在这个表里存放数 据库当前版本、最近检查/修复（对FoxPro）、关联设计文档的名称、客户等信息 。这样可以实现一种简单机制跟踪数据库，当客户抱怨他们的数据库 没有达到希望的要求而与你联系时，这样做对非客户机 /服务器环境特别有用。 </p>
		<p>测试、测试、反复测试 </p>
		<p>建立或者修订数据库之后，必须用用户新输入的数据测试数据字段 。最重要的是，让用户进行测试并且同用户一道保证你选择的数据类 型满足商业要求。测试需要在把新数据库投入实际服务之前完成。 </p>
		<p>检查设计 </p>
		<p>在开发期间检查数据库设计的常用技术是通过其所支持的应用程序原 型检查数据库。换句话说，针对每一种最终表达数据的原型应用 ，保证你检查了数据模型并且查看如何取出数据。 </p>
		<p>Microsoft Visual FoxPro 设计技巧 </p>
		<p>对复杂的 Microsoft Visual FoxPro数据库应用程序而言，可以把所有的主表放在一个数据库容器文件里 ，然后增加其他数据库表文件和装载同原有数据库有关的特殊文件 。根据需要用这些文件连接到主文件中的主表。比如数据输入 、数据索引、统计分析、向管理层或者政府部门提供报表以及各类只 读查询等。这一措施简化了用户和组权限的分配，而且有利于应用程 序函数（存储过程）的分组和划分，从而在程序必须修改的时候易于 管理。 </p>
<img src ="http://www.blogjava.net/weibogao/aggbug/93125.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/weibogao/" target="_blank">weibogao</a> 2007-01-11 12:09 <a href="http://www.blogjava.net/weibogao/archive/2007/01/11/93125.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>转《“数据建模”读书笔记 》</title><link>http://www.blogjava.net/weibogao/archive/2007/01/11/93122.html</link><dc:creator>weibogao</dc:creator><author>weibogao</author><pubDate>Thu, 11 Jan 2007 04:01:00 GMT</pubDate><guid>http://www.blogjava.net/weibogao/archive/2007/01/11/93122.html</guid><wfw:comment>http://www.blogjava.net/weibogao/comments/93122.html</wfw:comment><comments>http://www.blogjava.net/weibogao/archive/2007/01/11/93122.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/weibogao/comments/commentRss/93122.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/weibogao/services/trackbacks/93122.html</trackback:ping><description><![CDATA[
		<h3 class="title"> </h3>
		<p>         最近逛书店发现一本数据建模的好书——《数据建模：分析与设计的工具和技巧》(Data Modeler's Workbench：Tools and Techniques for Analysis and Design)，作者Steve Hoberman。粗读完一遍后，感觉这本书的确无愧于译者和国外专家们的盛赞：“这本书充满了对改进数据模型和设计有益的技术和技巧，并且它还极富阅读乐趣——一个了不起的结合！任何一个数据建模者都应该拥有一本Steve Hoberman的关于数据建模工具和技术的书。”</p>
		<p>　　尽管我对自己所掌握的数据建模知识有一定的自负，读完该书后，还是获益良多。本着好书大家一起分享的想法，我把该书的每个章节的总结和技巧建议列出来，以方便手头暂时没有该书的朋友在数据建模时参考。该书所介绍的工具和模版可在作者的Web站点下载，地址是：<br />　　www.wiley.com/compbooks/hoberman</p>
		<p>　　第一章：使用趣闻、类比和演示文稿来阐明数据建模的概念</p>
		<p>　　在一般的日常沟通中。我们可能会说出并听到许多故事、或者趣闻这些故事涉及的论题范围很大。有些例子是周末发生在我们自己身边的事情，或者是与我们的工作项目有关的经历。这些趣闻有助于加强我们和周围人们的关系，增进我们的愉悦情绪，而且对我们有教育作用。我们能够把由语言表达出来的东西形象化。有时，当故事结束时，给我们留下的是以前未曾想到的信息或更多的认识。在解释数据建模概念时，趣闻是极其有效的。原因有如下几个：<br />　　它们建立起持久的形象。<br />　　它们引人入胜、使人愉悦。<br />　　它们增经人们之间的关系。<br />　　它们减缓压力。</p>
		<p>　　成功编造并讲述一个数据建模方面的趣闻有下面三个简单的步骤：<br />　　1)定义一个论题。要在心中保证，你讲述的这个趣闻有一个特定的目标或论题，也就是说，这个故事是为了解释一个数据建模的概念或术语。<br />　　2)选择你的故事。我们可以选择的故事类型多种多样。我们要考虑选择一个有趣并有益，而且能够明白无误地传达主题意图的简短的故事。<br />　　3)演练你的故事。一旦找到了合适的故事，你要好好演练一番，直到你自信它能够在两分钟的时间内充分表达你的论题。要避免讲述拖拖拉拉的故事。</p>
		<p>　　数据模型类比<br />　　类比就是把两个或多个概念进行相互比较，以强调它们之间的相似或差异。类比是介绍外来事物或新鲜事物的一个很好的技巧，尤其是向非计算机专业的人士介绍计算机的专业知识时。Hoberman在数据建模中最常见的几个类比如下（他用这些类比轻松的打动管理层给他涨了一倍的工资^_^）：<br />　　主体域模型是一个居高临下的视点。<br />　　数据模型是一个设计图。<br />　　企业模型是一个世界地图。<br />　　标准就是城市规划。<br />　　元数据仓储库是一个图书馆。<br />　　数据仓库是“心脏”。</p>
		<p>　　第二章：元数据宾果游戏<br />　　简单来说，即通过宾果卡片游戏的方式，调动项目团队成员的积极性，来确定数据模型，并确定元数据的有效性。元数据宾果游戏强调“共赢”，如果运气好，游戏结束时每个人都能赢。</p>
		<p>　　第三章：确保高质量的定义<br />　　本章集中讨论一个被称为“定义检查单”(Definition Checklist)的工具，它包含了确保定义的质量处于最高水平的准则。</p>
		<p>　　第四章：数据建模者的项目计划<br />　　本章重点介绍确定数据建模阶段、任务、工具和时限的四个工具：<br />　　·数据建模阶段的工具：用来确定最高层次上的数据建模步骤。<br />　　·阶段—任务—工具：提取出“数据建模阶段”的各个阶段并把他们分解成数据建模任务。<br />　　·优先级三角形：你可以从以下三项中取两项极值：很高的质量、最短的时间与最低的成本，但你永远也别想三者兼得。<br />　　·可靠的估算工具：“主体域工作量时限”根据应用程序的类型，确定每个数据建模阶段应占整个项目的百分比。“任务工作量工具”提取在“阶段—任务—工具”中确定的每项任务，并列出它们应占整个数据建模工作产品的百分比。这两个工具的组合可使你向项目经理提供一份具有一定精确度的合理估算。</p>
		<p>　　第五章：主体域分析<br />　　本章主要探讨五个关键的工具，这五个工具对数据建模工作的主体域分析阶段有帮组作用。它们应该按照下面的顺序被逐个完成：<br />　　1)主体域检查单：新应用程序中的主体域的完整列表，还有各个主体域的定义和同义词（或别名）。<br />　　2)主体域CRUD(Create Read Update Delete)矩阵：包含新应用程序和现有应用程序之间的主体域方面的差别和重复之处，确定应用程序的范围。<br />　　3)In-the-Know模版：确定完成这个新应用程序的数据间模工作产品所需要的、被用作资源的人员和文档。<br />　　4)主体域家族树：包含每一个主体域的源应用程序和若干其他的关键信息，阐明主体域数据将来自哪里。<br />　　5)主体域力度矩阵：使用一个电子表格的格式，记录每一个度量和事实主体域的发布层次。</p>
		<p>　　第六章：主体域建模<br />　　本章阐述三个队主体域信息进行建模的强大工具：<br />　　·“业务清理板”模型。<br />　　·“应用程序清理板”模型。<br />　　·“早期现实性检查”模型。</p>
		<p>　　第七章：逻辑数据分析<br />　　本章关注四个逻辑数据分析工具，它们应该按照下面的次序被使用：<br />　　1)数据元素家族树：包含应用程序的数据元素的完整列表，以及每个数据元素的来源和变换信息，还有其他几个关键的数据元素元数据。<br />　　2)数据元素粒度矩阵：用一个电子表格的格式，来记录每个度量和事实的发布层次。<br />　　3)数据质量记录模板：展示每个数据元素的员数据和一些实际数据的对比。<br />　　4)数据质量确认模板：记录每个数据元素的元数据和一些实际数据的对比的结果。</p>
		<p>　　第八章：规范化之旅和反向规范化生存指南（强烈推荐：是我目前所读过最好的关系型数据库的规范化技术文档）<br />　　规范化是一个剔除冗余并应用规则的过程，它的目的是为了更好的理解和表达存在于数据元素之间的依赖性和参与性。规范化包含6个层次，最高层是第五范式(5NF)。一般的技术文档上都认为达到3NF即可，Steve Hoberman给我们指明了更高的目标：5NF。Graeme Simsion写过一本名为《Data Modeling Essentials》的书，在这本书中，他写道：“较高层次的范式常被从业者误解并因此而被忽视，或为了支持不可靠的建模时间而被引用。”但是，我们需要理解这些较高层次的规范化，因为它们体现了额外的规范化机会，并帮组我们进一步减少冗余信息、改进设计的灵活性。尽管余下的三个规范化层次有可能仅仅产生次数很少的变化，但它们仍然具有一些提高灵活性和效率的机会。下面是BCNF&amp;4NF&amp;5NF的定义(比国内教材上罗列的数学公式容易理解得多^_^)：<br />　　BCNF=3NF＋下面的规则：<br />　　每一个数据元素都完全依赖于键、整个键，而且除依赖于这个键以外，不依赖于任何其他数据元素。<br />　　4NF=3NF+下面的规则：<br />　　要把主键中拥有三个或更多外建数据元素、切割格外键之间不存在约束的那些实体分解成两个或更多个实体。<br />　　5NF=4NF+下面的规则：<br />　　把主键中拥有三个或更多的外键数据元素，且这些外键数据元素之间存在着约束的实体分解成为所有的约束都需要的多对多的关系。</p>
		<p>　　当我们攀上5NF的顶峰后，再根据实际需求情况来进行“反向规范化”增加数据冗余，从而简化开发，提高查询速度。反向规范化是这样一个过程：在定义了一个可靠的、完全规范化了的数据结构之后，你借助这个过程，有选择地引入一些重复的数据，以促进特殊性能需求的实现。Steve Hoberman的“反向规范化生存指南”给如何适当增加冗余提供了一套可计算的评分标准。通过考察每个关系的6个问题，累加各个问题的得分之后，当得分大于等于10时，我们将对该关系进行反向规范化。</p>
		<p>　　“反向规范化生存指南”的计分规则：<br />　　1.关系是什么类型的：该问题确定我们所分析的关系的类型。父实体对于子实体具有什么样的关系？<br />　　层次关系(20分)<br />　　同等关系(-10分)<br />　　确定关系(-20分)<br />　　2.参与率是多少：该问题确定一个关系中的每个实体的参与性。换句话说，对于一个给定的父实体数值，大概会有几个子实体数值？父与子的关系越接近“一对一”，我们对它进行反向规范化的机会就越大。<br />　　多达“一对五”的比率(20分)<br />　　多达“一对一百”的比率(-10分)<br />　　超过“一对一百”的比率(-20分)<br />　　3.父实体中有多少个数据元素<br />　　少于10个数据元素(20分)<br />　　数据元素的数量介于10到20之间(-10分)<br />　　多于20个数据元素(-20分)<br />　　4.使用率是多少：当用户需要来自子的信息时，通常情况下，它们是否还需要来自父的信息呢？换句话说，这两个实体的耦合或相关程度如何？<br />　　相互之间的关联很强(30分)<br />　　相互之间的关联较弱或者没有关联(-30分)<br />　　5.父实体时一个占位符吗：在不远的将来，我们是否还打算向父实体加入更多的数据元素或关系？如果答案是“不”，那么进行反向规范化的可行性就更强。<br />　　是(20分)<br />　　不(-20分)<br />　　6.变动对比率是多少：该问题是为了确定，在同一时间周期内，两个实体的插入和更新的频度是否相近。如果其中一个实体很少变化，而另一个实体却变动频繁，那么，我们就非常倾向于保持它们的规范化状态，把它们放在各自的表中。<br />　　相同(20分)<br />　　不同(-20分)</p>
		<p>　　“反向规范化生存指南”的使用方法：<br />　　1)把模型中的关系按照优先级排序<br />　　2)选择一个关系<br />　　3)对这个关系回答提问<br />　　4)如果得分等于或大于10，就进行反向规范化<br />　　5)返回步骤二，直到完成所有的关系。</p>
		<p>　　第九章：抽象化安全指南和组件<br />　　看过我的“浅谈数据库设计技巧(上) ”的朋友应该还记得我举的第二个例子：网上电子商务平台上的商品信息表的设计。本章将我在上面例子中所用的方法上升到了理论阶段，采用了面向对象的设计，将所有商品的共有属性提取出来，抽象成一个超类，再加入一个表来记录各个不同实体之间的细节来实现超类的派生，从而实现设计的灵活性。当出现下面两种条件的任何场合，抽象化都是极其有用的：<br />　　设计需要永久维持下去：要求以后尽可能的不修改数据库设计<br />　　需求可能发生变化：应用程序的需求发生变化，而要求业务流程重组或进行功能升级<br />　　数据仓库：当新的分类类型从源应用程序中传过来时，我们无须对数据仓库的设计进行任何改动，而只需在分类类型实体加入一个新行即可<br />　　元数据仓储库：和数据仓库的要求类似</p>
		<p>　　当然，抽象化会大大增加工作量和开发的复杂度，而人们通常关注的是非常短期的应用和眼前的成本，而不关心将来的高得多的成本。所以，我非常赞同敏捷软件开发这个观点：在最初几乎不进行预先设计，但是一旦需求发生变化，此时作为一名追求卓越的程序员，应该从头审查整个架构设计，在此次修改中设计出能够满足日后类似修改的系统架构。</p>
		<p>　　“抽象组件”就是小型的抽象模型片段，在许多的建模场合(无论是什么行业、组织，甚至什么主体域的建模场合)中，它们都可被反复使用。在键模阶段多次使用抽象化之后，你将开始看到出现的抽象化结构的趋势。这些“抽象组件”有如下的目的：<br />　　加快设计速度<br />　　加快开发速度<br />　　提供通用且有用的机构</p>
		<p>　　第十章：数据模型美化技巧<br />　　本章通过关注如何改进逻辑和物理数据模型的视觉外观，使我们的设计超越直接的应用程序需求。本章中讨论了五个类别的美化技巧：<br />　　逻辑数据元素排列技巧：这些技巧是一个推荐的、对你的逻辑数据模型中的每一个实体的数据元素进行排序的方法。<br />　　物理数据元素排序技巧：这些技巧关注数据模型中每一个实体的最佳布局。<br />　　实体布局技巧：这些技巧关注数据模型中的每一个实体的最佳布局<br />　　关系布局技巧：这些技巧关注如何调整重叠的关系线条以及看起来穿越（而不是绕过）无关实体的关系<br />　　吸引注意力的技巧：这些技巧关注如何在我们的涉及中突出的某些元素、实体或关系。</p>
		<p>　　第十一章：规划一个长盛不衰的数据建模生涯<br />　　对数据建模者的十大忠告清单：<br />　　1)记住：灵活性、准确性和背景<br />　　2)建模只是你的工作的一小部分<br />　　3)尝试其他角色<br />　　4)了解95/5规则：95%的时间将花费在5%的数据元素上<br />　　5)数据建模从不令人厌烦：如果你一直在做数据建模工作，而且发现自己经常感到厌烦，那么，你的确该改变一下了。这可能不是数据建模领域本身令人厌烦，而是你所在的特定的任务、公司或行业不再令人兴奋。冒险一下，尝试着道一个不同的项目或行业中进行数据建模工作吧！<br />　　6)站在技术前沿<br />　　7)尽量不要在模型上牵扯感情因素：建模者必须理解，人们在评审过程中的意见并不是针对模型的创建者，而是针对这个模型的内容。即那句老话：对事不对人。<br />　　8)让你的创造力展开翅膀：在考虑记录数据需求和改进设计的新方法时，要紧可能有创造性。有创造性也许就意味着修改本书中的某些工具。这还可能意味着提出你自己的电子表格或其他工具。<br />　　9)单纯的理论太昂贵了：在设计活动过程中，你要确保把这个观点牢记在心。为这个应用程序掏腰包的部门和组织期望看到的是能看得着的实用结果。<br />　　10)成为一个了不起的会讲故事的人：作为一名数据建模者，讲故事是工作的一个很重要的部分。为了帮组教化和影响项目经理以及对我们行业缺乏理解的其他人，我们需要讲故事或趣闻。</p>
		<p>　　最后，我个人觉得，Steve Hoberman所提出的“抽象组件”的观念和面向对象设计中的的“设计模式”非常类似。即数据库专家在多次的数据建模后，将各个项目中的类似部分抽象化，提取出特定的建模模型片段，以后只需在新的项目中对这些模型片段细化派生，即可快速构建出适合于该项目的数据库架构。不过，这些建模模型片段并没有统一，形成标准，目前也没有出版这类的书籍。本人正在陆续总结自己在这方面的经验，但是自知水平有限，不敢在高人面前班门弄斧，只希望自己日后陆续发布的相关文章能起到抛转引玉的作用，争取由中国的程序员率先统一出数据建模领域的“设计模式”。</p>
<img src ="http://www.blogjava.net/weibogao/aggbug/93122.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/weibogao/" target="_blank">weibogao</a> 2007-01-11 12:01 <a href="http://www.blogjava.net/weibogao/archive/2007/01/11/93122.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>转《公文转发流程自定义的数据建模 》</title><link>http://www.blogjava.net/weibogao/archive/2007/01/11/93120.html</link><dc:creator>weibogao</dc:creator><author>weibogao</author><pubDate>Thu, 11 Jan 2007 03:56:00 GMT</pubDate><guid>http://www.blogjava.net/weibogao/archive/2007/01/11/93120.html</guid><wfw:comment>http://www.blogjava.net/weibogao/comments/93120.html</wfw:comment><comments>http://www.blogjava.net/weibogao/archive/2007/01/11/93120.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/weibogao/comments/commentRss/93120.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/weibogao/services/trackbacks/93120.html</trackback:ping><description><![CDATA[
		<h3 class="title"> </h3>
		<p>　　开发比较复杂的企业多用户管理信息系统(MIS)，不可能不涉及到系统内多个用户之间的数据文件的流转、审批等功能的开发。由于企业的需求总是随着时间推移不断发生变化，加之各个企业内部所设置的办公流程不尽相同，一套通用性比较好的管理信息系统应该能让系统管理员自己定义公文转发的流程。</p>
		<p>　　尽管笔者没有机会在已参与开发了的MIS中实现出文件转发流程自定义的功能，但是，早在2002年初就曾深入思考过这方面的设计。当时由于某些原因不能公开自己的设计思路，现在市面上已经有不少MIS产品提供这样的功能，笔者又已离职，所以是时候把我的设计思路整理出来，和大家分享。</p>
		<p>　　首先，让我们分析需求，制定目标。</p>
		<p>　　1)一般情况下，企业内的公文转发、审批是按部门或职位来转送，即对岗不对人。例如：某个流程的某个环节需要财务总监审批，日后财务总监换人，该流程应该不受影响。而且，流程中某个环节可能出现某个部门中的任何一人都能审批，或者需要该部门的所有人员共同审批。<br />　　2)流程中转送，审批的公文一般分为文件和表单2种格式。文件格式的公文应该支持批处理，即一次可以转发多个文件，审批时可以只退回其中某一个不合格的文件，其他的文件可以转送到下一个环节继续处理。表单格式的公文应该能让用户自己定义表单格式，确定表单中的表项。同理，表单也应该支持批处理。<br />　　3)流程中处理公文的动作应该能让用户自己定义。这样一旦日后增加了新的处理动作，也不用修改MIS系统的底层数据建模。当然，要实现新的处理动作，还是需要在业务逻辑层编写相应的代码，不过和修改底层数据建模比起来，工作量要少得多。<br />　　4)每个流程的环节数不一定相同，应该能让用户设定环节数，指定公文流转中每个环节的发送部门和接受部门，处理模式，最长等待时间。<br />　　5)当待处理的公文发出后，系统应该在等待时间中定期向该流程中下个环节的用户(们)发出通知，提醒该用户(们)及时处理，直至公文已被处理。如果超出最长等待时间，公文还未被用户(们)处理，此次流程处理失败。企业管理层可能会要求记录相关信息，以便在日后业务流程重组(BPR)时参考。<br />　　6)某些企业由于特殊原因，在某个流程中要求实现跨环节处理。例如，该流程有6步，执行到第二个环节时要求处理后可以跳过中间三个环节，直接转到最后一个环节等候处理。其实，这种情况下，并不一定要在技术层面上实现其灵活性，这种特例毕竟是少数。用户只需定义一个新流程，把上面流程的第1，2，6步复制加入进来，2个流程之间用流程名来区分即可。一个优秀的系统架构设计师应该充分利用现有的工具，不要什么都自行架设开发。</p>
		<p>　　上面的需求对灵活性要求较高，抽象化程度较深，所以在表现层和业务逻辑层的开发量较大，初期投资较多，不过开发完毕后估计不需对底层数据库修改，即可满足日后不断变化的公文流转需求。如果不需要这么高的灵活性，可以按实际项目简化某些假设条件。下面按照上面的需求进行用例(use case)分析和数据建模。</p>
		<p>　　1)由于流程环节的发送方和接受方是对岗不对人，我们应该先描画出整个企业的机构设置，确定每个部门的权利职责。其中大的部门内可能有若干子部门，每个子部门内又有不同职位，负责处理相应的事务。所以，可先建立一个树形关系的数据表来保存企业结构，然后，采用权限表和用户组相结合的方式来保存每个部门每个职位的职能。这块的设计思路见我之前发布的“浅谈数据库设计技巧(上)、(下)”，我在下面直接给出大致的数据表结构：</p>
		<p>部门表(Department_table)<br />名称　　　　类型　　　　约束条件　　　 说明<br />Dp_id 　 int 　 无重复　　 类别标识，主键<br />Dp_name　　 varchar(50) 不允许为空 类型名称，不允许重复<br />Dp_father int 不允许为空 该类别的父类别标识，如果是顶节点的话设定为某个唯一值<br />Dp_layer varchar(6) 限定3层,初始值为000000 类别的先序遍历，主要为减少检索数据库的次数</p>
		<p>功能表(Function_table)<br />名称　　　　类型　　　　约束条件　　　说明<br />f_id int 　 无重复　　 功能标识，主键<br />f_name varchar(20) 不允许为空 功能名称，不允许重复<br />f_desc varchar(50) 允许为空 功能描述</p>
		<p>用户组表(User_group)<br />名称　　　　类型　　　　 约束条件　　　说明<br />group_id int 无重复 用户组标识，主键<br />group_name varchar(20) 不允许为空 用户组名称<br />group_power varchar(100) 不允许为空 用户组权限表，内容为功能表f_id的集合</p>
		<p>用户表(User_table)<br />名称　　　　类型　　　　约束条件　　　说明<br />user_id int 无重复 用户标识，主键<br />user_name varchar(20) 无重复 用户名<br />user_pwd varchar(20) 不允许为空 用户密码<br />user_type int 不允许为空 所属用户组标识，和User_group.group_id关联</p>
		<p>　　说明：其中，按部门的不同职位设置不同权限的用户组，如某个用户组为“市场部业务员”，该用户组的用户可在流程“报销申请”中发送报销申请。</p>
		<p>　　2)尽管流程中的公文分为文件和表单2种格式，但是每个文件/表单都应该有其唯一标识，名称等属性。所以，我们把公文抽象化，把这2种格式的公文的共有属性提取出来建立一张公文表。</p>
		<p>公文表(Document_table)<br />名称　　　　类型　　　　约束条件　　　说明<br />doc_id int 无重复 公文标识，主键<br />doc_name varchar(50) 不允许为空 公文名称<br />doc_type char(1) 不允许为空 公文类型</p>
		<p>　　doc_type字段用来辨别公文格式，目前只有2种格式，可设“1”表示文件格式，“2”表示表单格式。估计未来新增公文格式不会太多，所以该字段只需一位字符。文件格式的公文一般是在文件内固定好格式，我们可用一个二进制的字段直接保存整个文件的内容。文件格式的公文需要建一个表来保存相关信息，其大致数据表如下：</p>
		<p>文件表(File_table)<br />名称　　　 类型　　　　约束条件　　　说明<br />file_id int 无重复 文件标识，主键<br />file_name varchar(50) 不允许为空 文件名称<br />file_value binary 不允许为空 文件内容<br />……</p>
		<p>　　表单格式的公文要让用户自己定义表单格式，确定表单中的表项。有两种方法来实现：<br />　　①每当用户建立一个新格式的表单时，就新建立一个表，把用户输入的表单表项当作该表的字段。这种方式的优点是表单查询速度较快方便，业务逻辑层的开发量较小。缺点是不太灵活，如果企业所使用的不同格式的表单较多(&gt;20种)，整个数据库的结构显得比较混乱，而且大部分表单中都有相同的字段，这样也增加了数据冗余。这种方式的数据建模如下：</p>
		<p>表单总表(Sheet_table)<br />名称　　　　类型　　　　约束条件　　　说明<br />sheet_id int 无重复 表单标识，主键<br />sheet_name varchar(50) 不允许为空 表单名称<br />table_name varchar(20) 不允许为空 表单子表名，如Sub_table1/Sub_table2</p>
		<p>表单子表1(Sub_table1)<br />名称　　　类型　　　约束条件　　　说明<br />sub_id int 无重复 表单子表标识，主键<br />option1 varchar 不允许为空 表单表项1<br />option2 varchar 不允许为空 表单表项2<br />option3 varchar 不允许为空 表单表项3<br />……</p>
		<p>表单子表2(Sub_table2)<br />名称　　　类型　　　约束条件　　　说明<br />sub_id int 无重复 表单子表标识，主键<br />option1 varchar 不允许为空 表单表项1<br />option2 varchar 不允许为空 表单表项2<br />option3 varchar 不允许为空 表单表项3<br />……</p>
		<p>……</p>
		<p>　　②对表单再进行一个抽象，把表单看成由若干个表单表项所组合成的一个集合。这种方式的优点是相当灵活，用户建立新格式的表单时只用从已有表单表项中勾选出需要的表项即可，而且整个数据库结构清晰，没有数据冗余。缺点是开发比较复杂，工作量和上面相比高出不少，而且表单查询速度较慢。下面是这种方式的数据建模：</p>
		<p>表单总表(Sheet_table)<br />名称　　　　类型　　　　约束条件　　　说明<br />sheet_id int 无重复 表单标识，主键<br />sheet_name varchar(50) 不允许为空 表单名称</p>
		<p>表单表项表(Option_table)<br />名称　　 类型　　　　约束条件　　　说明<br />op_id int 无重复 表单表项标识，主键<br />op_name varchar(50) 不允许为空 表单表项名称<br />op_length int 不允许为空 表单表项长度<br />op_unit varchar(10) 允许为空 表单表项单位</p>
		<p>表单信息表(Sheetinfo_table)<br />名称　　　 类型　　　　约束条件　　 说明<br />info_id int 　 无重复 　 表单信息标识，主键<br />sheet_id int 不允许为空 所属表单标识，和Sheet_table.sheet_id关联<br />op_id　 int 不允许为空 表单表项标识，和Option_table.op_id关联<br />info_value varchar() 不允许为空 表单信息值</p>
		<p>　　3)我们可以把公文转发的流程抽象化，看作一个实体超类。建表如下：</p>
		<p>流程表(Flow_table)<br />名称　　　　 类型　　　　 约束条件　　　说明<br />flow_id int 无重复 流程标识，主键<br />flow_name varchar(50) 不允许为空 流程名称<br />flow_stepnum int 不允许为空 流程步数<br />flow_desc varchar(200) 允许为空 流程描述</p>
		<p>　　流程中的每一步都可以抽象化成从发送方至接受方的用例，其数据建模大致如下：</p>
		<p>处理动作表(Action_table)<br />名称　　类型　　　　 约束条件　　　说明<br />a_id int 无重复 动作标识，主键<br />a_name varchar(20) 不允许为空 动作名称<br />a_call varchar(50) 不允许为空 动作所调用的模块<br />a_desc varchar(200) 允许为空 动作描述</p>
		<p>　　说明：如果采用面向过程的开发方式，如纯脚本语言，可以把每一个处理动作写成一个函数，调用a_call字段记录的函数，即可完成相应处理动作。如果采用面向对象的开发方式，可以用COM组件来封装处理动作，则a_call用来记录相应的COM组件的接口方法。如果是在.NET Framework环境下，可以采用Web服务的方式。当然，发送方、接受方以及公文标识是作为输入参数的。</p>
		<p>流程环节表(Step_table)<br />名称　　　 类型　　 约束条件　　　说明<br />step_id int 无重复 环节标识，主键<br />belong int 不允许为空 所属流程标识，和Flow_table.flow_id关联<br />setp_order int 不允许为空 所属流程的步骤次序<br />sender int 不允许为空 发送方标识，和User_group.group_id关联<br />receiver int 不允许为空 接受方标识，和User_group.group_id关联<br />a_id int 不允许为空 处理动作标识，和Action_table.a_id关联<br />a_type int 不允许为空 接受方所需的处理人数<br />max_wait int 不允许为空 最长等待时间<br />wait_unit varchar(5) 不允许为空 等待时间的单位</p>
		<p>　　说明：a_type用来确定接受方所需的处理人数，“0”表示需同职位的所有人一起处理，“1”表示只需该职位的任意一名员工处理，“2”表示需该职位的任意两名员工一起处理，依次递推……一起处理的方式和处理动作有关，例如是投票方式，少数服从多数，还是某人有一票否决权等等。可能针对某些处理动作还得细化，进行相关的数据建模，这里我就不细分下去了。</p>
		<p>　　4)下面分析公文转发的流程环节记录。此时相当于实例化一个流程环节的对象，发送方和接受方应具体联系到管理信息系统的某个(些)用户，而不是某个用户组。每经过一环节，我们除了要保存这方面的信息，还必须保存该环节所转发的公文，以及处理状况等信息。而且，该环节所转发公文数量大于等于一，所以可以参考我之前发布的“浅谈数据库设计技巧(下)”中的“简洁的批量m:n设计”，建表大致如下：</p>
		<p>流程环节记录表(Step_log)<br />名称　　　类型　　 约束条件　　　说明<br />log_id int 无重复 环节记录标识，主键<br />step_id int 不允许为空 环节标识，和Step_table.step_id关联<br />sender varchar(100) 不允许为空 发送用户标识，相关用户组的User_table.user_id的集合<br />receiver varchar(100) 不允许为空 接受用户标识，相关用户组的User_table.user_id的集合<br />doc_id int 不允许为空 转发公文标识，和Document_table.doc_id关联<br />batch_no int 不允许为空 批量转发公文编号，同一流程环节转发的batch_no相同<br />state char(1) 不允许为空 处理状态<br />sub_date datetime 不允许为空 提交时间<br />res_date datetime 允许为空 处理回复时间<br />comment varchar(255) 允许为空　　　处理回复注释</p>
		<p>　　说明：<br />　　①同一流程环节转发的batch_no和该批第一条入库的log_id相同。举例：假设当前最大log_id是64，接着某用户一次转发了3件公文，则批量插入的3条流程环节记录的batch_no都是65。之后另外一个用户通过某个流程环节转发了一件公文，再插入流程环节记录的batch_id是68。<br />　　②state字段用来描述其流程环节所处的状态，是正待处理，已被处理通过，已被处理驳回，还是超出最长等待时间被系统自动收回等等。通过这个字段我们对接受用户发出处理通知，还可以可以很容易的查询出所有超出最长等待时间被系统自动收回的流程，以便企业管理层在日后业务流程重组(BPR)时参考。<br />　　③如果某份公文在某个流程中的某个环节被处理驳回，可以看作该公文在此次流程中被驳回至起始点，最初发送用户可根据处理回复注释修改公文后重新发送。</p>
		<p>
				<br />　　总结：<br />　　企业公文流程自定义应该是把企业内已经固定了的公文转发、审批流程电子化，实现高效的无纸化办公，对于非正式的口头讨论、商议、集会等商务活动并不适合。当企业累积了一定数量的电子化公文转发的记录后，可以在商业咨询专家和技术开发人员的协助下对其进行数据挖掘，分析出其中的低效、无用环节，进行优化重组，最终提高整个企业的竞争力。作为技术开发人员，我们应该根据企业实际运作情况、资金投入规模，选择当前时期最适合的技术解决方案，切不可为了展示自己的技术实力，而把开发复杂化，企业开发并不是追求技术最先进，而且最适合。</p>
<img src ="http://www.blogjava.net/weibogao/aggbug/93120.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/weibogao/" target="_blank">weibogao</a> 2007-01-11 11:56 <a href="http://www.blogjava.net/weibogao/archive/2007/01/11/93120.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>转《商品销售打折自定义的数据建模》</title><link>http://www.blogjava.net/weibogao/archive/2007/01/11/93119.html</link><dc:creator>weibogao</dc:creator><author>weibogao</author><pubDate>Thu, 11 Jan 2007 03:50:00 GMT</pubDate><guid>http://www.blogjava.net/weibogao/archive/2007/01/11/93119.html</guid><wfw:comment>http://www.blogjava.net/weibogao/comments/93119.html</wfw:comment><comments>http://www.blogjava.net/weibogao/archive/2007/01/11/93119.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/weibogao/comments/commentRss/93119.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/weibogao/services/trackbacks/93119.html</trackback:ping><description><![CDATA[
		<h3 class="title"> </h3>
		<p>　　近期看了idilent的文章《使用面向对象技术解决商品打折问题》，文后有读者提出要求：如果不同商品的折扣不同怎么办？ 或者有买一百送五十这种方式，或不同会员等级的折扣不同。 怎么处理？”idilent认为打折这个问题并不是能够通过一个数据库的字段就可以解决的。有不同的会员，不同的产品，不同的销售计划，而这些也是在不停的不变化和增加的。而会员和产品的打折，以及店庆等打折，虽然都是折扣，但是很难抽象成数据库中的一个字段或者几个字段，不使用程序解决，而希望只是通过改变数据库中的数据，在目前阶段实现起来可能还比较困难。</p>
		<p>　　之前我曾参与过一个影碟出租销售管理系统的项目开发，负责其中的架构设计和数据建模工作，尽管最后该项目由于某些客观原因而被放弃，但是该项目中也有打折优惠这方面的功能需求，我也思考过这一块的数据建模。其实，我们可以把商品销售打折这样的商务规则分解成几个部分，分析各个部分之间的关系，从中找出关键点，再将其泛化数据建模，即可实现让用户自己定义打折规则。下面开始分析商品销售打折的商业规则：</p>
		<p>　　一套商品销售管理信息系统，必定存在下面两个实体：顾客，商品，打折这种商业规则一定是围绕着这两个实体以及相互间的关系而制定的。回顾我们的购物经历，打折的需求应该可以分为三种：<br />　　1)对特定商品的折扣一般有如下几种情况：按售价进行一定百分比的打折；原价-&gt;特价(某个时间段内进行的)的打折；捆绑优惠销售(如购买某一(几)种商品后即可按较低的价格或折扣购买另一(几)种商品)。<br />　　2)对顾客的的打折方式一般采用会员制，即是按会员等级在交费时直接给与一定的折扣优惠，或者在会员累积消费一定金额后给以一定比例的返点优惠，该方式需要顾客办理会员卡之类的身份标识卡。<br />　　3)对总额的打折方式一般是顾客消费后送出的限定最后使用期限的代金券。</p>
		<p>　　注意到上面三大类折扣方式分解开来，都离不开商品，所以这三种打折方式都是商品的共有属性，应该归入到商品表中。另外，一般大型超市多拥有多个分店，而且可能出现各个分店的打折规则略有不同的情况，上面的三种打折规则得同相应的分店一一对应。最后，数据建模大致如下：</p>
		<p>店铺表(Shop)<br />名称 类型 约束条件 说明<br />shop_id int 无重复 店铺标识，主键<br />shop_name varchar(40) 不允许为空 店铺名称<br />shop_addr varchar(80) 不允许为空 店铺地址<br />……</p>
		<p>商品类别表(Ware_type)<br />名称 类型 约束条件 说明<br />type_id int 无重复 商品类别标识，主键<br />type_name varchar(20) 不允许为空 商品类别名称<br />father int 不允许为空 该类别的父类别标识，如果是顶节点的话设定为某个唯一值<br />layer char(6) 限定3层,初始值为000000 类别的先序遍历，主要为减少检索数据库的次数</p>
		<p>商品表(Ware)<br />名称 类型 约束条件 说明<br />ware_id int 无重复 商品标识，主键<br />ware_type int 不允许为空 所属商品类别，和Ware_type.type_id关联<br />ware_name varchar(40) 不允许为空 商品名称<br />buy_price float 不允许为空 进货价<br />sell_price float 不允许为空 销售价<br />d_type char(1) 不允许为空 商品打折方式<br />m_type char(1) 不允许为空 会员打折方式<br />has_coupon bit 默认值为0 是否有代金券<br />……</p>
		<p>　　说明：<br />　　①d_type用来辨别该商品的商品打折方式。"0"表示该商品无商品折扣方式；"1"表示该商品采用百分比打折方式；"2"表示该商品采用特价打折方式；"3"表示该商品采用捆绑打折方式，是捆绑打折规则中的必购商品；"4"表示该商品采用捆绑打折方式，是捆绑打折规则中的允购商品。<br />　　②m_type用来辨别该商品的会员打折方式。"0"表示该商品不参与会员折扣计算；"1"表示该商品采取会员百分比折扣方式；"2"表示该商品采取会员卡累积消费返点折扣方式。<br />　　③has_coupon用来指明该商品是否有代金券。"0"表示该商品无代金券；"1"反之。</p>
		<p>商品库存表(Store_table)<br />名称 类型 约束条件 说明<br />store_id int 无重复 库存标识，主键<br />shop_id int 不允许为空 店铺标识，和Shop.shop_id关联<br />ware_id int 不允许为空 商品标识，和Ware.ware_id关联<br />number int 默认值为0 店铺库存数量<br />unit varchar(10) 不允许为空 销售单位</p>
		<p>商品折扣规则表(Discount)<br />名称 类型 约束条件 说明<br />id int 无重复 折扣规则标识，主键<br />s_id int 不允许为空 店铺标识，和Shop.shop_id关联<br />w_id int 不允许为空 商品标识，和Ware.ware_id关联<br />d_value float 不允许为空 打折数值，用来记录百分比或特价<br />enddate datetime 不允许为空 该规则的终止日期<br />number int 允许为空 该规则所允许的最大销量<br />unit varchar(10) 允许为空 销售单位</p>
		<p>商品捆绑打折表(Bind_discount)<br />名称 类型 约束条件 说明<br />b_id int 无重复 捆绑打折规则标识，主键 <br />shop_id int 不允许为空 店铺标识，和Shop.shop_id关联<br />1st_ware int 不允许为空 必购的商品标识的集合，和Ware.ware_id关联<br />min_req int 默认值为1 最小必购数量<br />2nd_ware int 不允许为空 允购的商品标识的集合，和Ware.ware_id关联<br />max_buy int 默认值为1 最大允购数量<br />d_type char(1) 不允许为空 打折方式，是百分比方式还是特价方式<br />d_value float 不允许为空 打折数值，用来记录百分比或特价<br />enddate datetime 不允许为空 该规则的终止日期<br />number int 允许为空 该规则所允许的最大销量<br />unit varchar(10) 允许为空 销售单位</p>
		<p>　　说明：1st_ware用来记录必购商品的集合，min_req表示在必购商品集合内的最小购买数量。2nd_ware用来记录允购商品的集合，max_buy表示达到必购商品的最小购买数量后，所允许购买的允购商品的最大允购数量。举例说明：某捆绑销售规定，凡是购买了某系列商品中的任意1件，即可按特价购买允购商品中的任意1款1件。这种促销方式大家都见过吧？买二送一不过是其中的特例罢了。</p>
		<p>会员等级表(Member_type)<br />名称 类型 约束条件 说明<br />type_id int 无重复 会员等级标识，主键 <br />s_id int 不允许为空 店铺标识，和Shop.shop_id关联<br />type_name varchar(10) 不允许为空 会员等级名称<br />t_value float 不允许为空 打折百分比或累积消费返点数<br />condition float 不允许为空 达到该等级所需累积的消费额</p>
		<p>会员表(Member)<br />名称 类型 约束条件 说明<br />m_id int 无重复 会员标识，主键 <br />m_name varchar(10) 不允许为空 会员姓名<br />type_id int 不允许为空 会员等级标识，和Member_type.type_id关联<br />score float 默认值为0 会员累积的消费积分<br />……</p>
		<p>代金券表(Coupon)<br />名称 类型 约束条件 说明<br />c_id int 无重复 代金券标识，主键 <br />c_name varchra(20) 不允许为空 代金券姓名<br />c_value float 不允许为空 代金数额<br />condition float 不允许为空 所需现金消费<br />enddate datetime 不允许为空 代金券的终止日期</p>
		<p>　　当然，由于本人所认知的打折方式并不全面，也没有和相关的业务人士深入讨论过这方面的问题。所以，上面的数据建模并不能保证覆盖现实商品销售中的的所有打折方式。不过，我相信，采用上面的数据建模来定义打折规则，覆盖率还是在90%以上的。根据95/5规则，只要给我足够的时间，再加上专业人士的协助，不计开发成本的话，100%的覆盖率是可以达到的^-^</p>
		<p>　　最后，由于每张购物清单都是由商品组成，而每种商品的折扣的计算规则并不一定完全相同，所以我认为在用面向对象的设计方法，设计计算折扣的组件时，采用装饰(Decorator)模式比较适合。</p>
<img src ="http://www.blogjava.net/weibogao/aggbug/93119.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/weibogao/" target="_blank">weibogao</a> 2007-01-11 11:50 <a href="http://www.blogjava.net/weibogao/archive/2007/01/11/93119.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>转《Powerdesigner使用建议 》</title><link>http://www.blogjava.net/weibogao/archive/2007/01/11/93111.html</link><dc:creator>weibogao</dc:creator><author>weibogao</author><pubDate>Thu, 11 Jan 2007 03:34:00 GMT</pubDate><guid>http://www.blogjava.net/weibogao/archive/2007/01/11/93111.html</guid><wfw:comment>http://www.blogjava.net/weibogao/comments/93111.html</wfw:comment><comments>http://www.blogjava.net/weibogao/archive/2007/01/11/93111.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/weibogao/comments/commentRss/93111.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/weibogao/services/trackbacks/93111.html</trackback:ping><description><![CDATA[
		<p>
				<br />1.1业务规则的使用(Business Rule)<br />对于一些业务逻辑可能出现在多个数据表中，建议封装成Business Rule，这样便于业务逻辑的重新使用，也便于业务逻辑的维护。</p>
		<p>为了便于维护业务逻辑，可以考虑将Business Rule和Domains结合起来使用。将业务Business Rule应用到Domains上，然后再把Domains应用到数据表的字段上。</p>
		<p>例如：在拆迁项目中，拆迁业务部分，管理参数业务部分，房源业务部分，拆迁合同部分的数据表中都有楼层这个字段，因此先一个Business Rule，然后定义一个Domain，这样相应的数据表的字段就可以使用这个Domain了。</p>
		<p>1.2.自定义数据类型(Domains)的使用<br />oralce提供了一些内置的数据类型，但是用户也可以根据业务的需要，定义自定义的数据类型。</p>
		<p>在自定义数据类型里面包装业务逻辑。</p>
		<p>正如上面的房屋楼层，我们可以定义一个独立的数据类型(Domain)维护，然后在相关数据表的</p>
		<p>字段上使用这个自定义数据类型。</p>
		<p>一般在定义自己的数据类型时候，可以在oracle基本类型上定义，然后可以加上一些standard check或者Business Rules。</p>
		<p>比如：在拆迁项目中，面积类别这个字段在很多数据表都出现了，可以作为一个单独的数据类型类维护，定义一个” 面积类别” Domains（包含的种类有：0 --- 厅房面积,1 --- 使用面积,2 --- 单元面积,,3 --- 总建筑面积,4 --- 分摊面积）。而且由于Powerdesigner的提供关联作用,这样便于当业务逻辑发生了变动，能够很快查询出那些对象受到影响。</p>
		<p>1.3序列号(Sequence)的使用 <br />在powersigner的模型里面定义一堆了Sequence，接下来的是要把他们和数据表的相关字段关联起来，特别是那些用于多个数据表字段的Sequence。</p>
		<p>一个数据表原则上只允许一个字段使用Sequence，并且在数据表的字段使用Sequence前，应该把该Sequence添加到数据表的Extended Dependencies中。</p>
		<p>如果一个数据表有2个字段或者更多字段使用了Sequence，那模型检查时会给出提示信息。</p>
		<p>使用的规则一般是只能应用到数据表的主键字段上。</p>
		<p>主键字段建议是 数据表+“ID“或者 “编号“构成。</p>
		<p>例如：“房屋整合面积“ 数据表，那它的主键字段=房屋整合面积编号，对应的Sequence为</p>
		<p>SEQ_房屋整合面积。其它数据表可能也使用到了这个Sequence，那也需要在使用前设置引用关系。</p>
		<p>（在数据表的Extended Dependencies 上设置引用关系）</p>
		<p>1.4 Oracle Package的使用<br />在Oracle Package里面可以定一些procedure ，但是Oracle包引用的数据库对象到底有哪些呢，这些信息建议手动维护起来。特别是Oracle Package使用了哪些数据表，视图，以及Oracle Packag等信息建议维护起来。</p>
		<p>1.5包的使用<br />PowerDesigner的包相当于文件夹。用户可以把它当作一个维护业务逻辑的容器。PowerDesigner包一般建议按照业务模块来建立。如果模块需要细分，可以考虑建立PowerDesigner子包来完成。</p>
		<p>建议容器里保存的是模型对象的快捷方式。原始信息建议不要放到容器里面。因为在要是把这些信息放到容器里，在PowerDesigner的模型合并或者逆向工程时，这种方式的信息可能得不到维护。</p>
		<p>PowerDesigner的包下面的PhysicalDiagram，建议采用象ERWin的Subject Area那样，按照某个主题或者业务角度的方式来组织PhysicalDiagram包含的对象，使得每个PhysicalDiagram的功能明确。</p>
		<p>
		</p>
		<p>1.6.视图(View)的使用<br />视图一般是数据表或者视图上建立得来的（当然也可能引用了某个存储过程）。一般视图的模型中应该维护视图的数据来源的引用信息。</p>
		<p>在我们现在的项目中数据库模型没有对视图进行维护，为此需要在建立视图的Powerdesigner</p>
		<p>模型。</p>
		<p>我在Powerdesigner9.5环境下通过逆向工程不能够获得视图(view)的脚本，通过修改相关配</p>
		<p>置参数，还是不能够获得脚本。</p>
		<p>可以通过以下2方法获得视图(view)的脚本。</p>
		<p>方法1：使用powerdesigner8.0的逆向工程获得视图的脚本，然后在Powerdesigner9.5中把视</p>
		<p>图的模型合并进来，这样就可以对视图进行维护了。</p>
		<p>方法2：使用Erwin逆向工程获得视图的Erwin模型，然后再把模型保存为ERX类型的文件</p>
		<p>在Powerdesigner9.5中导入该文件，然后进行合并模型就可以了</p>
		<p>PowerDesigner的视图模型处理能力比较差，不能构维护视图的依赖关系(也就是建立视图对数据源的依赖关系)，这一点明显不如ERWin。</p>
		<p>
		</p>
		<p>1.7.同义词（synonym）的使用<br />同义词相当于给数据库对象一个别名，提供了位置和数据的独立性。在跨数据库用户访问对象时，可以考虑建立同义词结合权限分配，简化数据库对象的访问。</p>
		<p>
		</p>
		<p>1.8．数据表的使用<br />数据表的注释语句的更新。</p>
		<p>业务背景：</p>
		<p>在我们的项目中，Erwin模型中的数据表的注释语句没有同步到Oracle数据库。现在需要更数据库中的数据表的注释语句。</p>
		<p>可能可以采取的实现方法：</p>
		<p>方法1：Erwin直接正向工程，但是从Erwin直接正向工程由于注释语句中有回车符号，更新会失败。</p>
		<p>方法2：如果把Erwin模型转换成为powerdesigner模型再更新数据表的注释语句，这样就可以避免回车符号的问题，按正常情况是可以行得通的，但是由于Erwin模型中的逻辑模型和物理模型不一致，甚至它们出现的顺序不一致，这样获得powerdesigner模型就不正确了，生成的修改数据库的脚本也就不正确了。</p>
		<p>实际采用的方法：</p>
		<p>把Erwin模型转换成powerdesigner模型在Erwin中保存为ERX类型，然后在PowerDesigner导入模型），并且把文件保存为PDM类型（XML格式），删除模型中的视图，domains,Business Rule,reference等信息，只留下相关数据表本身的信息，然后把模型文件的后缀修改XML，并且采用XMLSPY生成这个文件的DTD文件，再采用Java编写了一个基于SAX的程序去解析XML文件，把各个数据表以及字段的注释语句提取出来，然后更新数据库中数据表和字段的注释语句，这样就可以了。</p>
		<p>
		</p>
		<p>1.9．ERWin升级到PowerDesigner的相关问题<br />1.9.1 Domain的升级<br />从Erwin3.52升级到PowerDesigner9.5时，Domain信息和数据表的关联关系会丢失，需要手动重新添加2者间的关系。当然可以通过编程修改PowerDesigner的模型文件，添加2者之间的关联关系。一般的PowerDesigner模型文件较大，只要有个几十张数据表肯定模型文件有1MB，建议采用SAX的方式添加信息。</p>
		<p>注意：添加数据表字段使用的Domain时候，需要设置数据表对Domain的引用关系（也就是Extended Dependencies）。</p>
		<p>1.9.2 Business Rule的升级<br />从Erwin3.52升级到Powerdesigner9.5，Business Rule的表达式(脚本)需要修改的，把所有的</p>
		<p>Business Rule的表达式中的@column 修改成%COLUMN% </p>
		<p>具体实现的方式，可以直接在Powerdesigner9.5里面修改；或者把模型保存为XML格式（文件类为 .pdm）,通过UltraEdit或者XMLSpy等工具来修改，一个查找替换旧搞定了。当然的注意</p>
		<p>只能修改<?XML:NAMESPACE PREFIX = C /?><c:businessrules></c:businessrules>里面的内容，否则会修改一些不应该修改的地方。</p>
		<p>同Domain一样，从Erwin3.52升级到PowerDesigner9.5时，Business信息和数据表的关联关系也会丢失。如果Business Rule 不是太多建议手动修改模型文件。</p>
		<p>
		</p>
		<p>1.9.3.Sequence的升级<br />.Sequence的升级建议采用和Domain的方式，编程实现维护。</p>
		<p>1.9.4.物理图的升级<br />从Erwin3.52升级到Powerdesigner9.5，物理图同样能够倒入Powerdesigner9.5中，但是Powerdesigner9.5的升级功能有些问题：在生成的物理图中数据表的信息有些问题：物理图中的数据表的字段显示不完全，而且很多时候数据表字段的类型都不能显示完全。我使用java采用sax的方式把升级后的模型文件进行解析，然后重新生成物理图中数据表的位置信息（数据表的2个坐标：左上角坐标，右下角坐标）；另外根据业务需要可以生成自己的Powerdesigner9.5包并且可以创建物理图，把数据表添加到物理图上。</p>
		<p>
		</p>
		<p>1.9.5.其他说明<br />从Erwin3.52升级到Powerdesigner9.5，我写了一些java程序解决了相关问题，如果哪位同行遇到相似的问题</p>
		<p>可以交流一下。</p>
		<p>2．关于powerdesigner中的数据结构的变更管理<br />目前拆迁项目中数据结构的有些失控，在结合powerdesigner包的概念的基础山上提出如下一些建议。</p>
		<p>2.1．数据结构按照业务模块进行维护<br />模型中所有的数据结构都在一个文件中，而且在顶层文件夹中各个业务模块维护的是数据结构的快捷方式。</p>
		<p>2.2．数据结构按照其生命周期进行分类管理。<br />在各个业务模块的包下面建立如下的包：</p>
		<p>2.2.1临时测试数据结构：<br />是一些当前业务模块测试时使用的数据结构，可以随时被删除</p>
		<p>2.2.2讨论中数据结构：<br />是数据结构处于讨论中，还没有确定下来。</p>
		<p>2.2.3需要更新的数据结构：<br />是数据结构已经确定下来，但是还没有更新到数据库中。</p>
		<p>2.2.4正式数据结构：<br />在数据库中被业务正常使用的数据结构</p>
		<p>2.2.5作废中的数据结构：<br />在数据库中以前被业务正常使用，现在已经不再使用，但是还没有进行</p>
		<p>被作废的数据表中数据的迁移，没有完全作废的数据结构。如果要把这些数据结构进行作废，</p>
		<p>需要先进行数据迁移，以及其他相关处理。</p>
		<p>2.2.6已经作废的数据结构：<br />在数据库已经不再被使用的业务数据表，相关的数据迁移已经完成，但是数据表还没有删除，</p>
		<p>相关的文档没有更新。</p>
<img src ="http://www.blogjava.net/weibogao/aggbug/93111.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/weibogao/" target="_blank">weibogao</a> 2007-01-11 11:34 <a href="http://www.blogjava.net/weibogao/archive/2007/01/11/93111.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>关于SQLServer的若干注意事项</title><link>http://www.blogjava.net/weibogao/archive/2006/07/12/57854.html</link><dc:creator>weibogao</dc:creator><author>weibogao</author><pubDate>Wed, 12 Jul 2006 11:28:00 GMT</pubDate><guid>http://www.blogjava.net/weibogao/archive/2006/07/12/57854.html</guid><wfw:comment>http://www.blogjava.net/weibogao/comments/57854.html</wfw:comment><comments>http://www.blogjava.net/weibogao/archive/2006/07/12/57854.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/weibogao/comments/commentRss/57854.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/weibogao/services/trackbacks/57854.html</trackback:ping><description><![CDATA[如果你正在负责一个基于SQL Server的项目, 或者你刚刚接触SQL Server, 你都有可能要面临一些数据库性能的问题, 这篇文章会为你提供一些有用的指导( 其中大多数也可以用于其它的DBMS) . <br /><br />在这里, 我不打算介绍使用SQL Server的窍门, 也不能提供一个包治百病的方案, 我所做的是总结一些经验----关于如何形成一个好的设计. 这些经验来自我过去几年中经受的教训, 一直来, 我看到许多同样的设计错误被一次又一次的重复. <br /><br /><strong>你了解你的工具吗? </strong><br /><br />不要轻视这一点, 这是我在这篇文章中讲述的最关键的一条. 也许你也看到有很多的SQL Server程序员没有掌握全部的T-SQL命令和SQL Server提供的那些有用的工具. <br /><br />“什么? 我要浪费一个月的时间来学习那些我永远也不会用到的SQL命令? ”, 你也许会这样说. 对的, 你不需要这样做. 但是你应该用一个周末浏览所有的T-SQL命令. 在这里, 你的任务是了解, 将来, 当你设计一个查询时, 你会记起来: “对了, 这里有一个命令可以完全实现我需要的功能”, 于是, 到MSDN查看这个命令的确切语法. <br /><br /><strong>不要使用游标</strong><br /><br />让我再重复一遍: 不要使用游标. 如果你想破坏整个系统的性能的话, 它们倒是你最有效的首选办法. 大多数的初学者都使用游标, 而没有意识到它们对性能造成的影响. 它们占用内存, 还用它们那些不可思议的方式锁定表, 另外, 它们简直就像蜗牛. 而最糟糕的是, 它们可以使你的DBA所能做的一切性能优化等于没做. 不知你是否知道每执行一次FETCH就等于执行一次SELECT命令? 这意味着如果你的游标有10000条记录, 它将执行10000次SELECT! 如果你使用一组SELECT, UPDATE或者DELETE来完成相应的工作, 那将有效率的多. <br /><br />初学者一般认为使用游标是一种比较熟悉和舒适的编程方式, 可很不幸, 这会导致糟糕的性能. 显然, SQL的总体目的是你要实现什么, 而不是怎样实现. <br /><br />我曾经用T-SQL重写了一个基于游标的存储过程, 那个表只有100,000条记录, 原来的存储过程用了40分钟才执行完毕, 而新的存储过程只用了10秒钟. 在这里, 我想你应该可以看到一个不称职的程序员究竟在干了什么.<br /><br />我们可以写一个小程序来取得和处理数据并且更新数据库, 这样做有时会更有效. 记住: 对于循环, T-SQL无能为力. <br /><br />我再重新提醒一下: 使用游标没有好处. 除了DBA的工作外, 我从来没有看到过使用游标可以有效的完成任何工作. <br /><br /><strong>规范化你的数据表</strong><br /><br />为什么不规范化数据库? 大概有两个借口: 出于性能的考虑和纯粹因为懒惰. 至于第二点, 你迟早得为此付出代价. 而关于性能的问题, 你不需要优化根本就不慢的东西. 我经常看到一些程序员“反规范化”数据库, 他们的理由是“原来的设计太慢了”, 可结果却常常是他们让系统更慢了. DBMS被设计用来处理规范数据库的, 因此, 记住: 按照规范化的要求设计数据库. <br /><br /><strong>不要使用SELECT *</strong><br /><br />这点不太容易做到, 我太了解了, 因为我自己就经常这样干. 可是, 如果在SELECT中指定你所需要的列, 那将会带来以下的好处: <br /><br />1 减少内存耗费和网络的带宽<br /><br />2 你可以得到更安全的设计<br /><br />3 给查询优化器机会从索引读取所有需要的列<br /><br /><strong>了解你将要对数据进行的操作</strong><br /><br />为你的数据库创建一个健壮的索引, 那可是功德一件. 可要做到这一点简直就是一门艺术. 每当你为一个表添加一个索引, SELECT会更快了, 可INSERT和DELETE却大大的变慢了, 因为创建了维护索引需要许多额外的工作. 显然, 这里问题的关键是: 你要对这张表进行什么样的操作. 这个问题不太好把握, 特别是涉及DELETE和UPDATE时, 因为这些语句经常在WHERE部分包含SELECT命令. <br /><br /><strong>不要给“性别”列创建索引</strong><br /><br />首先, 我们必须了解索引是如何加速对表的访问的. 你可以将索引理解为基于一定的标准上对表进行划分的一种方式. 如果你给类似于“性别”这样的列创建了一个索引, 你仅仅是将表划分为两部分: 男和女. 你在处理一个有1,000,000条记录的表, 这样的划分有什么意义? 记住: 维护索引是比较费时的. 当你设计索引时, 请遵循这样的规则: 根据列可能包含不同内容的数目从多到少排列, 比如: 姓名+省份+性别. <br /><br /><strong>使用事务</strong><br /><br />请使用事务, 特别是当查询比较耗时. 如果系统出现问题, 这样做会救你一命的. 一般有些经验的程序员都有体会-----你经常会碰到一些不可预料的情况会导致存储过程崩溃. <br /><br /><strong>小心死锁</strong><br /><br />按照一定的次序来访问你的表. 如果你先锁住表A, 再锁住表B, 那么在所有的存储过程中都要按照这个顺序来锁定它们. 如果你( 不经意的) 某个存储过程中先锁定表B, 再锁定表A, 这可能就会导致一个死锁. 如果锁定顺序没有被预先详细的设计好, 死锁是不太容易被发现的. <br /><br /><strong>不要打开大的数据集</strong><br /><br />在CSDN技术论坛中 :) , 一个经常被提出的问题是: 我怎样才能迅速的将100000条记录添加到ComboBox中? 这是不对的, 你不能也不需要这样做. 很简单, 你的用户要浏览100000条记录才能找到需要的记录, 他一定会诅咒你的. 在这里, 你需要的是一个更好的UI, 你需要为你的用户显示不超过100或200条记录. <br /><br /><strong>不要使用服务器端游标</strong><br /><br />与服务器端游标比起来, 客户端游标可以减少服务器和网络的系统开销, 并且还减少锁定时间. <br /><br /><strong>使用参数查询</strong><br /><br />有时, 我在CSDN技术论坛看到类似这样的问题: “SELECT * FROM a WHERE a.id='A'B, 因为单引号查询发生异常, 我该怎么办? ”, 而普遍的回答是: 用两个单引号代替单引号. 这是错误的. 这样治标不治本, 因为你还会在其他一些字符上遇到这样的问题, 更何况这样会导致严重的bug, 除此以外, 这样做还会使SQL Server的缓冲系统无法发挥应有的作用. 使用参数查询, 釜底抽薪, 这些问题统统不存在了. <br /><br /><strong>在程序编码时使用大数据量的数据库</strong><br /><br />程序员在开发中使用的测试数据库一般数据量都不大, 可经常的是最终用户的数据量都很大. 我们通常的做法是不对的, 原因很简单: 现在硬盘不是很贵, 可为什么性能问题却要等到已经无可挽回的时候才被注意呢? <br /><br /><strong>不要使用INSERT导入大批的数据</strong><br /><br />请不要这样做, 除非那是必须的. 使用UTS或者BCP, 这样你可以一举而兼得灵活性和速度. <br /><br /><strong>注意超时问题</strong><br /><br />查询数据库时, 一般数据库的缺省都比较小, 比如15秒或者30秒. 而有些查询运行时间要比这长, 特别是当数据库的数据量不断变大时. <br /><br /><strong>不要忽略同时修改同一记录的问题</strong><br /><br />有时候, 两个用户会同时修改同一记录, 这样, 后一个修改者修改了前一个修改者的操作, 某些更新就会丢失. 处理这种情况不是很难: 创建一个timestamp字段, 在写入前检查它, 如果允许, 就合并修改, 如果存在冲突, 提示用户. <br /><br /><strong>在细节表中插入纪录时, 不要在主表执行SELECT MAX(ID)</strong><br /><br />这是一个普遍的错误, 当两个用户在同一时间插入数据时, 这会导致错误. 你可以使用SCOPE_IDENTITY, IDENT_CURRENT和@@IDENTITY. 如果可能, 不要使用@@IDENTITY, 因为在有触发器的情况下, 它会引起一些问题( 详见这里的讨论) . <br /><br /><strong>避免将列设为NULLable</strong><br /><br />如果可能的话, 你应该避免将列设为NULLable. 系统会为NULLable列的每一行分配一个额外的字节, 查询时会带来更多的系统开销. 另外, 将列设为NULLable使编码变得复杂, 因为每一次访问这些列时都必须先进行检查. <br /><br />我并不是说NULLS是麻烦的根源, 尽管有些人这样认为. 我认为如果你的业务规则中允许“空数据”, 那么, 将列设为NULLable有时会发挥很好的作用, 但是, 如果在类似下面的情况中使用NULLable, 那简直就是自讨苦吃. <br /><br />CustomerName1<br />CustomerAddress1<br />CustomerEmail1<br />CustomerName2<br />CustomerAddress2<br />CustomerEmail3<br />CustomerName1<br />CustomerAddress2<br />CustomerEmail3<br /><br />如果出现这种情况, 你需要规范化你的表了. <br /><br /><strong>尽量不要使用TEXT数据类型</strong><br /><br />除非你使用TEXT处理一个很大的数据, 否则不要使用它. 因为它不易于查询, 速度慢, 用的不好还会浪费大量的空间. 一般的, VARCHAR可以更好的处理你的数据. <br /><br /><strong>尽量不要使用临时表</strong><br /><br />尽量不要使用临时表, 除非你必须这样做. 一般使用子查询可以代替临时表. 使用临时表会带来系统开销, 如果你是用COM+进行编程, 它还会给你带来很大的麻烦, 因为COM+使用数据库连接池而临时表却自始至终都存在. SQL Server提供了一些替代方案, 比如Table数据类型. <br /><br /><strong>学会分析查询</strong><br /><br />SQL Server查询分析器是你的好伙伴, 通过它你可以了解查询和索引是如何影响性能的. <br /><br /><strong>使用参照完整性</strong><br /><br />定义主健, 唯一性约束和外键, 这样做可以节约大量的时间. <br /><br /><img src ="http://www.blogjava.net/weibogao/aggbug/57854.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/weibogao/" target="_blank">weibogao</a> 2006-07-12 19:28 <a href="http://www.blogjava.net/weibogao/archive/2006/07/12/57854.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>