计算机是怎样跑起来的-第8章 一用就会的数据库
前面的章节讲解的是计算机的构造和程序设计。而本章一改之前的主题,来讲一讲数据库。像 DBMS、关系型数据库、SQL( Structured Query Language,结构化查询语言)、事务(Transaction)之类的数据库术语,想必诸位都有所耳闻吧。可是应该也有很多人觉得自己好像是明白了这些术语的意思,实际上却并没有真正地理解。不仅是数据库,其他计算机技术也一样,不实际地应用,就不能充分掌握。
本章首先介绍数据库的概况,然后通过文字的描述,请诸位体验一下编写简单的数据库应用程序的过程。这样就不但能理解数据库术语的含义,而且还能灵活应用这些知识了。还有一点请诸位明白,在编写数据库应用程序时,可以采用各种各样的方法,而本章所介绍的方法仅仅是其中的一种。
8.1 数据库是数据的基地
所谓数据库(Database)就是数据(Data)的基地(Base)。在实施企业的商业战略时,如果企业内的数据散布在各个地方,在更新和检索时就要花费大量时间,分析起来就会很麻烦。但是只要把企业内的数据预先汇集到一个“基地”中并加以整理,各个部门中充满干劲的员工就可以根据需要灵活地使用这些数据。这个数据的基地就是数据库。虽然使用纸质文件整理出来的数据也可以称为数据库,但是利用善于处理数据的计算机整理会更加方便。因为计算机是提高手工工作效率的工具,所以就成为了数据的基地。
把数据存储到计算机中以后,为了将其整理得易于使用,就不得不考虑其存储方式。在手工作业的阶段,通常是像账单或名片那样,把所需的信息汇集到一张纸上。将这样的数据存储形式原封不动地移植到计算机中,就形成了“卡片型数据库”。存储一条数据就好比把一张账单或是名片上的信息记录到一个文件中。卡片型数据库适用于想要实现小规模的数据库的情况。像是地址簿管理程序、存储 Web 电子公告板上的评论等,使用的都是卡片型数据库(如图 8.1 所示)。

可是,如果要实现能够管理企业所有信息的大规模数据库,卡片
型数据库就无能为力了。这是因为卡片与卡片之间缺乏关联性,因此
也就难以记录像是“A 公司向 B 公司出售了商品”这样的信息。诸位看了图 8.1 后就会明白,假设公司名称由“GrapeCity 股份有限公司”变更为“葡萄城股份有限公司”,那么麻烦的工作就来了,所有记录了“GrapeCity 股份有限公司”的卡片都需要修改。
适合存储大规模数据的是关系型数据库(Relational Database)。在关系型数据库中,数据被拆分整理到多张表中,同时表与表之间的关系也可以被记录下来。对于上面的例子,只要把数据分别存储到企业表和个人表中,再在这两张表间建立关系,那么在公司名称变更时,只需要更新企业表中的一项数据就能解决问题了,即把企业表中的“GrapeCity 股份有限公司”改为“葡萄城股份有限公司”即可(如图 8.2所示)。同时也就能够很方便地记录像是“A 公司向 B 公司出售了商品”这样的数据了。

1970 年美国 IBM 公司的 Codd 先生设计发明了关系型数据库。现在关系型数据库被广泛应用,以至于一提到数据库就默认是关系型数据库。在后面的章节中,将要请诸位通过文字上的描述感受其编写过程的数据库应用程序,也是使用关系型数据库完成的。
8.2 数据文件、DBMS 和数据库应用程序
为了编写数据库应用程序(即为了便于操作数据库而编写的程序),诸位可以从零开始埋头编写所有代码,但是一般情况下,还是会借助称作 DBMS 的软件。Microsoft Access、Oracle、SQL Server、DB2 等诸位都有所耳闻吧,这些都是 DBMS 的实例。数据库的实质虽然是某种数据文件,但是诸位编写的应用程序并不是直接去读写这些数据文件,而是以 DBMS 作为中介间接地读写(如图 8.3 所示)。DBMS 不但可以使应用程序轻松地读写数据文件,而且还具有一致并且安全地存储数据的功能。

何为“一致并且安全地存储”将会在后面解释,在此还是先介绍一下数据库系统的构成要素吧。数据库系统的构成要素包括“数据文件”“DBMS”“应用程序”三部分。在小型系统中,把三个要素全部部署在一台计算机上,称作“独立型系统”。在中型系统中,把数据文件部署在一台计算机上,并且使数据文件被部署了 DBMS 和应用程序的多台计算机共享,这样的系统被称为“文件共享型系统”。在大型系统中,把数据文件和 DBMS 部署在一台(或者多台)计算机上,然后用户从另外一些部署着应用程序的计算机上访问,这样的系统被称作“客户端/服务器型系统”。其中部署着数据文件和 DBMS 的计算机是服务器(Server),即服务的提供者;部署着应用程序的计算机是客户端(Client),即服务的使用者。如果把服务器和客户端之间用互联网联结起来,就形成了 Web 系统。在 Web 系统中,一般情况下应用程序也是部署在服务器中的,在客户端只部署 Web 浏览器(如图8.4 所示)。


8.3 设计数据库
既然已经大体上了解了数据库的概况,那么我们就开始实际编写一个数据库应用程序吧。本节中,我们将在一台个人计算机上使用名为 Microsoft Access 的 DBMS 实现一个独立型系统。应用程序部分,使用 Visual Basic 6.0 编写。应用程序以酒铺管理为主题。请诸位学会利用身边的例子来帮助理解新知识。
首先从设计数据库开始。而设计数据库的第一步是从“你想要了解什么”的视角出发找出需要的数据。如果是自己使用的数据库,那么就问问你自己想要了解什么。如果是为客户设计数据库,就要去询问对方想要了解什么。
在酒铺管理的应用程序中,将下面的数据视为客户想要了解的数据。
酒铺经营者需要知道什么?
● 商品名称
● 单价(日元)
● 销售量
● 顾客姓名
● 住址
● 电话号码
当然,仅仅存储这些数据是否够用,是由数据库的使用者决定的。如果缺少了所需的数据,就算使用了数据库,也不能使其发挥作用。反过来,如果包含了不必要的数据,存储包含着这些数据的文件就会白白浪费掉磁盘空间。
把必要的数据筛选出来以后,下一步要考虑的是各种数据的属性。属性也称作模式(内模式),具体来说就是数据的类型(是数字还是字符串),数字的话是整数还是浮点小数,字符串的话最多允许包含多少个字符,是否允许 NULL 值(表示未知或者不存在的值),等等。
几乎所有的 DBMS 都提供了通过可视化界面设置数据属性的工具。通过这种工具,既可以生成逻辑上的表,又可以生成物理上存储数据的数据文件。其中,表可以被赋予任意的名称。对酒铺经营者所需的各个数据分别设置完属性后,我们将表暂且命名为酒铺表(如图 8.5所示)。

在这里,请先记住一些数据库术语。在关系型数据库中,把录入到表中的每一行数据都称为记录,把构成一条记录中的各个数据项(在本例中是商品名称、单价等)所在的列都称作字段。记录有时也被称为行或元组(Tuple),字段有时也被称为列或属性(Attribute)。上面提到的属性(数据的类型)就是设置在字段上的。为了代表字段所存储数据的内容还要为每个字段起一个名字。如图 8.5 所示,通过这个界面定义了构成一条记录的多个字段。之后只要在这个表中录入数据,表就可以使用了。
8.4 通过拆表和整理数据实现规范化
既然表已经准备好了,那么只需要把带有用户界面并且能够读写数据的应用程序做出来,就大功告成了。可实际上却并非如此,如果就这样使用这张表,那么在数据库的运行过程中有可能会产生一些问题。DBMS 既然已经提供了用于手工输入数据的工具,那么我们就先试着录入几条测试数据看看吧(如图 8.6 所示)。

于是这就产生了两个问题。第一个问题是,用户不得不多次录入
相同的数据,就像第一条和第二条记录中的数据“日经次郎、东京都千
代田区、03-2222-2222”。录入重复数据不仅使应用程序的操作变得繁琐,更白白浪费了磁盘空间。另一个问题是,录入的名称不同指代的却是相同的商品,就像在第三条记录中,应该输入“威士忌”,却错误地输入了“维士忌”,如果让计算机来处理,这种情况就会被识别成不同的商品。也就是说,如果仅使用一张表,就会和应用卡片型数据库(每条记录对应一张卡片)时面临相同的问题。
为了解决这类问题,在设计关系型数据库时,还要进行“规范化”。所谓规范化,就是将一张大表分割成多张小表,然后再在小表之间建立关系,以此来达到整理数据库结构的目的。通过规范化,可以形成结构更加优良的数据库。DBMS 提供了可视化的工具,用户仅仅通过简单的操作就可以进行规范化(如图 8.7 所示)。

规范化的要点是在一个数据库中要避免重复存储相同的数据。因此在本例中,把酒铺的数据库分为“商品表”“顾客表”和“销售记录表”三张表,然后再在它们之间建立关系(用被细线连接起来的短杠表示)。通过这样的处理,既省去了多次重复录入相同的顾客姓名、住址、电话号码的麻烦,又能防止把相同的商品名称输入成不同名称的错误。如图 8.8 所示,酒铺的数据被分别存储到了三张表中。表格的最后一行只是表示在这里可以输入下一条记录,并非实际存在着这样一条记录。

8.5 用主键和外键在表间建立关系
为了在表间建立关系,就必须加入能够反映表与表之间关系的字段,为此所添加的新字段就被称为键(Key)。首先要在各个表中添加一个名为主键(Primary Key)的字段,该字段的值能够唯一地标识表中的一条记录(如图 8.9 所示)。在顾客表中添加的“顾客 ID”字段,在销售记录表中添加的“销售记录 ID”字段以及在商品表中添加的“商品ID”字段,都是主键。

正如“顾客 ID”一样,通常将主键命名为“某某 ID”。这是因为主键存储的是能够唯一标识一条记录的 ID(Identification,识别码)。如图 8.8 所示,如果顾客表中顾客 ID 是 1 的话,就能确定是日经次郎这条记录;顾客 ID 是 2 的话,就能确定是矢泽三郎这条记录。正因为这种特性,在主键上绝不能存储相同的值。如果试图录入在主键上含有相同值的记录,DBMS 就会产生一个错误通知,这就是 DBMS 所具备的一种一致并且安全地存储数据的机制。
在销售记录表上,还要添加顾客 ID 和商品 ID 字段,这两个字段分别是另外两张表的主键,对于销售记录表来说,它们就都是“外键” (Foreign Key)。通过主键和外键上相同的值,多个表之间就产生了关联,就可以顺藤摸瓜取出数据。例如,销售记录表中最上面的一条记录是(1,1,1,3),分别表示该销售记录 ID 为 1,顾客 ID 为 1 的顾客买了 3 个商品 ID 为 1 的商品。通过顾客表,可以知道顾客 ID 为 1的顾客信息是(1,日经次郎,东京都千代田区,03-2222-2222)。通过商品表,可以知道商品 ID 为 1 的商品信息是(1,日本酒,2000)。虽然作为销售记录表主键的“销售记录 ID”字段并不是其他表的外键,但是考虑到以后有可能会与其他表发生关联,并且习惯上必定要在表中设置一个主键,所以予以保留。主键既可以只由一个字段充当,也可以将多个字段组合在一起形成复合主键。
表之间的关系使记录和记录关联了起来。记录之间虽然在逻辑上有一对一、多对多以及一对多(等同于多对一)三种关系,但是在关系型数据库中无法直接表示多对多关系。这是因为在多个字段中以顺藤摸瓜的方式查找数据并不是那么容易的。如果将酒铺的数据库只分为顾客表和商品表,那么这两张表就形成了多对多关系。也就是说一位顾客可以购买多个商品,反过来一种商品可以被多位顾客所购买。
当出现多对多关系时,可以在这两张表之间再加入一张表,把多对多关系分解成两个一对多关系(如图 8.10 所示)。加入的这张表被称作连接表(Link Table)。在酒铺数据库中,销售记录表就是连接表。如图 8.7 所示,在表示一对多关系的连线的两端,写有“1”的一侧表示“一”,写有“∞”、即无穷大符号的一侧表示“多”。

DBMS 中还具有检查参照完整性的功能,这种机制也是为了一致并且安全地存储数据。例如,在目前的酒铺数据库中,如果从商品表中删除了“日本酒”这条记录,那么在销售记录表中,曾经记录着买的是日本酒的那两条记录就不再能说明买的是什么商品了。但是一旦勾选了实施参照完整性的选项(如图 8.11 所示),在应用程序中再执行这类操作时,DBMS 就会拒绝执行。

如果诸位是直接从编写的应用程序中读写数据文件,那么就会导致用户可以录入在主键上含有相同值的记录,或者由于没有进行参照完整性等方面的检查,使用户可以任意地执行删除数据之类的操作。而 DBMS 却能在这种问题上起到防患于未然的作用,确实是一种很方便的工具。
8.6 索引能够提升数据的检索速度
可以在表的各个字段上设置索引(Index),这也是 DBMS 所具备的功能之一。虽然索引和键这两个概念容易让人混淆,但其实两者是完全不同的。索引仅仅是提升数据检索和排序速度的内部机制。一旦在字段上设置了索引,DBMS 就会自动为这个字段创建索引表(如图8.12 所示)。

索引表是一种数据结构,存储着字段的值以及字段所对应记录的位置。例如,如果在顾客表的顾客姓名字段上设置了索引,DBMS 就会创建一张索引表(如图 8.13 所示),表中有两个字段,分别存储着顾客姓名和位置(所对应的记录在数据文件中的位置)。与原来的顾客表相比,索引表中的字段数更少,所以可以更快地进行数据的检索和排序。当查询数据时,DBMS 先在索引表中进行数据的检索和排序,然后再根据位置信息从原来的数据表中把完整的记录取出来。索引所起的就是“目录”的作用。与图书的目录一样,数据库的索引也是一种能够高效地查找目标数据的机制。

也许会有人这样想,既然索引能够提升检索和排序的速度,那么在所有表的所有字段上都加上索引不就好了吗?实际上并不能这样做。因为一旦设置了索引,每次向表中插入数据时,DBMS 都必须更新索引表。提升数据检索和排序速度的代价,就是插入或更新数据速度的降低。因此,只有对那些要频繁地进行检索和排序的字段,才需要设置索引。在酒铺数据库这个例子中,只需要在顾客表的顾客姓名字段和商品表的商品名称字段上设置索引就足够了。如果表中充其量也就只有几千条记录,那么即使完全不使用索引,也不会感到检索或排序速度有多慢。
8.7 设计用户界面
只要通过拆表实现了规范化、设置了主键和外键、确保没有多对多关系、根据需要设置了参照完整性和索引,那么数据库的设计就告一段落了。接下来就该进入为了利用数据库中的数据而编写数据库应用程序的阶段了。只要数据库设计好了,设计一个带有用户界面的、能够操作其中数据的应用程序就很轻松了。在设计系统时,请诸位记住一个重要的顺序:优先设计数据库,然后再设计用户界面。
对数据库进行的操作的种类通常称为 CRUD。CRUD 由以下四种
操作的英文名称的首字母组成,即记录的插入(CREATE)、获取(REFER)、更新(UPDATE)、删除(DELETE)。数据库应用程序只要能够对记录进行 CRUD 的操作就可以了。当然,为了满足用户的需求,为应用程序相应地增加统计、打印等功能的情况也是存在的。
由于 DBMS 具有自动生成主键和外键上的值的功能,所以在设计用户界面时,需要显示其余的字段,并要使 CRUD 操作能够通过按钮和菜单来完成。
图 8.14 展示了一个用四个按钮分别进行 CRUD 操作的例子。对于购买了多种商品的顾客,还可以通过“下一条”和“上一条”按钮交替地在界面上显示每种商品的名称、单价和销售量。请诸位注意一点,虽然数据被拆分成三个表存放,但是透过应用程序,用户感到他所处理的是一个相关数据的集合。界面中所显示的数据,是从三张表中用顺藤摸瓜的方式取出来的。

8.8 向 DBMS 发送 CRUD 操作的 SQL 语句
为了对数据库进行 CRUD 操作,就必须从应用程序向 DBMS 发送命令。这里所使用的命令就是 SQL 语言(Structural Query Language,结构化查询语言)。SQL 语言的标准是由 ISO(International Organization for Standardization,国际标准化组织)制订的。现在市面上几乎所有的DBMS 都支持 SQL 语言。
一旦向 DBMS 发送了一条命令(SQL 语句),与此相应的操作就会立刻被执行。与 BASIC 或 C 语言等编程语言不同的是,使用 SQL 语言通常不需要定义变量或者考虑程序的执行流程。下面给诸位展示一个 SQL 语句的例子,可以看出它和英文的句子很像。
SELECT 顾客姓名 , 住址 , 电话号码 , 商品名称 , 单价 , 销售量FROM 顾客表 , 商品表 , 销售记录表
WHERE 顾客表 . 顾客姓名 = “日经次郎”
AND 销售记录表 . 顾客 ID = 顾客表 . 顾客 ID
AND 销售记录表 . 商品 ID = 商品表 . 商品 ID ;
开头的 SELECT 所表示的就是 CRUD 中的 R 操作,也就从表中获取数据。在 SELECT 后面列出了想获取的字段的名字,用逗号分隔。在 FROM 后面,列出了用逗号分隔的表名。WHERE 后面则列出了查询条件。其中的 AND 表示多个查询条件是逻辑与的关系(条件 A 和条件 B 都成立)。而像“顾客表 . 顾客姓名”这样用“.”分隔的形式表示顾客姓名字段是属于顾客表的。在 SQL 语句的末尾放置一个分号表示
语句的结束。
DBMS 不仅提供了手动向 DBMS 发送 SQL 语句的工具,而且还提供了通过可视化操作自动生成 SQL 语句的工具。将上述 SQL 语句发送到 DBMS 执行以后,结果如图 8.15 所示。日经次郎购入的商品一目了然。

CRUD 中的 C、U、D 分别对应着 SQL 语言中的 INSERT(插入)、 UPDATE(更新)、DELETE(删除)语句。在 SQL 语言中除了 CRUD语句,还有新建数据库以及后面将要介绍的事务控制等语句,有兴趣的读者,可以查查 SQL 语言的文档等资料。
8.9 使用数据对象向 DBMS 发送 SQL 语句
在 Windows 应用程序中,向 DBMS 发送 SQL 语句时,一般情况下使用的都是被称为数据对象(Data Object)的软件组件(参考第 7 章所介绍的类)。一般的开发工具中也都包含了数据对象组件。在 Visual Basic 6.0 中,使用的是被称为 ADO(ActiveX Data Object,ActiveX 数据对象)的数据对象。
ADO 是以下几个类的统称,其中包括用于建立和 DBMS 连接的Connection 类, 向 DBMS 发 送 SQL 语 句 的 Command 类 以 及 存 储DBMS 返回结果的 Recordset 类等。图 8.14 所示的应用程序的代码如代码清单 8.1 所示。在程序启动时连接 DBMS,然后进行与各个按钮对应的 CRUD 操作,在程序结束时关闭与 DBMS 的连接。在使用 ADO 时必不可少的是 SQL 语句,其中主要是 SELECT 语句。而插入、更新、删除语句可以通过 Recordset 类所提供的 AddNew、Update、Delete 方法(类中所提供的函数)执行。可以认为这些方法在内部自动生成了SQL 语句并发送给了 DBMS。诸位可以不去深究以下代码的细节,只
要能抓住其大意就可以了。
代码清单 8.1 使用 ADO 访问数据库的示例程序(VB 6.0)



8.10 事务控制也可以交给 DBMS 处理
最后介绍 DBMS 的一个高级功能——事务控制。事务由若干条SQL 语句构成,表示对数据库一系列相关操作的集合。有一个经典的银行账户间汇款的例子可以用于说明其概念。为了从顾客 A 的账户中给顾客 B 的账户汇入 1 万日元,就需要将以下两条 SQL 语句依次发送给 DBMS :1. 把 A 的账户余额更新(UPDATE 语句)为现有余额减去1 万日元;2. 把 B 的账户余额更新(UPDATE 语句)为现有余额加上1 万日元。此时这两条 SQL 语句就构成了一个事务。
假设在第一条 SQL 语句执行后,网络或计算机发生了故障,第二条 SQL 语句无法执行,那么会发生什么呢? A 的账户余额虽然减少了1 万日元,但是 B 的账户余额却没有相应地增加 1 万日元,这就导致了数据不一致。为了防止出现这种问题,在 SQL 语言中设计了以下三条语句:1. BEGIN TRANSACTION(开启事务)语句,用于通知 DBMS开启事务;2. COMMIT(提交事务)语句,用于通知 DBMS 提交事务;3. ROLL BACK(事务回滚)语句,用于在事务进行中发生问题时,把数据库中的数据恢复到事务开始前的状态(如图 8.16 所示)。在使用ADO 创建应用程序时,可以分别使用 Connection 类的 BeginTrans、 CommitTrans 和 RollbackTrans 方法实现上述三个操作。DBMS 真的是很方便,只要使用了 DBMS,连事务管理这样的高级功能都不必自己实现了。

计算机是一种工具,它可以执行输入、计算、输出三种操作,并可以通过这一系列的操作处理某种数据。因此可以说计算机就是处理数据的装置。因此,可以说计算机基本上就是被当作数据库来利用的。只要不是游戏程序,几乎在所有的应用程序中,人们都在巧妙地运用着数据库。因此为了了解计算机,数据库是一门必修课。
此外,为了了解计算机,网络也是与数据库不分伯仲的另一门必修课。接下来的第 9 章中,笔者将讲解网络相关的内容。敬请期待!
计算机是怎样跑起来的-第8章 一用就会的数据库
最新推荐文章于 2026-06-25 10:26:00 发布
1898

被折叠的 条评论
为什么被折叠?



