SQL权威指南(第4版)第1 章:数据库与文件系统_SQL权威指南(第4版)第1 章:数据库与文件系统试读-查字典图书网
查字典图书网
当前位置: 查字典 > 图书网 > 编程 > SQL权威指南(第4版) > 第1 章:数据库与文件系统

SQL权威指南(第4版)——第1 章:数据库与文件系统

数据库与文件系统 第1 章 给我们带来麻烦的往往不是我们完全不知道的事情,而是那些我们自以为了解了, 但其实不然的事情。 ——阿蒂默斯•沃德(1834—1867),美国作家、 幽默作家 数据库和RDBMS 与那些伴随着COBOL、FORTRAN、C、BASIC、PL/I、Java 等过程语言或面向对象语言一起出现的文件系统截然不同,SQL 语言本身没有I/O 系统,因此我们通常把SQL 解读为“几乎不能称为语言”(Scarcely Qualifies as a Language)。SQL 依赖于宿主语言,以便从终端用户获取数据或为其返回数据。 编程语言通常都以某一底层模型为基础。如果了解这个模型,就能更好地理解这门语言。例如,FORTRAN 语言建立在代数学理论之上,虽然这并不表明FORTRAN 语言酷似代数学,但如果你了解代数学,那么对FORTRAN 语言就不会完全陌生,你可以在赋值语言中写出表达式,也可以猜出那些没有见过的库函数所代表的意思。 在大多数其他编程语言中,程序员习惯于对文件进行处理。文件的设计源于纸质表格,这些文件都存储在计算机里并且非常依赖宿主编程语言。FORTRAN 语言很难读取COBOL 文件内容,反之亦然。事实上,在相同语言编写的程序之间共享文件也是非常困难的事情。文件的最基本形式是一组记录,这些记录按照一定的次序记录在文件里,而我们能够通过物理位置引用记录。我们可以打开一个文件,读取第一条记录,之后依次读取后续的记录,直到读完最后一个记录。当读完最后一个记录后再次读取将会返回EOF(End Of File)值。我们可以在这些记录中来回切换,并且一次操作一条记录,这些操作对于其他程序打开的其他文件没有任何影响,只有程序才能修改文件。SQL 语言的模型是集合数据,而不是物理文件。SQL 语言的“工作单位”是整个模式(schema),而不是单个表格。 我们在学校里学过数学中集合的概念。集合是无序的,集合中的成员都属于同一类型。当我们对集合进行操作时,这些操作一次作用于全部成员。这就意味着,当我要从正整数集合中选择奇数子集时,返回的是所有奇数值组成的一个集合。我并没有一个个地对整数进行扫描,然后将满足条件的奇数组成集合,仅仅是定义了奇数的规则“如果将整数除以2 时余数为1,那么该数为奇数”,这个规则可用于对任何整数进行奇数判定。集合模型具有很多优点,并行处理只是其中之一。 FORTRAN 并不是一门完美的代数学语言,同样地,SQL 也不是完美的集合语言,后面的 内容将展示这一点。但如果对SQL 语言中的某些内容存疑,考虑一下如何在集合中阐述这些内 容,也许就能得到正确的答案。 SQL 语言分三部分,即三个子语言: DDL(Data Declaration Language,数据描述语言); DML(Data Manipulation Language,数据操作语言); DCL(Data Control Language,数据控制语言)。 DDL 用于定义数据库的内容,并维护数据完整性。存储在文件中的数据并没有完整性约束、默认值以及关系的概念。与一个老程序员讨论如何使用FORTRAN 语言读取COBOL 文件,而且要求无误地获取操作结果,会得到这样的答案:如果一个程序“乱动”数据,那么另一个程序就会表现异常。 在DDL 语句上花费精力越多,你的RDBMS 就越能更好地工作。DDL 和 DML 及DCL 语言一起工作;SQL 是一个整体,而不是这三种语言无关联的简单拼凑。DML 是大多数读者执行查询、插入、更新以及删除操作并赖以为生的语言。如果对数据进 行了规范化处理,并创建了良好的模式,那么你在工作中将事半功倍。每次编译过程式语言代 码时候,操作步骤都是一样的, SQL 语言则不然,每次处理一个查询或其他语句时,执行计划 会根据数据库的当前状态而变更。正如柏拉图在《克拉底鲁篇》中所说:“所有事物都在改变, 没有永恒之物”(Everything flows, nothing stands still)。 DCL 不是数据安全相关的语言,而是一门关于访问控制的语言。DCL 语言并不加密数据;SQL 标准中没有提及数据加密,不过各个厂商为用户提供了一些可选方案。大多数SQL 书籍并不会强调DCL 语言,我也不打算在此花费太多篇幅。有必要为DCL 语言单独写一本微型书,它就好像是三脚凳中容易被忽视的那根支撑脚。也许以后我会写一本这样的书。现在让我们看一些基本概念。如果你对传统文件系统中数据处理的过程已经有所了解,那首先应该抛弃文件处理中的一些认识。 (1)数据库模式不是文件集合。各个文件之间不具有任何关系,应用程序负责处理文件之间的关系。SQL 标准没有提及任何与物理存储相关的问题,而文件建立于连续的物理存储之上。文件系统的出现伴随着打孔卡的产生,而磁带模拟出了文件系统,之后便是早期的文件系统。因为文件系统是所有问题的根源,所以我把它列入了第一条。 (2)数据表不是文件。表是模式的一部分,而模式是一个工作单元,不能在同一个模式下存在多个同名表。文件系统负责为加载到某一物理驱动器上的文件分配名称,而数据库中的表也有表名。文件本身是真实存在的,而表却可以是虚拟的(视图、通用表表达式,查询结果等)。 (3)行不是记录。记录的意义由读取它们的应用程序决定。记录是有先后次序的,所以可以对记录执行第一个、最后一个、下一个、前一个等操作;而行并没有物理顺序(ORDER BY 语句是游标的子句)。记录通过指针以及记录号指向其物理位置。行则通过关系键进行定位,关系键的概念建立在数据模型中属性子集唯一的基础之上。标准中并没有规定这一机制,因此在各个 SQL 版本中有很大差异。 1 (4)列不是字段。字段的意义是由读取字段的应用所决定的。对于不同的应用而言,字段可能具有不同的含义。记录中依次排列着各个字段,字段本身不具有数据类型、约束以及默认值。这就是主动数据与被动数据之间的对比!除此之外,列是可以为空的,而字段没有这样的概念。字段必须是客观存在的,而列可以是计算出来的或虚拟的。如果想有一个计算出来的列值,便可以通过应用程序进行计算,文件并不负责计算。另一个概念上的区别在于文件常常是整个业务流程的相关数据。文件自身必须包含足够的数据以辅助应用程序进行业务逻辑处理。文件往往都是混合数据,这些数据可以使用业务流程中的业务名称进行描述,例如,“工资表”或其他类似的名称。表可以是业务流程中的实体或关系。这意味着一个文件中的数据通常都会放置到多个表中。表倾向于存储可以用一个单词描述出来的纯数据,工资单可以通过员工打卡表、员工表、项目表等多个数据表进行描述。 1.1 实体表 实体是物理或逻辑上具有某一含义的事物。一个人、一笔买卖或一件货物都是实体。在关 系型数据库中,实体由它的属性定义。实体以表中行的形式体现,数据行中的每一列代表了实 体的一个属性。属性的值是一个标量值。为了提醒用户表是实体的集合,我倾向于使用描述实体功能的集合名词或复数名词定义系统中相关的表名。因此,“Employee”不是一个好的表名,因为这是单数形式,而复数形式的“Employees”更好些。“Personnel”最适合用作表名,因为它具有集合性,同时也不会被误以为是各自无关个人的集合。这个名字同时也符合ISO 11179 标准对元数据的要求。我的另外一本书《SQL 编程风格》中详细描述了这些标准。 如果多个表具有完全相同的结构,那么这些表是同一类元素的集合。但应该将同一类的数据元素归入到一个集合中!反之,文件是物理上独立的存储单元,这些文件可能非常相似:每个磁带 / 磁盘文件都代表了处理过程的一个步骤,如获取原始数据、编辑数据、最后将数据存档。而在SQL 中,这些应该是表中的一个状态标志。 1.2 关系表 在SQL 中,将表中的列指向一个或多个实体表,可以表现关联关系。脱离了实体,关系就没有意义,不过关系可以包含自身的属性。举例来说,一个演艺合同可能会涉及经纪人、雇主、演出人员。付款方式是合同本身的属性,而不是这三方的属性,这表明有一列必须引用其他表。文件和字段无法做到这点。 1.3 行与记录 行不同于记录。应用程序中定义并使用记录。而行则不然,行由数据库模式定义,而不是应用程序。应用程序中的读写语句将用到字段的名称。数据库模式为行进行命名。同样地,读取字段的物理顺序至关重要。读取a、b、c 字段与读取c、a、b 字段得到的内容是不同的,但在数据库中选择(select)a、b、c 与选择c、a、b 获得的数据是完全一致的。所有的空文件都很相似,它们都是操作系统中具有名称的目录项,这些空文件存储了0 B的内容。与它们相比,空表虽然没有数据行,但依然包含列、约束、安全权限以及其他结构。集合理论中空集是一个完全有效的集合,SQL 中这一点与集合理论一致。SQL 的集合模型与标准数学中的集合理论的差异之处在于,集合理论中只有一个空集合,而在SQL集合模型中,由于每个表都具有不同的数据结构,因此如果某处不能使用集合的非空版本,那么该集合的空集一定也不适用。同一表中的行还具有以下特点:这些行的数据结构完全相同,在模型中它们表示“同一类事物”。在文件系统中,记录在长短、数据类型以及结构上都有所不同,而数据流中的标志将告知应用程序如何解析数据。最常见的例子包括Pascal 中的变体记录(variant record),C 语言的struct 语法以及COBOL 的OCCURS 子句。 COBOL 语言中的OCCURS 关键字以及Pascal 语言中的VARIANT 记录都包含了一个数值,通过该数值告诉在程序当前的记录中,子记录结构的重复次数。C 语言中的联合体(union)虽然不是变体记录,但却可以基于相同的物理内存进行变体映射。例如: union x { int ival; char j[4];} mystuff; 该语句定义了mystuff 联合体,该联合体既可以是整型(在大多数C 编译器中是4 字节,不过这段代码是不可移植的),也可以是4 字节的数组,这取决于是通过mystuff.ival 还是mystuff.j[0] 调用它。比这更重要的差异是,文件通常都包含有汇总其他记录子集统计信息的记录,这也称为控制小计报表(control break report)。没有规定明确同一个文件中的记录需要以什么方式关联起来,文件只是包含二进制数据的数据流,读取文件的程序负责解释文件内容。 1.4 列与字段 记录中字段的含义由读取该字段的应用程序定义,而表中数据行的某一列的含义是由数据库模式定义的。列的数据类型总是标量。在READ 和INPUT 语句中,程序将依照应用程序变量在语句中的顺序读取这些变量,因此变量的顺序非常重要。在SQL 语言中,只能通过列名来引用列。虽然SELECT * 语句和INSERTINTO < 表名> 等语句中使用了会扩展成列名列表的缩写,这些扩展后的列名顺序与表定义中列名的顺序相同,不过这些缩写也仅仅是解析成命名列表的缩写而已。SQL 语言中NULL 值的作用不同于其他语言。字段中并不支持可作为字段、记录或文件本身一部分的“丢失数据标记”。除此之外,不能像SQL 的DEFAULT 和CHECK() 语句那样为记录的字段添加约束。文件本身是非常被动的,它会毫不反对地接受应用程序丢给它的所有内容。与此同时,文 件之间毫无关联,而这只是因为它们每次只与一个应用程序联系,所以并不知道其他文件是什 么样子。 数据库活动通过触发器、约束以及声明引用完整性等方式,“主动”寻求维护所有数据的正确性。 DRI(Declarative Referential Integrity,声明引用完整性)表明,表中的数据实际上与另一张表(也有可能是同一张表)的数据存有某一特定的关系。数据库可能通过与DRI 相关的引用操作进行自我更改。例如,可能会有这样一条业务规则,规定不能销售仓库中没有的产品。可以对订单(Orders)表使用REFERENCE 语句声明对仓库(Inventory)表的引用,并定义级联删除(ON DELETE CASCADE)引用操作,强制上述规则。与DRI 相比,触发器是更为通用的方式。触发器是一段程序代码,可以在INSERT INTO 和UPDATE 语句执行前、执行时或执行后运行。使用触发器除了可以完成DRI 可以完成的所有操作,还能完成其他功能。然而,使用触发器会带来一些问题。尽管从SQL-92 标准开始,就已经为触发器定义了标准的语法,但大多数厂商并没有实现这个语法,而是使用专有的语法。第二个问题是,触发器无法向优化器输入类似于DRI 的信息。在这一节的示例中,从Orders 表中可以知道每个产品编号,在Inventory 表中也使用相同的产品编号。优化器可以在设置EXISTS() 谓语以及往查询中添加联结时使用这些信息。但是我们无法通过解析过程式触发器代码确定产品编号间的关系。SQL-92 标准中出现的CREATE ASSERTION 语句允许数据库以整个库为单位,强制系统满足指定条件。断言不同于CHECK() 子句,不过其差异很难被觉察到。当往表里添加数据行时,将执行CHECK() 子句。如果表是空的, 那么所有的CHECK() 子句都将返回有效值TRUE。因此, 如果想确认Inventory 表非空,我们写下了以下语句。 CREATE TABLE Inventory ( … CONSTRAINT inventory_not_empty CHECK (( SELECT COUNT(*) FROM Inventory) > 0), … ); 但是这个语句不能成功执行,然而,当编写以下语句时: CREATE ASSERTION Inventory_not_empty CHECK (( SELECT COUNT(*) FROM Inventory) > 0); 我们获得了预期效果。这是因为断言不是在表级,而是在模式级进行检查的。 1.5 模式对象 尽管大多数数据库操作是基于表进行的,但数据库并不仅仅是数据表的集合。一个完整的数据库除了表,还包括存储过程、自定义函数以及用户创建的游标。此外还有索引和其他一些存取方法,而这些是用户不能直接使用的。本章将概述数据库用户可创建的模式对象。SQL 标准将数据库用户划分为普通用户(USER)和管理员用户(ADMIN), 模式对象的创建、修改、删除操作需要管理员权限,而普通用户则能够调用模式对象并存取调用结果。  1.6 CREATE SCHEMA 语句 SQL 标准定义了CREATE SCHEMA 语句,通过该语句能够一次性创建一个完整的数据库模式,事实上,几乎每种数据库产品都提供了辅助程序,帮助用户分配物理存储并创建数据库模式。这些辅助程序通常具有较大的差异,其中一些使用专有的语法来进行物理存储的分配。数据库模式必须具有名称和默认字符集(多年前通常是8 位的ASCII 字符集或ISO 标准定义的简单拉丁字符集)。现在更常见的是Unicode(16 位)字符集。CREATE SCHEMA 语句提供了一个可选的“ 授权”子句,为安全性创建了一个访问控制所需的授权标识符。在此之后,就是一组模式元素的集合: < 模式元素> ::= < 域定义> | < 数据表定义> | < 视图定义> | < 授权语句> | < 断言定义> | < 字符集定义> | < 排序规则定义> | < 翻译定义> 在SQL 数据库中,模式就好比是数据库的骨架,它定义了模式对象的结构以及操作规则,而模式中的各类数据就好比是依附在骨骼之上的皮肉。表是SQL 语言中唯一的数据结构,SQL 中的表可以是持久化的(基础表), 可以用于临时存储(临时表),可以是虚拟表(视图、公用表表达式和衍生表)。以上这些表在性能上没有差异,但在实现上是不同的。SQL 语言只使用一种数据结构(表),这大大简化了SQL 处理。由于所有操作的返回类型也是表,因此无需对返回结果进行结构转换、编写特殊的操作符或是处理语言中的不规则问题。< 授权语句> 限制用户只能访问特定的模式元素;断言可以被看做应用于整个模式的约束条件(constraint),< 断言定义> 目前并没有广泛使用。而< 字符集定义>、< 排序规则定义> 和< 翻译定义> 则用于处理数据的显示。开发人员和普通用户并不需要关心上述模式对象,也不需要对这些对象进行设置,DBA(数据库管理员)将负责这些对象的设置和维护工作。从概念上讲,表是零行或多行数据的集合,而行是一列或多列的集合。这个层次关系很重要。我们可以在模式、表、行或列级别上执行操作。例如DELETE FROM(删除语句)并不会移除列,而是将行移除,并留下模式中的基础表。无法删除行中的某列。 每一列都有一个特定的数据类型以及约束,该数据类型及约束便构成了某一抽象域的具体实现。由于只能通过SQL 访问表,因此表的物理实现方式并不重要。数据库引擎负责处理所有的细节,因此不必担心处理文件时所需要处理的内部事件。事实上,几乎没有两款SQL 产品会使用相同的内部数据结构。 对于习惯于文件系统或PC 机的程序员而言,有两个常犯的概念性错误:第一个是认为表就是文件;第二个是认为表是电子表格。表与这两者表现都不相同。如果不了解这些基本概念,那你早晚会遇到麻烦。很容易将表想象成文件,把行想象成记录,把列想象成字段;这些概念让人觉得熟悉。当将SQL 中的数据转移到宿主语言时,这些数据必须转换成宿主语言数据类型和数据结构,以供 显示和使用。而宿主语言也都有内建的文件系统。使用文件系统和SQL 的一大区别在于,宿主语言加载SQL 的方式不同。在使用文件系统时,应用程序需要独立地打开 / 关闭各个文件,而使用SQL 时,程序每次执行连接 / 断开连接操作时,最小的工作单位是整个模式。虽然由于权限的关系,宿主程序也许无法查看或操作所有的表和其他模式对象,但这些对象已经是建立连接后加载数据的一部分了。 程序在文件中定义字段,而SQL 在模式中定义列。FORTRAN 语言使用FORMAT 和READ 语句从文件中获取数据,与之相似,COBOL 程序在数据部(data division)中定义字段,并使用READ 语句读取字段。诸如此类,对于每一种第三代编程语言而言,文件读写的概念都是相同的,只是在语法和选项上有所差异。 在每个程序中,文件系统使我们可以用不同的名称引用同一数据。如果文件的布局发生了变化,就必须重写使用该文件的所有程序。另外,所有的空文件都没有区别。试图读取空文件时,程序将根据返回的EOF(文件末尾)标志执行后续操作。数据库模式定义了表的列名和数据类型,在合理的限度下,在宿主程序不知情的情况下可以对数据表进行修改。宿主程序只需要关心如何将数据库中的数值转变成自己的变量。还记得高中数学课上学到的空集吗?空集也是一种有效集合。当一个表没有数据时,虽然它没有数据行,但仍包含列。因为表没有最后记录,因此不会通过设置EOF 标志抛出异常。数据库与文件系统的另一大区别是,前者能够为表和列设置约束。约束是定义每次事务后数据库都需要满足哪些要求的规则。这也意味着,相对于被动的文件系统,数据库更像是对象的集合。 表并不是电子表格,尽管在屏幕上或打印输出时,它们看起来非常相似。我可以访问电子表格的行、列、单元格,也可以通过鼠标光标导航访问一组单元格。表没有导航的概念。电子表格中的单元格除了存储数据,还能存储指令。电子表格中的行和列并没有实质的区别,将行和列翻转之后,仍然可以获得正确的结果。SQL 表则不然。 它们唯一的共性是,电子表格也是一个声明性编程语言,只不过恰巧是一门非线性语言。

展开全文


推荐文章

猜你喜欢

附近的人在看

推荐阅读

拓展阅读

《SQL权威指南(第4版)》其他试读目录

• 第1 章:数据库与文件系统 [当前]
• 前言
  • 大家都在看
  • 小编推荐
  • 猜你喜欢
  •