2006年02月03日

——做人,为什么要过于执著?!
——做人,干吗为难自己?!
——做人,先要相信自己。
——做人,依靠自己!
——做人,量力而行。
——做人,记得反省自己。
——做人,何妨放手一搏。
——做人,要活在当下。

2005年04月03日

7个步骤,7个禁忌和7种思路

作者: TechRepublic.com 来自ZDnet
(在形式上略有改编)

高效实现数据仓库的七个步骤 

数据仓库和我们常见的RDBMS系统有些亲缘关系,但它又有所不同。如果你没有实施过数据仓库,那么从设定目标到给出设计,从创建数据结构到编写数据分析程序,再到面对挑剔的用户的评估,整个过程都会带给你一种与以往的项目完全不同的体验。一句话,如果你试图以旧有的方式创建数据仓库,那你所面对的不是预算超支就是所建立的数据仓库无法良好运作。 

在处理一个数据仓库项目时需要注意的问题很多,但同时也有很多有建设性的参考可以帮助你更顺利的完成任务。开放思维,不断尝试新的途径,对于找到一种可行的数据仓库实现方法来说也是必需的。 

1. 配备一个全职的项目经理或你自己全面负责项目管理
在通常情况下,项目经理都会同时负责多个项目的实施。这么做完全是出于资金和IT资源方面的考虑。但是对于数据仓库项目的管理,绝对不能出现一人身兼数个项目的情况。由于你所处的领域是你和你的团队之前没有进入过的领域,有关数据仓库的一切-数据分析、设计、编程、测试、修改、维护-全都是崭新的,因此你或者你指派的项目经理如果能全心投入,对于项目的成功会有很大帮助。 

2. 将项目管理职责推给别的项目经理
由于数据仓库实现过程实在是太困难了,为了避免自虐,你可以在当前阶段的项目完成后就将项目管理职责推给别的项目经理。当然,这个新的项目经理一定要复合第一条所说的具有全职性。为什么要这么做呢?首先,从项目经理的角度看,数据仓库实施过程的任何一个阶段都足以让人身心疲惫。从物理存储设备的开发到Extract-Transform-Load的实现,从设计开发模型到OLAP,所有阶段都明显的比以前接触的项目更加困难。每个阶段不但需要新的处理方法、新的管理方法,还需要创新性的观点。所以将管理职责推给别的项目经理不但不会对项目有损害,还可以起到帮助作用。

3.与用户进行沟通

这里所讲的内容远比一篇文章本身要重要的多。你必须明白,在数据仓库的设计阶段,那些潜在用户自己也不清楚他们到底需要数据仓库为他们做什么。他们在不断的探索和发现自己的需求,而你的开发团队也在和客户的接触中做着同样的事情。更加频繁的与客户接触,多做记录,并让你的团队更关注于项目需求讨论的结果而不是讨论的过程本身。

既然你和客户的交流是为了了解存储的数据是何种类型以及如何有效存储数据,你也许需要(和你的用户一起)采用一种新的方法观察数据,而不是直接处理数据。你可以尝试从中找出隐藏的信息,比如在一段时期内的数字涨落等。不要试图追寻项目需求的答案,而是要让答案找上门来。

4. 以技术/信息库作为领导

由于数据仓库实施的各个阶段都有很大不同,因此你需要有人能起到维持整个项目的连续进行的作用,不过这个职责并不需要那种全职性。项目实施有三个重要方面:架构、技术和业务。将架构作为重点可以保证在整个项目中,数据仓库的架构从物理层往上,都会受到良好的维护。而我们应该将技术作为重点,因为开发团队和关键用户都在使用他们以前从未用过的工具,必须有人监督开发过程以及工具使用的一致性。

最后,在数据仓库的应用过程中浮现出来的业务需求必须被详细分析和记录,以促机开发过程持续下去。如果用户不能很好的开发人员以及其它用户沟通,那么数据分析和度量方面的开发进程就会延期,所以必须有人关注业务方面的开发,推动开发进入更高级别。

5. 跳出反复修改程序的陷阱
第一次实现的数据仓库肯定不会是最终交付的版本。为什么呢?实际上在真正见到产品前,你无法确定的知道自己的目标是什么。或者说,最终用户只有在使用数据仓库产品一段时间后,才能明确告诉你这个产品是不是他所希望的。与你以往处理的项目不同,业务智能还处于发展的初期,每个公司对业务智能都有不同的解释,因此你的项目决不会一次成功。

为了以正确的格式获得数据,你需要在不断变化的状况中摸索前进。BI具有很强的个性,不同的环境、不同的市场以及不同的企业都有不同的BI。这又代表什么呢?这表示你需要把数据库管理员放在一个消息相对封闭的环境中,不要让他知道数据仓库的数据结构以及ETL程序在不断的改变。对此没有别的办法。这样可以减轻你和DBA所承受的压力。

6. 对大量的前端资源进行数据源分析
在数据仓库实现过程中,你不得不在旧有的数据中艰难跋涉,这些数据来自老的数据库、老的磁带机以及远程的数据。它们中的大部分都凌乱不堪,并且难以获取。你要对这些数据进行大量处理,并且还要设计ETL程序来寻找其中的有用信息。如果你希望整个项目做起来比较顺利,并且找到一种方法能够一次成功,那就需要你的开发人员必须花费足够的时间来充分研究这些旧有数据,将凌乱的数据规则化,并尽力设计和实现强壮的数据采集和转换过程。数据仓库的ETL部分会占用整个项目资源的百分之八十,所以一定要确定你的资源都用在刀刃上了。

7. 将人际关系处理放在首位
在数据仓库实现过程中真正的地狱不是来自技术或者开发方面,而是来自你周围的人。你也许会遇到一个对项目并不乐观而又没时间听你陈述的领导。你也许会遇到一些开发人员将进度拖延太长时间还抱怨为什么不能用老方法实施。你也许还会遇到一些抱有不切实际的幻想的用户,他们希望轻点鼠标就能实现想象中的功能,但却不愿在他们那边多做些智力投资,更好的培训他们自己的员工。而你也已经疲惫不堪,鼓励投资,以及在开发团队和用户(甚至老板)中推广新的开发技巧。

总之你要保持微笑。当一切搞定,你的烦恼也就一扫而空了,笑到最后才笑得最轻松。



数据仓库开发过程中的七个禁忌 

过去我们一直使用的OLTP技术也许隐藏着许多严重的缺陷。数据仓库的实现并不是一个简单的任务,你会发现以前积累下来的丰富经验,并不适合处理每个数据仓库的独特需求。 

下面列出的条款是你在实现数据仓库过程中一定会面对的问题,其中一些看起来并没有想象中那么严重,但是你还是应该尽量避免出现类似问题。数据仓库并不是一个事务处理系统,它没有一定的标准也不会实现某个特定的应用,但它本质上是非常有组织性的。总之,每个公司所建立的数据仓库都是唯一的,并且每一次数据仓库的实现方法都不是一成不变的。在实现数据仓库时需要注意的不单是”应该如何作”,更要注意”不该如何做”。下面就是我们总结的七点”不该如何作”。 

1.不要编写自己无法快速修改的代码
你所要编写的程序主要用于数据分析,而不是处理事务。而你的用户也并不真正知道他们自己真正想要一个什么样的程序。因此你不得不反复修改代码好几次,才会明白用户到底需要一个什么样的程序。如果你编写的程序具有良好的结构和灵活性,就算需要修改也不会太浪费力气。反之,你会被自己累死。 

2. 不要使用无法修改的数据库访问API
在过去,你的数据库可以为大量的客户提供稳定的数据查询服务。而如今,你的程序必须能够应付更多的数据查询。这使得重新改写程序以使得每个查询请求能得到最大的数据量成为势在必行的工作,而一般来说这种代码修改都不会一次成功,所以只有选择合适的可以修改的API,才能使程序尽快适应新的需求。 

3. 不要设计任何无法扩展的东西
在联机处理过程(OLTP)应用中,数据分析并不是一个真正的应用程序。实际上,数据分析的关键是获取大量旧的数据,从中提取数据模型,并以此模型推断出新的信息。而你所编写的访问潜在信息的代码应该具有可扩展性,可以附加新的数据。千万别在支持数据分析的代码中假定数据都是固定格式的。 

4. 不要附加不必要的功能
一个仓库要做的是恰到好处的服务,用户走进仓库,从货架上取得自己所需得信息,仅此而已。由于业务智能、分析以及规律性的问题都有各自的处理程序,因此你的客户唯一的需要就是获取信息。他们需要一种应用环境,可以让他们快速的从数据仓库中取得分析过程所需的数据,而不论这个数据是什么样子的。也许你想帮助他们精炼一下获得的数据,但最好不要这么做。一定要记住,不要给客户的数据分析程序添加任何会影响数据访问性能的功能。

5. 不要简化数据清除和数据源分析的步骤
在实现数据仓库过程中最应该注意的地方就是为Extract-Transform-Load机制分析数据源,以及为优化负载而清除数据。安全的做法是假设项目经理在这个阶段会需要整个项目资源的一半以上。相反,如果你在这方面进行了简化,稍后肯定会后悔。所以就算系统工作缓慢,也不要简化清理旧的数据的过程。

6. 不要避免颗粒度和分区问题
在数据仓库设计过程中有两个最大的数据存储问题,第一是如何给转换数据定位一个恰当的颗粒度等级,第二是如何将数据绝对的分区。为什么这两点问题如此重要呢?因为整个数据仓库的响应能力受颗粒度影响,并且数据访问的效率直接与数据分区性能有关。因此这是具有关键性的工作,不要试图避免面对这些问题。

7. 不要在没考虑业务问题前就使用OLAP
用户在亲眼见到程序前通常都不知道自己到底想要个什么样的程序。因此他们的观点有不少错误,比如他们希望分析结果会忠实反应性能度量,或者希望程序会使他们部门或公司的业务工作有所不同。而你必须跳出自己的职责范围,从IT管理者的角度考虑用户部门直至整个企业的运行方式,才能在开发过程中避免这类问题。在通常的OLTP开发中,你可以比较方便的理解业务流程。而在联机分析处理(OLAP)领域,任何事情都需要亲自考察,而在你周围工作的人也许并不会发现你对业务方面存在的误解。因此,不要自以为已经了解了足够的信息。不断的询问才能使你真正了解”业务智能”中的”业务”到底是什么样子的


顺利开发数据仓库的七种思路
 


对于大多数IT顾问来说,实现一个数据仓库的难度比以前做过的任何项目难度都要大。考虑到不同的数据结构、用途以及应用程序开发方法,以前所积累的经验和技巧大部分都无用武之地了。但是只要在你的前进道路上稍加修正,你就会发现实现一个数据仓库并不是难事,就算你是第一次实现数据仓库也没问题。 

下面列出了数据仓库实施过程需要考虑的步骤,有一些你可能从来没有意识到,而另一些可能已经在实施过程中使用到了,但是重新思考一番也许你会有更多的领悟。开放思维,不断尝试新的途径,找到一种可行的数据仓库实现方法。 

1. 再三考虑应用程序的实现方法
数据仓库并不涉及事务处理,并且在报表方面也仅占一小部分。而数据仓库应用程序的本质是分析,尤其是针对业务智能的分析。BI并不是通常所说的数据:它是一种从旧有数据中,模型化得到的新的数据。那么如何才能从旧有数据中挖出这些新数据呢?事实上,这个工作不是让你来完成的,而是你的客户所要完成的。从项目主管的角度看,应该有一个经验丰富的数据表格设计师与你合作,进而决定如何将各类程序融合在一起。其中所遇到的最主要的挑战将是如何用新的方法观察数据,这也是你的客户正在试图使用的方法。 

2. 创建抽象的、良好部署的数据库访问组件
在过去你接触过的数据库项目和现在的数据仓库之间,有一点绝对不同,那就是:在Online Transaction Processing (OLTP)环境中,用户数量非常大,但使用到的数据却比较少;而在Online Analytical Processing (OLAP)环境中情况却正好相反,少量的用户在使用大量的数据。而你的工作就是编写一个应用程序来优化这种不同。这里有一个线索:在你所有的分析程序中,都要能抓取连续的数据项,这样在以后建立和访问的数据结构中才能存放与原数据物理结构类似的数据。具体如何实现呢?首先不要规格化数据。第二将其放入数组中最小化读取请求数。按照这种方法,DBA会很乐意与你合作。

3. 保持松散
现在回头看看第一步,你应该可以理解定义一个分析程序不是件简单事了,而且一般情况下,很难在第一次就实现符合要求的最终产品。而在你将要进行分析的数据结构上同样存在这种问题。一句话,实现过程会有很多变数,你需要不断的改动你的程序。通常我们都希望将改动次数降到最低。在一个数据仓库实现过程中,本质是要分析过程毫无差错,这也需要DBA的参与。不要死抓住你的程序设计、代码、框图,或你建立的其它什么东西不放手,要根据这种变化而不断进行调整。

4. 将管理放在首位
在分析数据源方面你做的如何呢?你是否认为清理垃圾数据的工作非常困难?并不是只有你一个人这样想,做过类似工作的人都有这种看法。在一个一般规模的机构中,作为数据仓库实现过程的一部分,会有大量的旧有数据必须进行一致性处理。所以分析数据源并花费数个小时编写转换程序将旧有数据导入数据仓库是整个数据仓库实现过程中最艰难的一部分。并且这也是整个项目中最重要的一环,可以占到整个项目周期和预算的四分之三。所以一定要小心对待。

5. 从字里行间发现问题
与用户交流是个很麻烦的事情,为什么这么说呢?因为很多用户在见到最终产品前都不知道自己想要什么样的产品。定义数据仓库应用程序是一个探索的过程,而且这个过程要反复进行。记住所谓的”业务智能”是用户自己定义的,他们按照自己的理解来处理业务流程。因此这些用户就是连接数据和业务处理过程间的桥梁。他们所要的并不是数据本身,而是隐藏在数据后面的智能性。你可以让他们讨论、思考并给出建设性的意见。但千万不要让他们解决或让他们任意想象和发表那些”有可能”的观点。最后,一定要随时留意用户得出的结论。

6. 保持领先
数据仓库看起来没有传统的OLTP模式根深蒂固,事实如此。虽然很多人投身数据仓库的开发中,但由于其框架与以前的系统大相径庭,因此在开始的一段时间数据仓库的实现看上去相当混乱。但是坚持下去是很重要的。它具有两方面重要的作用。

第一,技术的领先性。它可以跟踪项目中任何阶段的软件工具的部署和正确使用,以及开发过程。如果这复合你的背景,你可以对此多加留意。

第二,体系结构的领先性。它使得项目在各个阶段转换时,数据仓库和它所支持的系统的物理以及逻辑架构都具有持续性,不会发生改变。这也是你能提供的。

7. 发出警告
最后你要记住,你并不是唯一登上新大陆的人。你周围的每一个人都会有下面一点或几点问题:不现实的期望、对技术的误解、旧习惯或坏习惯、竞争行为,或缺乏对项目的信任度。虽然交流沟通等任务应该是项目经理负责的,但实际上你也要担负起相同的责任。那么作为技术总监你该怎么作呢?首先当然是要真诚的对待周围的人,但一定要竖立威信,适当的发出警告。当你发现项目进度缓慢、资源流失,或者员工失去目标,就要直言不讳的说出来。快速明确的给予警告在大部分情况下都是明智之举。匆忙上马的数据仓库项目也许会出轨,但不要让失败的项目把你拉下马。(

2005年03月14日

前些日子在 China-Pub 买了本《操作系统:设计与实现(第二版)》,然后就开始满世界的找 Minix(呵呵,我向来对底层机制比较感兴趣,想起来了就看看吧)。谁知道在网上一搜,发现了 Bochs 这个东西,不错啊!身材确实苗条,还是开源的:)它的官方网站上正好有 Minix 的硬盘 IMG 下载,一起给我拖回来。这个 Minix IMG 包的作者甚至还在包里带了配置文件(bochsrc.txt),省得自己配了:)(还是老外比较能办实事啊,这个不得不承认)

我就效仿 Bochs 自带的 dlxlinux 建了个叫 Minix 的文件夹,然后把 dlxlinux 里面那个 Run.bat 拷过来修改如下(我的 Bochs 装在 C:\Program Files\Bochs 里面):
cd “C:\Program Files\Bochs\Minix”
..\bochs -q -f bochsrc.txt

以后要用 Bochs 运行 Minix 双击这个批处理就搞定了——至此,算是解决了 Minix 的安装问题。其实虚拟机还有 vmWare 和 Virtual PC,而且在虚拟机和主机之间交换数据做得也比 Bochs 好,可是冲着 Bochs 的身材我算是喜欢上它了:)

好了,马上可以上路了!启动 Bochs 不久后可以看到:


Minix boot monitor 2.16
Press ESC to enter the monitor
Hit a key as follows:
= Start Minix

按等号后就可以看见登录画面了:


Multiuser startup in progress.
Starting daemons: update cron.

Minix Release 2 Version 0.3

noname login:

主机名是 noname,登录后可以在 /etc/hostname.file 里面改。这里的登录名用 root,没有密码。登录之后就可以看到 shell 提示符“#”,不过只有两个控制台可用。找了半天好像没有办法把提示符设成当前工作目录(就算设了也不能即时刷新,这点用起来觉得不太方便,不过它毕竟是个教学系统,不是用来干活的,凑合一下吧:)

大概转了转,基本的命令都有,不过也不可以 alias,以前在 Linux 和 FreeBSD 里面都习惯来个“alias dir ‘ls -al’”,这么一来还真觉得不方便:)看了一下手册,Minix 的 shell 好像叫 ash(以前没听说过),看来功能确实是简单啊。vi 也有,其实是 elvis(跟 vi 兼容又没版权问题的工具),居然还可以读配置文件,就是 home 目录下的 .exrc 文件(这个 IMG 把 root 的 home 目录设在了 /,当然你也可以改 /etc/passwd)。试用的感觉好像比 Solaris 下面带的那个 vi 还好用一些:)
set ts=4
set sw=4
这两个选项也可以用,就是不支持语法高亮(开个玩笑:))。

最爽的是这个 IMG 里面还带了 cc 和所有的源代码,这样一来我们有什么不满就可以自己动手丰衣足食了,不错,不错!看来这个 Bochs + Minix 2.0 IMG 还真是个学习操作系统原理和 Minix 的好东西!所有的源码就放在 /usr/src 下。

看着手痒,就先小试一把吧,ls 列目录的时候没有颜色,就拿这个改改吧:)什么?颜色怎么出来?给你看看下面的代码吧:


#include <stdio.h>

int
main( void )
{
printf( “\033[31m2ndBoy\033[0m\n" );
return( 0 );
}

假设程序保存为 color.c,编译:
# cc -o color color.c
运行结果如下:
# ./color
2ndBoy

怎么样?看到彩色了吧!我们只要把格式串里面的 31 换成别的值就可以看到其它颜色了,其它部分只要照搬就可以了。下面来看看我们要修改的对象:
# cd /usr/src
# find . -name ls.c
./commands/simple/ls.c

ls.c 是个 1134 行的程序,大致的看了一下源代码,发现一个叫 printname() 的函数,在没有详细分析过代码的前提下它看起来比较象是我们要找的对象,那就先试试看,我们参照上面的代码来修改一下这个函数:


void printname( char *name )
{
int c, q = present( 'q' );
printf( "\033[31m" );
while( ( c = (unsigned char)*name++ ) != 0 ){
if( q && ( c <= ' ' || c == 0177 ) ) c = '?';
putchar( c );
}
printf( "\033[0m" ); /* 恢复默认颜色,免得影响后续内容 */
}
红色代码是我加进去的,存盘退出,编译:
# make ls
cc -i -D_MINIX -D_POSIX_SOURCE -o ls ls.c
install -S 20kw ls
# ./ls

果然,所有列出的文件名全变成了红色,看来是找对门儿了!我们继续修改。

又看了看,程序中是用一个 struct file (ls.c:248)来存放每一个文件的文件信息的,而这些信息又是通过调用 stat() 得来的(注意程序中的 status()),而这正是我们需要的!由于 struct stat 结构中的 st_mode 中存放了文件的文件类型,所以我们可以通过解读这个成员来用不同的颜色显示不同类型的文件。但是 printname() 只有一个参数,看来我们要给它再加一个参数了,改成这样:
void printname( char *name, struct file *f )
当然调用的地方也要做相应的修改,一共有两处,改为:
printname( xxxx, f );

这样一来我们就有了文件类型和文件名这两个东西了,代码修改如下:


void printname( char *name, struct file *f )
{
int c, q = present( 'q' );
if( S_ISDIR( f->mode ) )
printf( "\033[1;34m" ); /* 目录用亮蓝色 */
else if( f->mode & S_IXUSR || f->mode & S_IXGRP || f->mode & S_IXOTH )
printf( "\033[1;32m" ); /* 可执行文件用亮绿色 */
else if( f->mode & S_IFCHR || f->mode & S_IFBLK )
printf( "\033[1;33m" ); /* 字符和块设备用黄色 */
#ifdef S_IFLNK
else if( f->mode & S_IFLNK )
printf( "\033[1;36m" ); /* 符号链接用青色 */
#endif
while( ( c = (unsigned char)*name++ ) != 0 ){
if( q && ( c <= ' ' || c == 0177 ) ) c = '?';
putchar( c );
}
printf( "\033[0m" );
}
编译完之后我们用修改过的 ls 将原来的 ls 覆盖,方便以后正常:
# cp ls /usr/bin
覆盖之后试试有无效果:
# ls -al /

果然,目录用亮蓝色显示出来了。不过在不加 -l 参数时显示出的文件名却没有颜色,看来我们在没有详细分析过程序的前提下改出的代码还有有问题的:)想了想:要取得文件类型肯定要调用 stat(),顺着这个思路搜了一下,果然,程序里调用 stat() 的地方必须要求 field 不为 0(也就是设置了某个参数)。所以我们换个思路,让 ls 认为我们所有对它的调用都加了参数,field 所有的取值在程序中都定义了宏,从 0x0001 到 0x2000,那我们就用它还没有霸占的 0x4000:)在 main() 中做如下修改(红色代码是我加上的):
if( field & L_LONG ) field &= ~L_EXTRA;
field |= 0x4000;

这样一来就可以了,我们的 ls 算是修改成功!!!

上面说到了,Minix 的 shell 不能把提示符设置成即时显示当前目录,那我们再来改改:)搜了一下发现在 /usr/src/commands/ 下面有 sh 和 ash 这两个目录,编译后试了一下,sh 不支持 Tab 键命令补全,那么我们用的这个 shell 一定是 ash 了。在 /usr/src/commands/ash/ 下一番 grep 后确定了目标:parser.c。看起来在显示提示符之前都要用 ps1val() 来获得 PS1 的值,那我们改写这个 ps1val() 提前做个处理不就行了?!打开程序一看,原来这个 ps1val() 是个宏,它在 var.h 里面定义成了 #define ps1val() (vps1.text + 4),而 vps1 里面存放的是 PS1=xxx 的形式,所以 +4 之后直接得到了它的显示内容。要做手脚就不太可能继续用宏,我们把这个宏注视掉,把它改成函数声明:
char *ps1val( void );

现在打开 parser.c,按 G 跳到文件尾加入如下代码:


#include <pwd.h>

char *
ps1val( void )
{
static char szPS1[128];
char *p = vps1.text + 4;
int nPos = 0;
memset( szPS1, 0, sizeof( szPS1 ) );
while( *p != ‘\0′ )
{
if( *p != ‘\\’ ) /* 不是转义字符就原样输出 */
{
szPS1[nPos] = *p;
nPos++;
}
else
{
p++;
if( *p == ‘w’ ) /* 取当前工作路径 */
{
getcwd( szPS1 + nPos, sizeof( szPS1 ) – nPos );
}
else if( *p == ‘u’ ) /* 取用户登录名 */
{
uid_t uid = getuid();
struct passwd *pPasswd = getpwuid( uid );
strcat( szPS1 + nPos, pPasswd->pw_name );
}
nPos = strlen( szPS1 );
}
p++;
}
szPS1[nPos] = ‘\0′;
return( szPS1 );
}

这里我模仿了 Bash 的做法,你可以用转义符来代替一些东西,比如这里用 \u 代表当前用户名,\w 代表当前工作目录。存盘后编译:
# make

这里 make 时间要长一些,好了之后让我们来试一下:
# ./sh
# PS1=”[\u \w]# “
[root /usr/src/commands/ash]#

哈哈,大功告成,双儿,来……

把编译好的 sh 复制到 /bin 下,以后我们进入系统就用它了,记得改一下 .profile 设置好 PS1,这下 Minix 的环境好用多了!!!

Minix 是专门设计用来学习操作系统原理的,我在这里改改 shell 跟 ls 根本没有接触到 OS 理论的东西,呵呵,不过用这两个东西开个头倒也不错。

 

“真实”和“谎言”一起到河边洗澡, 先上岸的“谎言”偷偷穿上“真实”的衣服不肯归还,
固执的 “真实” 死也不肯穿上“谎言”的衣服,只好一丝不挂光溜溜地走回家。从此,
人们眼中只有穿着真实外衣的谎言,却怎么也无法接受赤裸裸的真实。

2005年02月13日

云海情天网络广播收听地址:mms://219.133.55.165/live

2005年01月02日

目前,绝大多数的数据库参考书都介绍了ODBC的手工配置方法,或者介绍了如何在代码中进行ODBC配置。但这两种方法都有一定的局限性: 
    不是当程序最终完成并分发到用户手中后,还需要为用户配置ODBC,显得既麻烦又不符合专业软件的要求;就是得编写复杂的更改操作系统注册表文件的程序,十分烦琐。本文从ADO(ActiveX Data Objects)入手,介绍无需配置数据源的几种常用大型数据库连接方法。 
    本文所述的无“数据源”连接,意义不是不需要数据源,否则连接无从谈起,而是不需要配置注册数据源所进行的连接。ODBC(Open DataBase Connectivity,开放式数据库连接)是用于连接不同数据源的标准编程语言接口。许多文章中介绍,在实现ODBC时,必须首先配置ODBC环境,进行数据源的注册,然后才能在对数据库编程时,对数据源进行连接、访问和操作,并提供了用PB或VB等语言工具实现数据源注册的具体方法。这些方法不但复杂烦琐,而且由于参数内容不一,配置时令人感觉无所适从,不宜把握。 

    走近ADO 

     ADO是微软提供的数据库访问技术。它被设计用来同新的数据访问层OLE DB Provider一起协同工作,以提供通用数据访问(Universal Data Access)能力。OLE DB是一个底层的数据访问接口,用它可以访问各种数据源,包括传统的关系型数据库、电子邮件系统及自定义的商业对象等。 

   ADO提供了一个熟悉的、高层的对OLE DB的Automation封装接口。对那些熟悉RDO的程序员来说,可以把OLE DB看作ODBC驱动程序,如同RDO对象是ODBC驱动程序接口一样,ADO对象是OLE DB的接口。同样,像不同的数据库系统需要它们自己的ODBC驱动程序一样,不同的数据源也要求它们自己的OLE DB提供者(OLE DB provider)。目前,虽然OLE DB提供者比较少,但微软正积极推广该技术,并计划用OLE DB取代ODBC。 

    微软公司已宣布今后不会对VB SQL/DBLib进行升级,而且ODBC API函数一级的编程方式也不为人们所喜爱,所以,RDO今后将被以ActiveX技术为基础的ADO所替代。ADO是基于OLE DB之上的技术,它通过内部的属性和方法提供统一的数据库访问接口。 
    1.ADO组件 
    ● Microsoft ActiveX Data Objects (ADO) :使客户端应用程序能通过OLE DB提供者访问和操作数据库服务器中的数据。 
    ● ActiveX Data Objects Extensions for DDL and Security(ADOX) :将ADO扩展为包括创建、修改和删除的模式对象,如表格和过程,以及包括用于维护用户和组以及管理对象权限的安全对象。 
    ● ActiveX Data Objects (Multidimensional) (ADO MD): 将ADO扩展为包括指定到多维数据的对象,并允许浏览多维模式、查询立方和检索结果。 
    2.ADO优点 
    ● ADO具有高度的灵活性,它可以使用相同的编程模式连接到不同的数据提供者,而不管给定提供者的特定特性。 
    ● 较低的内存占用率。 
    ● 具有远程数据服务(RDS)功能,通过RDS可以在一次往返过程中将数据从服务器移动到客户端应用程序或Web页,并在客户端对数据进行处理后将更新结果返回服务器。 
    ● 同传统的数据对象层次(DAO和RDO)不同,ADO可以独立创建。可以只创建一个“Connection”对象,然后由多个独立的“Recordset”对象来使用它。 
    ● ODBC本身是以SQL Server、Oracle等关系数据库作为访问对象,而OLE DB则不仅限于此,它还可以对电子邮件、文本文件、复合文件、数据表等各种各样的数据通过统一的接口进行存取。 
    OLE DB Provider for ODBC是ADO的默认提供者,默认值是MSDASQL,如果省略连接字符串的Provider=参数,ADO将试图建立与该提供者的连接。 
    ADO的连接方式主要可分为OLE DB Privder方式与OLE DB Provider for ODBC方式。前者很明显是微软公司极力推荐的方式,对于ADO或RDS程序员来说,理想的环境是每个数据源都具有一个OLE DB接口,这比ODBC方式要快且所需资源更少。 
    ODBC Provider允许ADO连接到任何ODBC数据源。ODBC驱动程序对于当今使用的各种主要DBMS都有效,包括SQL Server、Access、FoxPro,以及诸如Oracle等非微软数据库产品。提供者将不受线程控制,允许使用Unicode,并将支持事务。 
    连接对象属性 
    ConnectionString是Connection对象的属性名称,为可读写String类型,提供数据提供者或服务提供者打开到数据源的连接所需要的特定信息,包括Provider、Driver、Server、Database、DSN、UID、PWD或者Provider、Data Source、User、Password、Initial Catalog等。 
    1.Provider 字符串表达式,指定OLE DB数据或服务提供者的名称,可以缺省。 
    一般有三种提供者:数据提供者、服务提供者和服务组件。数据提供者拥有自己的数据并将数据以表的格式显示给应用程序。服务提供者将服务封装,使ADO应用程序中的功能得以扩大。服务提供者也可以进一步定义为服务组件,服务组件必须连同其他服务提供者或组件一起工作。 
    2.Driver 
            字符串表达式,表示ODBC驱动程序的名称,并不是ODBC驱动程序动态链接库(DLL)的文件名。 
            有些驱动程序是微软公司的产品,在安装操作系统时就已经安装好了; 
            而有些数据库产品的驱动程序由开发数据库产品的软件公司随数据库产品一起提供,需要在安装数据库时选择安装后,才可以使用。如:Sybase数据库驱动程序等。 

            3.Server(SRVR) 
            字符串表达式,数据库服务名称。 

            4.Database(DB) 
            字符串表达式,指定服务器上的数据库名称。即使DSN定义已经指定了数据库,也可以在DSN之外指定Database参数以便连接到不同的数据库。 

            5.DSN(Data Source) 
            字符串表达式,在此为空,无须指定连接的ODBC数据源的名称。 

            6.UID(User ID) 
            字符串表达式,为ODBC数据源指定用户标识(用户账号名),指定用户必须有足够的权限。 

            7.PWD(Password) 
            字符串表达式,为ODBC数据源指定用户口令,必须有足够的权限。 

            8.Persist Security Info 
            布尔类型,为True时,表明采用集成安全机制;若为False,则表明不采用集成安全机制。 

            无DSN(非DSN)连接 
            除了ADO所定义的参数外,提供者不支持任何特定连接参数,它将把任何非ADO连接参数传递给ODBC驱动程序管理器。下面介绍几种常见数据库的处理方法。 


            1. Visual Foxpro 
            [PROVIDER=MSDASQL.1]; ’或者为MSDASQL 
            DRIVER={Driver Name}; 
            SourceDB=Path; 
            SourceType=DBF 
            例如: 
            cnna.ConnectionString = “PROVIDER=MSDASQL; ” 
            + “DRIVER={Microsoft Visual Foxpro Driver};” 
            + “SourceDB=D:\data\;” 
            + “SourceType=DBF” 

            2.SQL Server 
            [PROVIDER=MSDASQL;] 
            DRIVER={Driver Name}; 
            SERVER=server; 
            DATABASE=database; 
            UID=user; 
            PWD=password 
            例如: 
            cnnb.ConnectionString = “PROVIDER= 
            MSDASQL;” 
            + “DRIVER={SQL Server};” 
            + “SERVER=servera;” 
            + “DATABASE=pubs;” 
            + “UID=sa;” 
            + “PWD=yyuui” 

            3. Sybase数据库 
            [PROVIDER=MSDASQL;] 
            DRIVER={Driver Name}; 
            SRVR=server; ’必须是SRVR,不能是SERVER 
            DB=database; ’可以是DB,也可以是DATABASE 
            DSN=; ’可以省略 
            UID=user; 
            PWD=passwod; 
            PERSIST SECURITY INFO=False 
            例如: 
            cnnc.ConnectionString= 
            “PROVIDER=MSDASQL;” 
            + “DRIVER={Sybase System 11};” 
            + “SRVR=serveru; ” 
            + “DSN=;” 
            + “DB=dataa;” 
            + “UID=sa;” 
            + “PWD=dqwe;” 
            + “PERSIST SECURITY INFO=False” 

            4. Oracle数据库 

            [PROVIDER=MSDASQL;] 

            DRIVER={Driver Name}; 

            SERVER=server; 

            databasename=database; 

            databasefile=path; 

            DSN=; 

            UID=user; 

            PWD=password; 

            例如: 

            cnnd.ConnectionString = 

            “PROVIDER=MSDASQL; ” 

            + “DRIVER={Microsoft ODBC for Oracle};” 

            + “SERVER=Webserver;” 

            + “DSN=;” 

            + “databasename=dataall;” 

            + “databasefile=d:\data\;” 

            + “UID=dba;” 

            + “PWD=killer” 

            应用实例 

            下面以Sybase 
            11.9.2为例,编制一个简单的工作人员管理程序,介绍ADO的具体实现方法和步骤。数据库名称为Workerdb,只包括一个表(Worker),其结构如下: 


            字段名称    宽度       注释 

            code nchar(4) 代号 

            name char(8) 姓名 

            …… …… …… 

            首先安装ADO,在VB的“工程”/“引用”对话框中选择“ActiveX Data Object 2. 5 
            Library”(ADODB)。其中“ADO Recordset 2. 5 
            Library”是一个客户端的版本(ADOR),因为不需要Connection对象来建立与远程数据源的联系,所以ADOR对于客户端的数据访问来说已经足够了。 
            下面是部分主要代码: 

            1. 处理代码 

            ’在工程菜单中引用Microsoft ActiveX Data Object 2. 5 Library 

            ’声明ADO连接对象为工程级全局变量 

            Public cndbase As New ADODB.Connection 

            …… 

            ’自定义连接数据库函数 

            Public Function ConnectDbase(StrConnect As String) As Boolean 

            On Error GoTo ErrHandle 

            cndbase.ConnectionString = StrConnect 

            cndbase.Open 

            cndbase.CursorLocation = adUseClient 

            ConnectDbase = True 

            Exit Function 

            ErrHandle: 

            ConnectDbase = False 

            End Function 

            Private Sub Form_Load() 

            …… 

            输入 VarServer ’服务名称 

            VarDbase ’数据库名称 

            VarUser ’用户名称 

            VarPassword ’用户口令 

            …… 

            ’连接数据库,采取无DSN连接方法 

            StrConnect = “Provider=MSDASQL;” 

            +“Driver={” & VarDriver &“};” 

            +“SRVR=” & VarServer & “;” 

            +“DB=” & VarDbase & “;” 

            +“DSN=;” 

            +“UID=” & VarUser & “;” 

            +“PWD=” & VarPassword & “;” 

            + “Persist Security Info=False” ’不采用集成安全机制 

            if ConnectDbase(StrConnect) Then 

            Exit Sub 

            else 

            myexit = MsgBox(“数据库连接失败!请检查连接设置信息。”, vbOKOnly, “错误提示:”) 

            Unload me 

            endif 

            ErrHand: 

            myexit = MsgBox(“错误程序:” & Err. Source & Chr(10) & “错误代码:” & Err. 
            Number & Chr(10) & “错误信息:” & Err.Description, VbAbortRetryIgnore, 
            “错误提示:”) 

            If myexit = 3 Then 

            Err.Clear 

            Unload Me 

            Else 

            If myexit = 4 Then 

            Err.Clear 

            Resume 

            Else 

            Err.Clear 

            Resume Next 

            End If 

            End If 

            End Sub 

            Private Sub Form_Unload() 

            …… 

            cndbase.Close 

            Set cndbase = Nothing 

            End Sub 

            2. 建表 

            codbase.CommandText=“{call CREATABLE (?) }” 

            codbase.CommandType = adCmdText 

            codbase.Name = “CREATABLE” 

            ’设定OutPut的参数 

            Set param = codbase.CreateParameter(“flag”, adInteger, 
            adParamOutput) 

            codbase. Parameters. Append param 

            Set codbase. ActiveConnection = cndbase 

            codbase. Execute 

            If codbase. Parameters(0) = 0 Then 

            myexit = MsgBox(“建表成功!”, vbOKOnly, “程序提示:”) 

            Else 

            myexit = MsgBox(“建表失败!”, vbOKOnly, “错误提示:”) 

            Endif 

            …… 

            3. 修改 

            rsdbase. 
            Open“worker”,cndbase,adOpenDynamic,adLockPessimistic,adCmdTable 

            rsdbase. MoveFirst 

            cndbase. BeginTrans 

            ’在记录集中进行循环更改 

            Do Until rsdbase.EOF 

            ’增加20元职务代码为1的人员的工资 

            If rsdbase! duty = 1 Then 

            rsdbase! salary = rsdbase! salary + 20 

            End If 

            rsdbase. MoveNext 

            Loop 

            rsdbase.UpdateBatch 

            …… 

            4. 统计 

            StrSQL = “Select avg(salary), sum(salary) from worker” 

            rsdbase. CursorLocation = adUseClient 

            rsdbase. Open StrSQL,cndbase 

            salaryavg = rsdbase(0) ’平均工资 

            salarysum = rsdbase(1) ’工资总和 

            …… 

            rsdbase .Close 

            5. 存储过程creatable. sql 

            CREATE PROCEDURE dbo.creatable(@return_value integer output) 

            AS 

            Begin 

            Create table Workerdb..Worker 

            ( code nchar(4) not null , 

            name char(8) not null , 

            year nchar(4) , 

            month nchar(2) , 

            day nchar(2) , 

            salary numeric(18,2) , 

            duty nchar(1)  

            If @@error != 0 

            begin 

            select @return_value = 1 

              End 

             Else 

            Begin 

            select @return_value = 0 

            End 

            return 

            End