提交 e6f8f268 编写于 作者: J jiajia.debug

ch2 50%

上级 bae11bab
......@@ -40,13 +40,13 @@
当时的其他数据库迫使应用程序开发人员必须考虑数据库内部的数据表示形式。关系模型致力于将上述实现细节隐藏在更简洁的接口之后。
多年来,在数据存储和查询方面存在着许多相互竞争的方法。在20世纪70年代和80年代初,网络模型和分层模型曾是主要的选择,但关系模型随后占据了主导地位。对象数据库在20世纪80年代末和90年代初来了又去。 XML数据库在二十一世纪初出现,但只有小众采用过。关系模型的每个竞争者都在其时代产生了大量的炒作,但从来没有持续【2】。
多年来,在数据存储和查询方面存在着许多相互竞争的方法。在20世纪70年代和80年代初,网络模型和分层模型曾是主要的选择,但关系模型随后占据了主导地位。对象数据库在20世纪80年代末和90年代初来了又去。XML数据库在二十一世纪初出现,但只有小众采用过。关系模型的每个竞争者都在其时代产生了大量的炒作,但从来没有持续【2】。
随着电脑越来越强大和互联,它们开始用于日益多样化的目的。关系数据库非常成功地被推广到业务数据处理的原始范围之外更为广泛的用例上。您今天在网上看到的大部分内容依旧是由关系数据库来提供支持,无论是在线发布,讨论,社交网络,电子商务,游戏,软件即服务生产力应用程序等等内容。
### NoSQL的诞生
现在 - 2010年代,NoSQL开始了最新一轮尝试,试图推翻关系模型的统治地位。 “NoSQL”这个名字让人遗憾,因为实际上它并没有涉及到任何特定的技术。最初它只是作为一个醒目的Twitter标签,用在2009年一个关于分布式,非关系数据库上的开源聚会上。无论如何,这个术语触动了某些神经,并迅速在网络创业社区内外传播开来。好些有趣的数据库系统现在都与*#NoSQL#*标签相关联,并且NoSQL被追溯性地重新解释为**不仅是SQL(Not Only SQL)** 【4】。
现在 - 2010年代,NoSQL开始了最新一轮尝试,试图推翻关系模型的统治地位。“NoSQL”这个名字让人遗憾,因为实际上它并没有涉及到任何特定的技术。最初它只是作为一个醒目的Twitter标签,用在2009年一个关于分布式,非关系数据库上的开源聚会上。无论如何,这个术语触动了某些神经,并迅速在网络创业社区内外传播开来。好些有趣的数据库系统现在都与*#NoSQL#*标签相关联,并且NoSQL被追溯性地重新解释为**不仅是SQL(Not Only SQL)** 【4】。
采用NoSQL数据库的背后有几个驱动因素,其中包括:
......@@ -69,10 +69,10 @@
**图2-1 使用关系型模式来表示领英简介**
例如,[图2-1](img/fig2-1.png)展示了如何在关系模式中表示简历(一个LinkedIn简介)。整个简介可以通过一个唯一的标识符`user_id`来标识。像`first_name``last_name`这样的字段每个用户只出现一次,所以他们可以在用户表上建模为列。但是,大多数人在职业生涯中拥有多于一份的工作,人们可能有不同样的教育阶段和任意数量的联系信息。从用户到这些项目之间存在一对多的关系,可以用多种方式来表示:
例如,[图2-1](img/fig2-1.png)展示了如何在关系模式中表示简历(一个LinkedIn简介)。整个简介可以通过一个唯一的标识符`user_id`来标识。像`first_name``last_name`这样的字段每个用户只出现一次,所以可以在User表上将其建模为列。但是,大多数人在职业生涯中拥有多于一份的工作,人们可能有不同样的教育阶段和任意数量的联系信息。从用户到这些项目之间存在一对多的关系,可以用多种方式来表示:
* 传统SQL模型(SQL:1999之前)中,最常见的规范化表示形式是将职位,教育和联系信息放在单独的表中,对用户表提供外键引用,如[图2-1](img/fig2-1.png)所示。
* 后续的SQL标准增加了对结构化数据类型和XML数据的支持;这允许将多值数据存储在单行内,并支持在这些文档内查询和索引。这些功能在Oracle,IBM DB2,MS SQL Server和PostgreSQL中都有不同程度的支持【6,7】。 JSON数据类型也得到多个数据库的支持,包括IBM DB2,MySQL和PostgreSQL 【8】。
* 传统SQL模型(SQL:1999之前)中,最常见的规范化表示形式是将职位,教育和联系信息放在单独的表中,对User表提供外键引用,如[图2-1](img/fig2-1.png)所示。
* 后续的SQL标准增加了对结构化数据类型和XML数据的支持;这允许将多值数据存储在单行内,并支持在这些文档内查询和索引。这些功能在Oracle,IBM DB2,MS SQL Server和PostgreSQL中都有不同程度的支持【6,7】。JSON数据类型也得到多个数据库的支持,包括IBM DB2,MySQL和PostgreSQL 【8】。
* 第三种选择是将职业,教育和联系信息编码为JSON或XML文档,将其存储在数据库的文本列中,并让应用程序解析其结构和内容。这种配置下,通常不能使用数据库来查询该编码列中的值。
对于一个像简历这样自包含文档的数据结构而言,JSON表示是非常合适的:参见[例2-1]()。JSON比XML更简单。面向文档的数据库(如MongoDB 【9】,RethinkDB 【10】,CouchDB 【11】和Espresso【12】)支持这种数据模型。
......@@ -118,9 +118,9 @@
有一些开发人员认为JSON模型减少了应用程序代码和存储层之间的阻抗不匹配。不过,正如我们将在[第4章](ch4.md)中看到的那样,JSON作为数据编码格式也存在问题。缺乏一个模式往往被认为是一个优势;我们将在“[文档模型中的模式灵活性](#文档模型中的模式灵活性)”中讨论这个问题。
JSON表示比[图2-1](img/fig2-1.png)中的多表模式具有更好的**局部性(locality)**。如果在上面的关系型示例中获取简介,那需要执行多个查询(通过`user_id`查询每个表),或者在用户表与其下属表之间混乱地执行多路连接。而在JSON表示中,所有相关信息都在同一个地方,一个查询就足够了。
JSON表示比[图2-1](img/fig2-1.png)中的多表模式具有更好的**局部性(locality)**。如果在前面的关系型示例中获取简介,那需要执行多个查询(通过`user_id`查询每个表),或者在User表与其下属表之间混乱地执行多路连接。而在JSON表示中,所有相关信息都在同一个地方,一个查询就足够了。
从用户简介文件到用户职位,教育历史和联系信息,这种一对多关系隐含了数据中的树状结构,而JSON表示使得这个树状结构变得明确(见[图2-2](img/fig2-2.png))。
从用户简介文件到用户职位,教育历史和联系信息,这种一对多关系隐含了数据中的一个树状结构,而JSON表示使得这个树状结构变得明确(见[图2-2](img/fig2-2.png))。
![](img/fig2-2.png)
......@@ -140,7 +140,7 @@ JSON表示比[图2-1](img/fig2-1.png)中的多表模式具有更好的**局部
存储ID还是文本字符串,这是个**复制(duplication)**问题。当使用ID时,对人类有意义的信息(比如单词:Philanthropy)只存储在一处,所有引用它的地方使用ID(ID只在数据库中有意义)。当直接存储文本时,对人类有意义的信息会复制在每处使用记录中。
使用ID的好处是,ID对人类没有任何意义,永远不需要改变:ID可以保持不变,即使它标识的信息发生变化。任何对人类有意义的东西都可能需要在将来某个时候改变——如果这些信息被复制,所有的冗余副本都需要更新。这会导致写入开销,也存在不一致的风险(一些副本被更新了,还有些副本没有被更新)。去除此类重复是数据库**规范化(normalization)**的关键思想。[^ii]
使用ID的好处是,ID对人类没有任何意义,因而永远不需要改变:ID可以保持不变,即使它标识的信息发生变化。任何对人类有意义的东西都可能需要在将来某个时候改变——如果这些信息被复制,所有的冗余副本都需要更新。这会导致写入开销,也存在不一致的风险(一些副本被更新了,还有些副本没有被更新)。去除此类重复是数据库**规范化(normalization)**的关键思想。[^ii]
[^ii]: 关于关系模型的文献区分了几种不同的规范形式,但这些区别几乎没有实际意义。一个经验法则是,如果重复存储了可以存储在一个地方的值,则模式就不是**规范化(normalized)**的。
......@@ -160,20 +160,20 @@ JSON表示比[图2-1](img/fig2-1.png)中的多表模式具有更好的**局部
***推荐***
假设你想添加一个新的功能:一个用户可以为另一个用户写一个推荐。在用户的简历上显示推荐,并附上推荐用户的姓名和照片。如果推荐人更新他们的照片,他们写的任何建议都需要显示新的照片。因此,推荐应该有作者个人简介的引用。
假设你想添加一个新的功能:一个用户可以为另一个用户写一个推荐。在用户的简历上显示推荐,并附上推荐用户的姓名和照片。如果推荐人更新他们的照片,那他们写的任何建议都需要显示新的照片。因此,推荐应该拥有作者个人简介的引用。
![](img/fig2-3.png)
**图2-3 公司名不仅是字符串,还是一个指向公司实体的链接(LinkedIn截图)**
[图2-4](img/fig2-4.png)阐明了这些新功能怎样使用多对多关系。 每个虚线矩形内的数据可以分组成一个文档,但是对单位,学校和其他用户的引用需要表示成引用,并且在查询时需要连接。
[图2-4](img/fig2-4.png)阐明了这些新功能需要如何使用多对多关系。每个虚线矩形内的数据可以分组成一个文档,但是对单位,学校和其他用户的引用需要表示成引用,并且在查询时需要连接。
![](img/fig2-4.png)
**图2-4 使用多对多关系扩展简历**
### 文档数据库是否在重演历史
### 文档数据库是否在重蹈覆辙
在多对多的关系和连接已常规用在关系数据库时,文档数据库和NoSQL重启了辩论:如何最好地在数据库中表示多对多关系。这个辩论可比NoSQL古老得多,事实上,最早可以追溯到计算机化数据库系统。
在多对多的关系和连接已常规用在关系数据库时,文档数据库和NoSQL重启了辩论:如何最好地在数据库中表示多对多关系。那场辩论可比NoSQL古老得多,事实上,最早可以追溯到计算机化数据库系统。
20世纪70年代最受欢迎的业务数据处理数据库是IBM的信息管理系统(IMS),最初是为了阿波罗太空计划的库存管理而开发的,并于1968年有了首次商业发布【13】。目前它仍在使用和维护,运行在IBM大型机的OS/390上【14】。
......@@ -203,19 +203,19 @@ CODASYL中的查询是通过利用遍历记录列和跟随访问路径表在数
相比之下,关系模型做的就是将所有的数据放在光天化日之下:一个**关系(表)**只是一个**元组(行)**的集合,仅此而已。如果你想读取数据,它没有迷宫似的嵌套结构,也没有复杂的访问路径。你可以选中符合任意条件的行,读取表中的任何或所有行。你可以通过指定某些列作为匹配关键字来读取特定行。你可以在任何表中插入一个新的行,而不必担心与其他表的外键关系[^iv]。
[^iv]: 外键约束允许对修改做限制,对于关系模型这并不是必选项。即使有约束,查询时会执行外键连接,而在CODASYL中,连接在插入时高效完成。
[^iv]: 外键约束允许对修改约束,但对于关系模型这并不是必选项。即使有约束,外键连接在查询时执行,而在CODASYL中,连接在插入时高效完成。
在关系数据库中,查询优化器自动决定查询的哪些部分以哪个顺序执行,以及使用哪些索引。这些选择实际上是“访问路径”,但最大的区别在于它们是由查询优化器自动生成的,而不是由程序员生成,所以我们很少需要考虑它们。
如果想按新的方式查询数据,可以声明一个新的索引,查询会自动使用最合适的那些索引。无需更改查询来利用新的索引。 (请参阅“[用于数据的查询语言](#用于数据的查询语言)”。)关系模型因此使添加应用程序新功能变得更加容易。
如果想按新的方式查询数据,你可以声明一个新的索引,查询会自动使用最合适的那些索引。无需更改查询来利用新的索引。(请参阅“[用于数据的查询语言](#用于数据的查询语言)”。)关系模型因此使添加应用程序新功能变得更加容易。
关系数据库的查询优化器是复杂的,耗费多年的研究和开发精力【18】。关系模型的一个关键洞察是:只需构建一次查询优化器,随后使用该数据库的所有应用程序都可以从中受益。如果你没有查询优化器的话,那么为特定查询手动编写访问路径比编写通用优化器更容易——不过通用解决方案从长期看更好。
关系数据库的查询优化器是复杂的,已耗费了多年的研究和开发精力【18】。关系模型的一个关键洞察是:只需构建一次查询优化器,随后使用该数据库的所有应用程序都可以从中受益。如果你没有查询优化器的话,那么为特定查询手动编写访问路径比编写通用优化器更容易——不过从长期看通用解决方案更好。
#### 与文档数据库相比
在一个方面,文档数据库还原为层次模型:在其父记录中存储嵌套记录([图2-1]()中的一对多关系,如`positions`,`education`和`contact_info`),而不是在单独的表中。
但是,在表示多对一和多对多的关系时,关系数据库和文档数据库并没有根本的不同:在这两种情况下,相关项目都被一个唯一的标识符引用,这个标识符在关系模型中被称为**外键**,在文档模型中称为**文档引用**【9】。该标识符在读取时通过连接或后续查询来解析。迄今为止,文档数据库没有遵循CODASYL的路数
但是,在表示多对一和多对多的关系时,关系数据库和文档数据库并没有根本的不同:在这两种情况下,相关项目都被一个唯一的标识符引用,这个标识符在关系模型中被称为**外键**,在文档模型中称为**文档引用**【9】。该标识符在读取时通过连接或后续查询来解析。迄今为止,文档数据库没有走CODASYL的老路
### 关系型数据库与文档数据库在今日的对比
......@@ -225,25 +225,25 @@ CODASYL中的查询是通过利用遍历记录列和跟随访问路径表在数
#### 哪个数据模型更方便写代码?
如果应用程序中的数据具有类似文档的结构(即,一对多关系树,通常一次性加载整个树),那么使用文档模型可能是一个好主意。将类似文档的结构分解成多个表(如[图2-1](img/fig2-1.png)中的位置,教育`contact_info`)的关系技术可能导致繁琐的模式和不必要的复杂的应用程序代码。
如果应用程序中的数据具有类似文档的结构(即,一对多关系树,通常一次性加载整个树),那么使用文档模型可能是一个好主意。将类似文档的结构分解成多个表(如[图2-1](img/fig2-1.png)中的`positions``education``contact_info`)的关系技术可能导致繁琐的模式和不必要的复杂的应用程序代码。
文档模型有一定的局限性:例如,不能直接引用文档中的嵌套的项目,而是需要说“用户251的位置列表中的第二项”(很像分层模型中的访问路径)。但是,只要文件嵌套不太深,通常不是问题。
文档模型有一定的局限性:例如,不能直接引用文档中的嵌套的项目,而是需要说“用户251的位置列表中的第二项”(很像分层模型中的访问路径)。但是,只要文件嵌套不太深,通常不是问题。
对文档数据库连接的糟糕支持也许或也许不是一个问题,这取决于应用程序。例如,分析应用程可能永远不需要多对多的关系,如果它使用文档数据库来记录何事发生于何时【19】。
文档数据库对连接的糟糕支持也许或也许不是一个问题,这取决于应用程序。例如,分析应用程可能永远不需要多对多的关系,如果它使用文档数据库来记录何事发生于何时【19】。
但是,如果您的应用程序确实使用多对多关系,那么文档模型就没有那么吸引人了。通过反规范化可以减少对连接的需求,但是应用程序代码需要做额外的工作来保持数据的一致性。通过向数据库发出多个请求,可以在应用程序代码中模拟连接,但是这也将复杂性转移到应用程序中,并且通常比由数据库内的专用代码执行的连接慢。在这种情况下,使用文档模型会导致更复杂的应用程序代码和更差的性能【15】。
很难说在一般情况下哪个数据模型让应用程序代码更简单;它取决于数据项之间存在的关系种类。对于高度相联的数据,文档模型是糟糕的,关系模型是可接受的,而图形模型(参见“[图数据模型](#图数据模型)”)是最自然的。
很难说在一般情况下哪个数据模型让应用程序代码更简单;它取决于数据项之间存在的关系种类。对于高度相联的数据,选用文档模型是糟糕的,选用关系模型是可接受的,而选用图形模型(参见“[图数据模型](#图数据模型)”)是最自然的。
#### 文档模型中的架构灵活性
大多数文档数据库以及关系数据库中的JSON支持都不会强制文档中的数据采用何种模式。关系数据库的XML支持通常带有可选的模式验证。没有模式意味着可以将任意的键和值添加到文档中,并且当读取时,客户端对无法保证文档可能包含的字段。
大多数文档数据库以及关系数据库中的JSON支持都不会强制文档中的数据采用何种模式。关系数据库的XML支持通常带有可选的模式验证。没有模式意味着可以将任意的键和值添加到文档中,并且当读取时,客户端对无法保证文档可能包含的字段。
文档数据库有时称为**无模式(schemaless)**,但这是误导性的,因为读取数据的代码通常假定某种结构——即存在隐式模式,但不由数据库强制执行【20】。一个更精确的术语是**读时模式(schema-on-read)**(数据的结构是隐含的,只有在数据被读取时才被解释),相应的是**写时模式(schema-on-write)**(传统的关系数据库方法,模式明确,且数据库确保所有的数据都符合其模式)【21】。
文档数据库有时称为**无模式(schemaless)**,但这具有误导性,因为读取数据的代码通常假定某种结构——即存在隐式模式,但不由数据库强制执行【20】。一个更精确的术语是**读时模式(schema-on-read)**(数据的结构是隐含的,只有在数据被读取时才被解释),相应的是**写时模式(schema-on-write)**(传统的关系数据库方法中,模式明确,且数据库确保所有的数据都符合其模式)【21】。
取模式类似于编程语言中的动态(运行时)类型检查,而模式写入类似于静态(编译时)类型检查。就像静态和动态类型检查的相对优点具有很大的争议性【22】,数据库中模式的强制性是一个具有争议的话题,一般来说没有正确或错误的答案。
时模式类似于编程语言中的动态(运行时)类型检查,而写时模式类似于静态(编译时)类型检查。就像静态和动态类型检查的相对优点具有很大的争议性一样【22】,数据库中模式的强制性是一个具有争议的话题,一般来说没有正确或错误的答案。
在应用程序想要改变其数据格式的情况下,这些方法之间的区别特别明显。例如,假设你把每个用户的全名存储在一个字段中,而现在想分别存储名字和姓氏【23】。在文档数据库中,只需开始写入具有新字段的新文档,并在应用程序中使用代码来处理读取旧文档时的情况。例如:
在应用程序想要改变其数据格式的情况下,这些方法之间的区别尤其明显。例如,假设你把每个用户的全名存储在一个字段中,而现在想分别存储名字和姓氏【23】。在文档数据库中,只需开始写入具有新字段的新文档,并在应用程序中使用代码来处理读取旧文档的情况。例如:
```go
if (user && user.name && !user.first_name) {
......@@ -260,48 +260,48 @@ UPDATE users SET first_name = split_part(name, ' ', 1); -- PostgreSQL
UPDATE users SET first_name = substring_index(name, ' ', 1); -- MySQL
```
模式变更的速度很慢,而且需要停运。这种坏声誉并不是完全应得的:大多数关系数据库系统可在几毫秒内执行`ALTER TABLE`语句。 MySQL是一个值得注意的例外,它执行`ALTER TABLE`时会复制整个表,这可能意味着在更改一个大表时会花费几分钟甚至几个小时的停机时间,尽管存在各种工具可以解决这个限制【24,25,26】。
模式变更的速度很慢,而且要求停运。它的这种坏名誉并不是完全应得的:大多数关系数据库系统可在几毫秒内执行`ALTER TABLE`语句。MySQL是一个值得注意的例外,它执行`ALTER TABLE`时会复制整个表,这可能意味着在更改一个大型表时会花费几分钟甚至几个小时的停机时间,尽管存在各种工具来解决这个限制【24,25,26】。
在大型表上运行`UPDATE`语句在任何数据库上都可能会很慢,因为每一行都需要重写。如果这是不可接受的,应用程序可以将`first_name`设置为默认值`NULL`,并在读取时再填充,就像使用文档数据库一样。
大型表上运行`UPDATE`语句在任何数据库上都可能会很慢,因为每一行都需要重写。要是不可接受的话,应用程序可以将`first_name`设置为默认值`NULL`,并在读取时再填充,就像使用文档数据库一样。
读时模式更具优势,当由于某种原因(例如,数据是异构的)集合中的项目并不都具有相同的结构例如,因为:
读时模式更具优势,当由于某种原因(例如,数据是异构的)集合中的项目并不都具有相同的结构时。例如,因为:
* 许多不同类型的对象,将每种类型的对象放在自己的表中是不现实的。
* 数据的结构由您无法控制且随时可能变化的外部系统决定
* 存在许多不同类型的对象,将每种类型的对象放在自己的表中是不现实的。
* 数据的结构由外部系统决定。您无法控制外部系统且它随时可能变化
在这样的情况下,模式的伤害远大于它的帮助,无模式文档可能是一个更加自然的数据模型。但是,如果所有记录都具有相同的结构,那么模式就是记录并强制这种结构的有用机制。第四章将更详细地讨论模式和模式演化。
在这样的情况下,模式的坏处远大于它的帮助,无模式文档可能是一个更加自然的数据模型。但是,要是所有记录都具有相同的结构,那么模式是记录并强制这种结构的有效机制。第四章将更详细地讨论模式和模式演化。
#### 查询的数据局部性
文档通常以单个连续字符串形式存储,编码为JSON,XML或其二进制变体(如MongoDB的BSON)。如果您的应用程序经常需要访问整个文档(例如,将其渲染至网页),则此存储局部性会带来性能优势。如果将数据分割到多个表中(如[图2-1](img/fig2-1.png)所示),则需要进行多次索引查找才能将其全部检索出来,这可能需要更多的磁盘查找并花费更多的时间。
文档通常以单个连续字符串形式进行存储,编码为JSON,XML或其二进制变体(如MongoDB的BSON)。如果应用程序经常需要访问整个文档(例如,将其渲染至网页),那么存储局部性会带来性能优势。如果将数据分割到多个表中(如[图2-1](img/fig2-1.png)所示),则需要进行多次索引查找才能将其全部检索出来,这可能需要更多的磁盘查找并花费更多的时间。
局部性仅适用于需要文档绝大部分内容的情况。数据库通常需要加载整个文档,即使只访问其中的一小部分,这对于大型文档来说是很浪费的。更新文档时,通常需要整个重写。 只有不改变文档大小的修改才可以容易地原地执行。因此通常建议,保持相对小的文档,并避免增加文档的大小的写入【9】。这些性能限制大大减少了文档数据库的实用场景。
局部性仅仅适用于同时需要文档绝大部分内容的情况。数据库通常需要加载整个文档,即使只访问其中的一小部分,这对于大型文档来说是很浪费的。更新文档时,通常需要整个重写。只有不改变文档大小的修改才可以容易地原地执行。因此,通常建议保持相对小的文档,并避免增加文档大小的写入【9】。这些性能限制大大减少了文档数据库的实用场景。
值得指出的是,将相关的数据分组在一起的想法并不局限于文档模型。例如,Google的Spanner数据库在关系数据模型中提供了相同的位置属性,允许模式声明一个表的行应该在父表内**交织(interleaved)**(嵌套)【27】。 Oracle允许使用一个称为**多表索引集群表(multi-table index cluster tables)**的类似特性【28】。 Bigtable数据模型(在Cassandra和HBase中使用)中的**列族(column-family)**概念与管理局部性的目的类似【29】。
值得指出的是,为了局部性而分组集合相关数据的想法并不局限于文档模型。例如,Google的Spanner数据库在关系数据模型中提供了同样的局部性属性,允许模式声明一个表的行应该交错(嵌套)在父表内【27】。Oracle类似地允许使用一个称为**多表索引集群表(multi-table index cluster tables)**的类似特性【28】。Bigtable数据模型(用于Cassandra和HBase)中的**列族(column-family)**概念与管理局部性的目的类似【29】。
[第3章](ch3.md)我们还会看到更多关于本地的内容。
[第3章](ch3.md)将还会看到更多关于局部性的内容。
#### 文档和关系数据库的融合
自2000年代以来,大多数关系数据库系统(MySQL除外)都支持XML。这包括对XML文档进行本地修改的功能,以及在XML文档中进行索引和查询的功能,这允许应用程序使用与使用文档数据库时所做的非常相似的数据模型。
自2000年代中期以来,大多数关系数据库系统(MySQL除外)都已支持XML。这包括对XML文档进行本地修改的功能,以及在XML文档中进行索引和查询的功能。这允许应用程序使用那种与文档数据库应当使用的非常类似的数据模型。
从9.3版本开始的PostgreSQL 【8】,从5.7版本开始的MySQL以及从版本10.5开始的IBM DB2 [30]也对JSON文档提供了类似的支持级别。鉴于Web API的JSON流行,其他关系数据库很可能会跟随他们的脚步并添加JSON支持。
从9.3版本开始的PostgreSQL 【8】,从5.7版本开始的MySQL以及从版本10.5开始的IBM DB2 [30]也对JSON文档提供了类似的支持级别。鉴于用在Web APIs的JSON流行趋势,其他关系数据库很可能会跟随他们的脚步并添加JSON支持。
在文档数据库方面,RethinkDB支持其查询语言中的类似关系的连接,一些MongoDB驱动程序自动解决数据库引用(有效地执行客户端连接,尽管这可能比在数据库中执行的连接慢,需要额外的网络往返,并且没有优化)。
在文档数据库中,RethinkDB在其查询语言中支持类似关系的连接,一些MongoDB驱动程序可以自动解析数据库引用(有效地执行客户端连接,尽管这可能比在数据库中执行的连接慢,需要额外的网络往返,并且优化更少)。
似乎随着时间的推移,关系数据库和文档数据库变得越来越相似,这是一件好事:数据模型相互补充[^v],如果一个数据库能够处理类似文档的数据,并对其执行关系查询,那么应用可以使用最适合其需要的特征的组合。关系模型和文档模型的混合是数据库未来的一个很好的途径
随着时间的推移,关系数据库和文档数据库似乎变得越来越相似,这是一件好事:数据模型相互补充[^v],如果一个数据库能够处理类似文档的数据,并能够对其执行关系查询,那么应用程序就可以使用最符合其需求的功能组合
[^v]: Codd对关系模型【1】的原始描述实际上允许在关系模式中与JSON文档非常相似。 他称之为**非简单域(nonsimple domains)**。 这个想法是,一行中的值不一定是一个像数字或字符串一样的原始数据类型,也可以是一个嵌套的关系(表格)所以你可以有一个任意嵌套的树结构作为一个值, 很像30年后添加到SQL中的JSON或XML支持
关系模型和文档模型的混合是未来数据库一条很好的路线
Codd对关系模型【1】的原始描述实际上允许在关系模式中与JSON文档非常相似。 他称之为**非简单域
[^v]: Codd对关系模型【1】的原始描述实际上允许在关系模式中与JSON文档非常相似。他称之为**非简单域(nonsimple domains)**。这个想法是,一行中的值不一定是一个像数字或字符串一样的原始数据类型,也可以是一个嵌套的关系(表),因此可以把一个任意嵌套的树结构作为一个值,这很像30年后添加到SQL中的JSON或XML支持。
## 数据查询语言
当引入关系模型时,它包含了一种查询数据的新方法:SQL是一个声明式查询语言,而IMS和CODASYL使用命令式的代码来查询数据库。那是什么意思?
当引入关系模型时,关系模型包含了一种查询数据的新方法:SQL是一种**声明式**查询语言,而IMS和CODASYL使用**命令式**代码来查询数据库。那是什么意思?
许多常用的编程语言是命令式的。例如,如果你有一个动物物种的列表,返回列表中的鲨鱼可以这样写:
许多常用的编程语言是命令式的。例如,给定一个动物物种的列表,返回列表中的鲨鱼可以这样写:
```js
function getSharks() {
......@@ -321,29 +321,29 @@ sharks = σ_{family = "sharks"}(animals)
$$
σ(希腊字母西格玛)是选择操作符,只返回符合条件的动物,`family="shark"`
定义SQL时,它紧密地遵循关系代数的结构:
定义SQL时,它紧密地遵循关系代数的结构:
```sql
SELECT * FROM animals WHERE family ='Sharks';
```
命令式语言告诉计算机以特定顺序执行某些操作。可以想象一下,逐行地遍历代码,评估条件,更新变量,并决定是否再循环一遍。
命令式语言告诉计算机以特定顺序执行某些操作。可以想象一下,逐行地遍历代码,评估条件,更新变量,并决定是否再循环一遍。
在声明式查询语言(如SQL或关系代数)中,您只需指定所需数据的模式 - 结果必须符合哪些条件,以及如何将数据转换(例如,排序,分组和集合)但不是如何实现这一目标。数据库系统的查询优化器决定使用哪些索引和哪些连接方法,以及以何种顺序执行查询的各个部分。
在声明式查询语言(如SQL或关系代数)中,您只需指定所需数据的模式 - 结果必须符合哪些条件,以及如何将数据转换(例如,排序,分组和集合) - 但不是如何实现这一目标。数据库系统的查询优化器决定使用哪些索引和哪些连接方法,以及以何种顺序执行查询的各个部分。
声明式查询语言是有吸引力的,因为它通常比命令式API更加简洁和容易。但更重要的是,它还隐藏了数据库引擎的实现细节,这使得数据库系统可以在不需要对查询进行任何更改的情况下提高性能
声明式查询语言是迷人的,因为它通常比命令式API更加简洁和容易。但更重要的是,它还隐藏了数据库引擎的实现细节,这使得数据库系统可以在无需对查询做任何更改的情况下进行性能提升
例如,在本节开头所示的命令代码中,动物列表以特定顺序出现。如果数据库想要在场景后面回收未使用的磁盘空间,则可能需要移动记录,改变动物出现的顺序。数据库能否安全地执行,而不会中断查询?
例如,在本节开头所示的命令代码中,动物列表以特定顺序出现。如果数据库想要在后台回收未使用的磁盘空间,则可能需要移动记录,这会改变动物出现的顺序。数据库能否安全地执行,而不会中断查询?
SQL示例不保证任何特定的顺序,所以它不介意顺序是否改变。但是如果查询被写为命令式的代码,那么数据库就永远不能确定代码是否依赖于排序。 SQL在功能上更加有限的事实为数据库提供了更多自动优化的空间。
SQL示例不确保任何特定的顺序,因此不在意顺序是否改变。但是如果查询用命令式的代码来写的话,那么数据库就永远不可能确定代码是否依赖于排序。SQL相当有限的功能性为数据库提供了更多自动优化的空间。
最后,声明性语言往往适合于并行执行。现在,通过增加更多的内核,CPU的速度会更快,而不是以比以前更高的时钟速度运行【31】。命令代码很难在多个内核和多个机器之间并行化,因为它指定了必须以特定顺序执行的指令。声明性语言在并行执行中获得更快的机会,因为它们仅指定结果的模式,而不是用于确定结果的算法。如果合适,数据库可以自由使用查询语言的并行实现【32】。
最后,声明式语言往往适合并行执行。现在,CPU的速度通过内核的增加变得更快,而不是以比以前更高的时钟速度运行【31】。命令代码很难在多个内核和多个机器之间并行化,因为它指定了指令必须以特定顺序执行。声明式语言更具有并行执行的潜力,因为它们仅指定结果的模式,而不指定用于确定结果的算法。在适当情况下,数据库可以自由使用查询语言的并行实现【32】。
### Web上的声明式查询
声明式查询语言的优点不仅限于数据库。 为了说明这一点,我们在一个完全不同的环境中比较声明式和命令式的方法:一个Web浏览器。
声明式查询语言的优势不仅限于数据库。为了说明这一点,让我们在一个完全不同的环境中比较声明式和命令式方法:一个Web浏览器。
假设你有一个关于海洋动物的网站。 用户当前正在查看鲨鱼页面,因此您将当前所选的导航项目“鲨鱼”标记为当前选中项目。
假设你有一个关于海洋动物的网站。用户当前正在查看鲨鱼页面,因此你将当前所选的导航项目“鲨鱼”标记为当前选中项目。
```html
<ul>
......@@ -365,7 +365,7 @@ SQL示例不保证任何特定的顺序,所以它不介意顺序是否改变
</ul>
```
现在想让当前所选页面的标题有一个蓝色的背景,以便在视觉上突出显示。 使用CSS实现起来非常简单:
现在想让当前所选页面的标题具有一个蓝色的背景,以便在视觉上突出显示。使用CSS实现起来非常简单:
```css
li.selected > p {
......@@ -373,9 +373,9 @@ li.selected > p {
}
```
在这里,CSS选择器`li.selected> p`声明了我们想要应用蓝色样式的元素的模式:即直接父元素是一个CSS元素的`<li>`元素的所有`<p>`元素。 示例中的元素`<p> Sharks </ p>`匹配此模式,但`<p> Whales </ p>`不匹配,因为其`<li>`父类缺少`class =“selected”`
这里的CSS选择器`li.selected> p`声明了我们想要应用蓝色样式的元素的模式:即其直接父元素是具有`selected`CSS类的`<li>`元素的所有`<p>`元素。示例中的元素`<p> Sharks </p>`匹配此模式,但`<p> Whales </p>`不匹配,因为其`<li>`父元素缺少`class =“selected”`
如果使用XSL而不是CSS,你可以做类似的事情:
如果使用XSL而不是CSS,你可以做类似的事情:
```xml
<xsl:template match="li[@class='selected']/p">
......@@ -385,9 +385,9 @@ li.selected > p {
</xsl:template>
```
在这里,XPath表达式`li[@class='selected']/p`相当于上例中的CSS选择器`li.selected> p`。 CSS和XSL的共同之处在于,它们都是用于指定文档样式的声明性语言。
这里的XPath表达式`li[@class='selected']/p`相当于上例中的CSS选择器`li.selected> p`。CSS和XSL的共同之处在于,它们都是用于指定文档样式的声明式语言。
想象一下,如果你必须使用一命令式的方法,生活会是什么样子。在Javascript中,使用**文档对象模型(DOM)**API,结果可能如下所示:
想象一下,必须使用命令式方法,情况会是如何。在Javascript中,使用**文档对象模型(DOM)**API,其结果可能如下所示:
```js
var liElements = document.getElementsByTagName("li");
......@@ -404,27 +404,27 @@ for (var i = 0; i < liElements.length; i++) {
}
```
这个JavaScript强制性地将元素设置为蓝色背景,但是代码太糟糕了。不仅比CSS和XSL等价物更长,更难理解,而且还有一些严重的问题:
* 如果选定的类被移除(例如,因为用户点击了不同的页面),即使代码重新运行,蓝色也不会被移除 - 因此该项目将保持突出显示,直到整个页面被重新加载。使用CSS,浏览器会自动检测`li.selected> p`规则何时不再适用,并在选定的类被移除后立即移除蓝色背景。
这段JavaScript代码命令式地将元素设置为蓝色背景,但是代码看起来很糟糕。不仅比CSS和XSL等价物更长,更难理解,而且还有一些严重的问题:
* 如果选定的类被移除(例如,因为用户点击了不同的页面),即使代码重新运行,蓝色背景也不会被移除 - 因此该项目将保持突出显示,直到整个页面被重新加载。使用CSS,浏览器会自动检测`li.selected> p`规则何时不再适用,并在选定的类被移除后立即移除蓝色背景。
* 如果您想要利用新的API(例如`document.getElementsBy ClassName(“selected”`)甚至`document.evaluate()`)来提高性能,则必须重写代码。另一方面,浏览器供应商可以在不破坏兼容性的情况下提高CSS和XPath的性能。
在Web浏览器中,使用声明式CSS样式比在JavaScript中强制操作样式好得多。类似地,在数据库中,像SQL这样的声明式查询语言比命令式查询API(IMS与CODASYL都是命令式的,应用程序通常通过COBOL代码来一次一行迭代数据库中的记录)要好得多[^vi]。
在Web浏览器中,使用声明式CSS样式比使用JavaScript命令式地操作样式要好得多。类似地,在数据库中,使用像SQL这样的声明式查询语言比使用命令式查询API要好得多[^vi]。
[^vi]: vi IMS和CODASYL都使用命令式API。 应用程序通常使用COBOL代码遍历数据库中的记录,一次一条记录【2,16】。
[^vi]: vi IMS和CODASYL都使用命令式API。应用程序通常使用COBOL代码遍历数据库中的记录,一次一条记录【2,16】。
### MapReduce查询
MapReduce是一个编程模型,用于在许多机器上批量处理大量的数据,由Google推广【33】。一些NoSQL数据存储(包括MongoDB和CouchDB)支持有限形式的MapReduce,作为在多个文档中执行只读查询的机制。
总的来说,MapReduce在[第10章](ch10.md)中有更详细的描述。现在我们将简要讨论一下MongoDB对这一模型的应用。
MapReduce是一个由Google推广的编程模型,用于在多台机器上批量处理大规模的数据【33】。一些NoSQL数据存储(包括MongoDB和CouchDB)支持有限形式的MapReduce,作为在多个文档中执行只读查询的机制。
MapReduce将[第10章](ch10.md)中有更详细的描述。现在我们将简要讨论一下MongoDB使用的模型。
MapReduce既不是一个声明性的查询语言,也不是一个完全强制性的查询API,而是位于两者之间的地方:查询的逻辑用代码片断来表示,这些代码片段被处理框架重复地调用。它基于许多函数式编程语言中存在的map(也称为collect)和reduce(也称为fold或inject)函数
MapReduce既不是一个声明式的查询语言,也不是一个完全命令式的查询API,而是处于两者之间:查询的逻辑用代码片断来表示,这些代码片段会被处理框架重复性调用。它基于`map`(也称为`collect`)和`reduce`(也称为`fold``inject`)函数,两个函数存在于许多函数式编程语言中
举一个例子,假设你是一名海洋生物学家,每当你看到海洋中的动物时,你都会在数据库中添加观察记录。现在你想生成一个报告,说明你每月看到多少鲨鱼。
最好举例来解释MapReduce模型。假设你是一名海洋生物学家,每当你看到海洋中的动物时,你都会在数据库中添加一条观察记录。现在你想生成一个报告,说明你每月看到多少鲨鱼。
在PostgreSQL中,你可以像这样表这个查询:
在PostgreSQL中,你可以像这样表这个查询:
```sql
SELECT
......@@ -435,11 +435,11 @@ WHERE family = 'Sharks'
GROUP BY observation_month;
```
`date_trunc('month',timestamp)`函数确定包含时间戳记的日历月份,并返回代表该月份开始的另一个时间戳记。换句话说,它将时间戳降到最近的月份。
`date_trunc('month',timestamp)`函数确定包含`timestamp`的日历月份,并返回代表该月份开始的另一个时间戳记。换句话说,它将时间戳舍入成最近的月份。
这个查询首先过滤观察结果,只显示鲨鱼家族的物种,然后根据它们发生的日历月份对观察结果进行分组,最后将在该月的所有观察中看到的动物数加起来。
这个查询首先过滤观察记录,以只显示鲨鱼家族的物种,然后根据它们发生的日历月份对观察记录果进行分组,最后将在该月的所有观察记录中看到的动物数目加起来。
MongoDB的MapReduce功能也可以这样表示
同样的查询用MongoDB的MapReduce功能可以按如下来表述
```js
db.observations.mapReduce(function map() {
......@@ -458,14 +458,14 @@ db.observations.mapReduce(function map() {
});
```
* 可以声明性地指定只考虑鲨鱼种类的过滤器(这是对MapReduce的特定于MongoDB的扩展)。
* 可以声明式地指定只考虑鲨鱼种类的过滤器(这是一个针对MapReduce的特定于MongoDB的扩展)。
* 每个匹配查询的文档都会调用一次JavaScript函数`map`,将`this`设置为文档对象。
* `map`函数发出一个键(包括年份和月份的字符串,如`"2013-12"``"2014-1"`)和一个值(该观察中的动物数量)。
* `map`发出的键值对按键组合。 对于具有相同键(即,相同的月份和年份)的所有键值对,`reduce`函数被调用一次
* `reduce`函数将特定月份内所有观测的动物数量相加。
* 最终的输出被写入到`monthlySharkReport`集合中。
* `map`函数发出一个键(包括年份和月份的字符串,如`"2013-12"``"2014-1"`)和一个值(该观察记录中的动物数量)。
* `map`发出的键值对按键来分组。对于具有相同键(即,相同的月份和年份)的所有键值对,调用一次`reduce`函数
* `reduce`函数将特定月份内所有观测记录中的动物数量相加。
* 将最终的输出写入到`monthlySharkReport`集合中。
例如假设`observations`集合包含这两个文档:
例如假设`observations`集合包含这两个文档:
```json
{
......@@ -482,15 +482,15 @@ db.observations.mapReduce(function map() {
}
```
`map`函数会对这两个文档每个调用一次,导致 `emit("1995-12",3)``emit("1995-12",4)`,然后,`reduce`函数会执行`reduce("1995-12",[3,4])`,并返回`7`
对每个文档都会调用一次`map`函数,结果将是`emit("1995-12",3)``emit("1995-12",4)`。随后,以`reduce("1995-12",[3,4])`调用`reduce`函数,将返回`7`
map和reduce函数在功能上有限制:它们必须是纯函数,这意味着它们只使用传递给它们的数据作为输入,它们不能执行额外的数据库查询,也不能有任何副作用。这些限制允许数据库以任何顺序运行任何功能,并在失败时重新运行它们。然而,它们仍然是强大的:它们可以解析字符串,调用库函数,执行计算等等。
map和reduce函数在功能上有所限制:它们必须是**纯**函数,这意味着它们只使用传递给它们的数据作为输入,它们不能执行额外的数据库查询,也不能有任何副作用。这些限制允许数据库以任何顺序运行任何功能,并在失败时重新运行它们。然而,map和reduce函数仍然是强大的:它们可以解析字符串,调用库函数,执行计算等等。
MapReduce是一个相当低级的编程模型,用于在一组机器上进行分布式执行。像SQL这样的更高级的查询语言可以用一系列的MapReduce操作来实现(见[第10章](ch10.md)),但是也有很多不使用MapReduce的分布式SQL实现。请注意,SQL中没有任何内容限制它在单个机器上运行,而MapReduce在分布式查询执行方面没有垄断权。
MapReduce是一个相当底层的编程模型,用于计算机集群上的分布式执行。像SQL这样的更高级的查询语言可以用一系列的MapReduce操作来实现(见[第10章](ch10.md)),但是也有很多不使用MapReduce的分布式SQL实现。请注意,SQL中没有任何内容限制它在单个机器上运行,而MapReduce在分布式查询执行上没有垄断权。
能够在查询中使用JavaScript代码是高级查询的一个重要特性,但不限于MapReduce,一些SQL数据库也可以使用JavaScript函数进行扩展【34】。
能够在查询中使用JavaScript代码是高级查询的一个重要特性,但这不限于MapReduce,一些SQL数据库也可以用JavaScript函数进行扩展【34】。
MapReduce的可用性问题是,你必须编写两个协调的JavaScript函数,这通常比编写单个查询更困难。此外,声明性查询语言为查询优化器提供了更多机会来提高查询的性能。由于这些原因,MongoDB 2.2添加了对称为聚合管道的声明性查询语言的支持【9】。在这种语言中,相同的鲨鱼计数查询如下所示:
MapReduce的一个可用性问题是,必须编写两个密切合作的JavaScript函数,这通常比编写单个查询更困难。此外,声明式查询语言为查询优化器提供了更多机会来提高查询的性能。基于这些原因,MongoDB 2.2添加了一种叫做**聚合管道**的声明式查询语言的支持【9】。用这种语言表述鲨鱼计数查询如下所示:
```js
db.observations.aggregate([
......@@ -504,7 +504,7 @@ db.observations.aggregate([
]);
```
聚合流水线语言与SQL的子集表现类似,但是它使用基于JSON的语法而不是SQL的英语句子式语法; 这种差异也许是品味的问题。 这个故事的寓意是NoSQL系统可能会发现自己意外地重新发明了SQL,尽管是伪装的
聚合管道语言与SQL的子集具有类似表现力,但是它使用基于JSON的语法而不是SQL的英语句子式语法; 这种差异也许是口味的问题。这个故事的寓意是NoSQL系统可能会发现自己意外地重新发明了SQL,尽管带着伪装
......@@ -538,7 +538,7 @@ db.observations.aggregate([
**图2-5 图数据结构示例(框代表顶点,箭头代表边)**
有几种不同但相关的方法来构建和查询图表中的数据。 在本节中,我们将讨论属性图模型(由Neo4j,Titan和InfiniteGraph实现)和三元组存储(triple-store)模型(由Datomic,AllegroGraph等实现)。 我们将看图的三种声明性查询语言:Cypher,SPARQL和Datalog。 除此之外,还有像Gremlin 【36】这样的图形查询语言和像Pregel这样的图形处理框架(见[第10章](ch10.md))。
有几种不同但相关的方法来构建和查询图表中的数据。在本节中,我们将讨论属性图模型(由Neo4j,Titan和InfiniteGraph实现)和三元组存储(triple-store)模型(由Datomic,AllegroGraph等实现)。我们将看图的三种声明式查询语言:Cypher,SPARQL和Datalog。除此之外,还有像Gremlin 【36】这样的图形查询语言和像Pregel这样的图形处理框架(见[第10章](ch10.md))。
### 属性图
......@@ -582,7 +582,7 @@ CREATE INDEX edges_heads ON edges (head_vertex);
这个模型的一些重要方面是:
1. 任何顶点都可以有一个边连接到任何其他顶点。没有哪种事物可不可以关联的模式限制。
2. 给定任何顶点,您可以高效地找到它的入边和出边,从而遍历图,即沿着一系列顶点的路径前后移动。 (这就是为什么[例2-2]()在`tail_vertex`和`head_vertex`列上都有索引的原因。)
2. 给定任何顶点,您可以高效地找到它的入边和出边,从而遍历图,即沿着一系列顶点的路径前后移动。(这就是为什么[例2-2]()在`tail_vertex`和`head_vertex`列上都有索引的原因。)
3. 通过对不同类型的关系使用不同的标签,可以在一个图中存储几种不同的信息,同时仍然保持一个干净的数据模型。
这些特性为数据建模提供了很大的灵活性,如[图2-5](img/fig2-5.png)所示。图中显示了一些传统关系模式难以表达的东西,例如不同国家的不同地区结构(法国有省和州,美国有不同的州和州),国中国的怪事(先忽略主权国家和国家错综复杂的烂摊子),不同的数据粒度(露西现在的住所被指定为一个城市,而她的出生地点只是在一个州的级别)。
......@@ -591,7 +591,7 @@ CREATE INDEX edges_heads ON edges (head_vertex);
### Cypher查询语言
Cypher是属性图的声明式查询语言,为Neo4j图形数据库发明【37】。 (它是以电影“黑客帝国”中的角色命名的,与密码术中的密码无关【38】。)
Cypher是属性图的声明式查询语言,为Neo4j图形数据库发明【37】。(它是以电影“黑客帝国”中的角色命名的,与密码术中的密码无关【38】。)
[例2-3]()显示了将[图2-5](img/fig2-5.png)的左边部分插入图形数据库的Cypher查询。图的其余部分可以类似地添加,为了便于阅读而省略。每个顶点都有一个像USA或Idaho这样的符号名称,查询的其他部分可以使用这些名称在顶点之间创建边,使用箭头符号:`(Idaho) - [:WITHIN] ->(USA)`创建一个带有标记`WITHIN`的边,爱达荷州为尾节点,美国为头节点。
......@@ -632,7 +632,7 @@ RETURN person.name
等价地,也可以从两个位置顶点开始并向后查找。如果名称属性上有一个索引,则可以高效地找到代表美国和欧洲的两个顶点。然后,您可以继续查找所有在`WITHIN`边中的位置(美国和欧洲的所有位置(州,地区,城市等))。最后,您可以查找可以通过在某个位置顶点处传入的`BORN_IN`或`LIVES_IN`边找到的人员。
对于声明查询语言来说,典型的情况是,在编写查询语句时,您不需要指定执行细节:查询优化程序会自动选择预测效率最高的策略,因此您可以继续编写其余的应用程序。
对于声明查询语言来说,典型的情况是,在编写查询语句时,您不需要指定执行细节:查询优化程序会自动选择预测效率最高的策略,因此您可以继续编写其余的应用程序。
### SQL中的图查询
......@@ -785,7 +785,7 @@ RDF有一些奇怪之处,因为它是为了在互联网上交换数据而设
### SPARQL查询语言
SPARQL是RDF数据模型三元组存储的查询语言【43】。 (它是SPARQL协议和RDF查询语言的缩写,发音为“sparkle”。)它早于Cypher,并且由于Cypher的模式匹配是从SPARQL中借用的,所以它们看起来非常相似【37】。
SPARQL是RDF数据模型三元组存储的查询语言【43】。(它是SPARQL协议和RDF查询语言的缩写,发音为“sparkle”。)它早于Cypher,并且由于Cypher的模式匹配是从SPARQL中借用的,所以它们看起来非常相似【37】。
与从前从美国转移到欧洲的人相同的查询——在SPARQL中比在Cypher中更加简洁(参见[例2-9]())。
**例2-9 与示例2-4相同的查询,用SPARQL表示**
......@@ -817,7 +817,7 @@ SPARQL是一种很好的查询语言——即使语义网从来没有出现,
> #### 图形数据库与网络模型相比较
>
> 在“[文档数据库是否在重演历史?](#文档数据库是否在重演历史?)”中,我们讨论了CODASYL和关系模型如何竞争解决IMS中的多对多关系问题。乍一看,CODASYL的网络模型看起来与图模型相似。 CODASYL是否是图形数据库的第二个变种?
> 在“[文档数据库是否在重蹈覆辙?](#文档数据库是否在重蹈覆辙?)”中,我们讨论了CODASYL和关系模型如何竞争解决IMS中的多对多关系问题。乍一看,CODASYL的网络模型看起来与图模型相似。CODASYL是否是图形数据库的第二个变种?
>
> 不,他们在几个重要方面有所不同:
>
......@@ -834,7 +834,7 @@ Datalog是比SPARQL或Cypher更古老的语言,在20世纪80年代被学者广
在实践中,Datalog在一些数据系统中被使用:例如,它是Datomic 【40】的查询语言,而Cascalog 【47】是用于查询Hadoop中的大数据集的Datalog实现[^viii]。
[^viii]: Datomic和Cascalog使用Datalog的Clojure S表达式语法。 在下面的例子中使用了一个更容易阅读的Prolog语法,没有任何功能差异。
[^viii]: Datomic和Cascalog使用Datalog的Clojure S表达式语法。在下面的例子中使用了一个更容易阅读的Prolog语法,没有任何功能差异。
Datalog的数据模型类似于三元组模式,但有一点泛化。我们把它写成谓词(主语,宾语),而不是写三元语(主语,宾语,宾语)。[例2-10]()显示了如何在Datalog中写入我们的例子中的数据。
......@@ -856,7 +856,7 @@ name(lucy, 'Lucy').
born_in(lucy, idaho).
```
现在我们已经定义了数据,我们可以像之前一样编写相同的查询,如**例2-11**所示。它看起来有点不同于Cypher或SPARQL的等价物,但是不要让你失望。 Datalog是Prolog的一个子集,如果你已经学过计算机科学,你可能已经见过。
现在我们已经定义了数据,我们可以像之前一样编写相同的查询,如**例2-11**所示。它看起来有点不同于Cypher或SPARQL的等价物,但是不要让你失望。Datalog是Prolog的一个子集,如果你已经学过计算机科学,你可能已经见过。
**例2-11 与示例2-4相同的查询,在Datalog中表示**
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册