七周七数据库(txt+pdf+epub+mobi电子书下载)


发布时间:2020-10-01 16:16:15

点击下载

作者:[美]Eric Redmond Jim R.Wilson 著

出版社:人民邮电出版社

格式: AZW3, DOCX, EPUB, MOBI, PDF, TXT

七周七数据库

七周七数据库试读:

前言

如果说数据是新的石油,那么数据库就是油田、炼油厂、钻井和油泵。数据存放在数据库中,如果你有兴趣利用它,那么掌握相应的现代化的工具就是好的开始。

数据库是工具,它们是到达终点的手段。每种数据库都有自己保存数据和看待世界的方式。你对它们的理解越多,就越能随心所欲,在日益增长的大数据上,就能更好地利用它们潜在的能力。

为什么是7种数据库

早在2010年3月,我们就想写本关于NoSQL的书。NoSQL这个术语已经聚集了人气,虽然许多人都在谈论,但关于它似乎也存在相当多的困惑。NoSQL到底意味着什么?包含哪些类型的系统?对于开发优秀的软件,它将产生怎样的影响?这些就是我们想要回答的问题,既是为我们自己,也是为别人。

在读了Bruce Tate的典范性的著作《七周七语言》后,我们知道他做得很对。他循序渐进地介绍语言的方式引起了我们的共鸣。我们觉得用同样的方式讲授数据库,将会提供一个很好的环境,回答这些棘手的NoSQL问题。

本书内容

本书针对的是有经验的开发者,他们希望全面地理解现代数据库的整体情况。本书不要求读者以前在数据库方面有经验,但有数据库经验会有助于学习本书。

在简单的介绍之后,本书分章介绍了7个数据库。这些数据库分属5种不同的数据库类型或风格,这在第1章中有介绍。它们依次是PostgreSQL、Riak、Apache HBase、MongoDB、Apache CouchDB、Neo4j和Redis。

每章都设计成一个长周末的学习量,分为三天。每天结束时都有一些练习,扩展刚刚介绍的主题和概念。每章最后都有一段总结性的讨论,总结了这种数据库的优点和缺点。你既可以学得快一点,也可以学得慢一点,但重要的是先掌握每天的概念,再继续后面的学习。我们试着设计了一些例子来探索每种数据库的独特之处。要真正理解这些数据库提供的能力,必须花些时间来使用它们,这意味着要动手实践。

虽然你可能想跳过某些章,但我们设想你是按章节顺序阅读这本书的。某些概念,如映射-归约(mapreduce),在前面的章节中深入地进行了介绍,所以在后面的章节中就略过了。本书旨在实现对现代数据库的一致理解,所以建议你完整地阅读本书。

不包含的内容

在阅读本书之前,你应该知道它不包含哪些内容。

本书不是安装指南

安装本书中提到的数据库有时候容易,有时候有些挑战,有时候非常棘手。对于某些数据库,可以使用提供的安装包;而对于另一些数据库,需要编译源代码。我们会不时提供一些有用的提示,但主要还是靠你自己。省略安装步骤让我们能安排更多有用的例子和概念讨论,这才是你真正想要的,对吗?

本书也不是管理手册

出于对安装同样的考虑,本书也不会介绍管理手册里的所有内容。每种数据库都有大量的选项、设置、开关和配置细节,绝大部分都能在 Web 上找到详尽的文档。我们更关心介绍有用的概念,完全深入进去,而不是仅关注日常操作。虽然数据库的一些特点会根据操作设置而改变(我们可能会讨论这些特点),但由于篇幅有限,我们不可能介绍所有可能配置的全部具体细节。

对Windows用户的说明

本书本身就讨论选择,主要是针对*nix平台上的开源软件。微软的环境作为集成环境有点困难,它限制了许多选择,只留下一个较小的、预定义的子集。因此,我们介绍的数据库是开源的,由*nix系统的用户开发(也主要为他们服务)。这不是我们的偏见,只是当前真实情况的反映。所以,我们假定教程式的例子运行在*nix的shell下。如果你运行Windows并希望给它一个尝试的机会,我们推荐安装1Cygwin,这样更容易成功。你也可以考虑运行一个Linux虚拟机。

注释1 http://www.cygwin.com/

代码示例和惯例

本书包含各种语言的代码。部分原因是因为我们介绍的这些数据库本身使用语言不同。我们曾试着将语言局限在Ruby/JRuby和JavaScript。我们更喜欢命令行工具,而不是脚本语言,但我们会引入其他一些语言来完成工作,如PL/pgSQL(Postgres)和Gremlin/Groovy (Neo4j)。我们也会尝试使用Node.js,编写一些服务器端的JavaScript应用。

除非特别注明,代码清单都是完整的,通常可以直接执行。根据所涉及的语言规则,突出了示例和代码片段中的语法。shell命令以$开始。

在线资源1

本书的Pragmatic Bookshelf页面是很好的资源。从上面可以下载本书中的所有源代码。你也会找到一些反馈工具,如社区论坛和勘误提交,你可以通过它们对本书未来的版本提出改进建议。

注释1 http://pragprog.com/book/rwdata/seven-databases-in-seven-weeks

感谢你陪伴我们完成现代数据库的观光之旅。Eric Redmond和 Jim R. Wilson致谢

本书内容涉及范围都比较宽泛,只靠两位作者是无法完成的。它需要许多非常聪明的人的努力,他们有超人般的眼睛,能尽可能多地发现错误,针对这些技术的细节提供有价值的见解。

我们要感谢所有贡献出时间和专业知识的人(不分先后):Ian Dees、Mark Phillips、Jan Lenhardt、Robert Stam、Oleg Bartunov、Dave Purrington、Daniel Bretoi、Matt Adams、Sean Copenhaver、Loren Sands-Ramshaw、Emil Eifrem和Andreas Kollegger。最后,还要感谢Bruce Tate的经验和指导。

我们还要真诚地感谢Pragmatic Bookshelf的整个团队。感谢他们提出这个大胆的项目,并看着我们完成。我们特别要感谢编辑 Jackie Carter。是你的耐心反馈,才有了今天这本书。感谢整个团队辛勤的工作,找出了我们所有的错误。

最后但同样重要的是,感谢Frederic Dumont、Matthew Flower、Rebecca Skinner和所有严格的读者,如果没有你们的学习热情,我们就不会有机会提供服务了。

对于这里遗漏的人,我们希望你接受道歉。我们肯定不是有意的。

Eric想说:亲爱的Neolle,你不是特别的,你是唯一的,这要好得多。谢谢你又忍受我完成了一本书。感谢数据库的创建者和贡献者,为我们提供了写书的内容和谋生的工具。

Jim想说:首先,我要感谢我的家庭。Ruthy,你无限的耐心和鼓励温暖了我的心。Emma和Jimmy,你们是两个聪明鬼,爸爸永远爱你们。还要感谢所有无名英雄,他们盯着IRC、消息板、邮件列表和bug管理系统,时刻准备帮助需要的人。你们对开源的贡献让这些项目一直激动人心。第1章 概述

当前是数据库世界的一个重要时刻。多年来,无论针对的问题是大还是小,关系模型一直是事实上的选择。我们不指望关系数据库会很快消失,但是人们正在从RDBMS的迷雾中走出来,寻找替代的方案,如无模式或可替代的数据结构,可简单复制,具有高可用性,可横向扩展,以及新的查询方法。这些选择统称为NoSQL,而NoSQL占据了本书的大部分内容。

本书将探讨七种数据库,涉及各种数据库风格。在阅读本书的过程中,你将了解每个数据库具有的各种功能和折中,如持久性与速度、绝对一致性与最终一致性等,并学会如何针对你的使用场景,做出最好的决策。1.1 从一个问题开始

本书的核心问题是:哪种数据库或数据库组合最好地解决了你的问题?读完本书,如果你知道如何根据特定需求和手头的资源做出这种选择,我们会很高兴。

但要回答这个问题,你需要了解你的选择。为此,我们将带你深入这7个数据库,揭示精华,并指出瑕疵。你将亲手尝试CRUD操作,发挥你使用的模式的力量,并找到下面这些问题的答案:

●这是什么类型的数据库?数据库分为各种类型,例如,关系型、键-值型、多列型、面向文档型和图型。流行的数据库(包括本书中介绍的)一般可以划分为这几大类型。你将了解每种类型的数据库,以及它们最适合的各种问题。我们对本书涉及的数据库精心挑选,以覆盖这些类型,包括一个关系数据库(Postgres),两个键-值存储数据库(Riak 和Redis),一个面向列的数据库(HBase),两个面向文档的数据库(MongoDB和CouchDB),以及一个图数据库(Neo4j)。

●驱动力是什么?数据库不是凭空产生的。它们是为了解决实际使用中提出的问题。在 RDBMS(关系数据库管理系统)出现的环境中,数据库查询的灵活性比灵活的模式更重要。另一方面,建立面向列的数据库是为了适于存储跨多机的大量数据,而数据之间的关系退居次要地位。我们将介绍使用每个数据库的场景和相关的例子。

●如何与数据库交互?数据库往往支持多种连接选项。只要某个数据库有交互式的命令行界面,我们会首先使用它,之后再介绍其他方法。如果需要编程,我们主要使用Ruby和JavaScript,尽管不时会用到其他几种语言,如PL/pgSQL(Postgres)和Gremlin(Neo4j)。更深入一层,我们将讨论诸如REST(CouchDB和Riak)和Thrift(HBase)协议。第9章将介绍一个更复杂的数据库环境,它由Node.js JavaScript实现联接在一起。

●每种数据库的独特性体现在哪里?任何数据存储库都支持写入和读回数据。它们在其他的方面彼此大不相同。有些数据库允许对任意字段的查询。有些数据库提供快速索引查找。有些数据库支持自由定义的查询(ad hoc query);而其他的数据库的查询必须先规划。模式是数据库所强制的一个刚性框架,或仅仅是一些随意商定的准则?理解每种数据库的功能和限制,将有助于挑选适合你的工作的数据库。

●每种数据库的性能如何?这个数据库如何工作?其开销如何?它支持分片吗?复制呢?它是否使用一致散列均匀地分布数据?它将相似的数据放在一起吗?这个数据库为读、写或其他操作做了优化吗?如果你能对优化进行控制,程度如何?

●每种数据库的可伸缩性如何?可伸缩性与性能相关。没有上下文,谈论可伸缩性一般不会有结论。本书会提供背景知识,你在建立这个上下文时就能提出正确的问题。虽然如何扩展每个数据库的讨论会有意淡化,但在这些章节里,你会发现每种数据存储库是更容易实现横向扩展(MongoDB、Hbase 和 Riak),还是传统的纵向扩展(Postgres、Neo4j和Redis),或者介于两者之间。

我们的目标不是将某种数据库的新手培养成大师。如果这样做的话,其中任何一个数据库都将充满整本书的篇幅。但最终你应该能够牢牢把握每个数据库的优势,以及它们的不同。1.2 风格

如同音乐一样,各种数据库有着其本身独特的风格。所有的歌曲都使用同样的音符,但是有些音符对某些网络的歌曲更合适。少有人驾驶着敞篷车,沿着405号公路超速行驶,同时播放巴赫的《B小调弥撒曲》。同样,在某些情况下,一些数据库比其他数据库更好些。你要问的不是“我能够用这个数据库来存储和完善数据吗?”而是“我应该用这个数据库吗?”

本节将要探讨 5种主要的数据库类型,也会简单介绍每种类型中我们要关注的数据库。

重要的是要记住,你将面临的大多数数据问题,可以用本书中的大部分或全部数据库解决,更别说还有其他数据库。问题不是某种数据库风格是否能别生搬硬套地用来为你的数据建模,而是它是否最适合你的问题领域、使用模式,以及可用的资源。你将学会预测一种数据库是否在本质上对你有用,而这是一门艺术。1.2.1 关系数据库

关系模型通常是大多数有数据库经验的人首先想到的。关系数据库管理系统(Relational DataBase Management System,RDBMS)是以集合理论为基础的系统,实现为具有行和列的二维表。与RDBMS交互的标准方法,是用结构化查询语言(Structured Query Language,SQL)编写查询。数据值具有类型,可以是数字、字符串、日期、未解释的二进制大对象,或其他类型。系统强制使用类型。重要的是,表可以联接并转化为新的、更复杂的表,因为它们的数学基础是关系(集合)理论。

有许多开源关系数据库可供选择,包括MySQL、H2、HSQLDB、SQLite等。第2章将介绍PostgreSQL。PostgreSQL

PostgreSQL久经沙场,它是迄今为止我们介绍的最古老和最健壮的数据库。PostgreSQL符合SQL标准,之前曾使用过关系数据库的人都会觉得熟悉它,这为我们将使用的其他数据库提供了一个坚实的比较基础。我们还将探讨一些不大为人熟悉的 SQL 功能以及Postgres特有的优势。从SQL新手到专家,每个人都能从中有所收获。1.2.2 键-值数据库

键-值(Key-Value,KV)存储库是我们介绍的最简单的模型。顾名思义,KV存储库将键与值配对,类似于所有流行编程语言中的映射(或哈希表)。某些KV实现允许复杂的值类型,如哈希或列表,但这不是必需的。一些 KV 实现提供了一种迭代遍历键的方式,但这也是额外的好处。如果你将文件的路径视为键而将文件内容作为值,文件系统也可以看成是键-值存储库。因为KV存储库对资源的要求非常少,所以这种数据库类型在一些场景中有令人难以置信的高性能,但是当你有复杂的查询和聚合需求时,它一般不会有帮助。

与关系数据库一样,有许多开源的 KV 存储库可以选择。一些较受欢迎的产品包括memcached(及相关的memcachedb和membase)、Voldemort,以及我们在本书中介绍的两个产品:Redis和Riak。1.Riak

第3章介绍的Riak不仅仅是一个键-值存储库,它从一开始就支持HTTP和REST等Web方式。它严格实现了亚马逊Dynamo的原理,具有一些高级功能,如解决冲突的向量时钟。Riak中的值可以是任何内容,从纯文本到XML到图像数据,而键之间的关系由称为链接(link)的命名结构处理。Riak是本书中知名度较小的数据库,但它越来越受欢迎,在我们将要讨论的数据库中,它是第一个通过mapreduce支持高级查询的数据库。2.Redis

Redis提供复杂的数据类型,如有序集合和哈希,以及基本消息模式,如发布-订阅和阻塞队列。它是查询机制最健壮的KV存储库之一。在写入磁盘之前先写入内存缓存, Redis 因此获得了惊人的性能,代价是在出现硬件故障的情况下,增加了数据丢失的风险。这一特性使得它适合用于缓存非关键数据,或作为消息代理。我们将它留到最后介绍(参见第8章),以便可以用Redis与其他数据库配合,构建多数据库应用。1.2.3 列型数据库

列型(或面向列的数据库)的命名源自于其设计的一个重要方面,即来自一个给定的列(在二维表的意义上)的数据存放在一起。相比之下,面向行的数据库(如RDBMS),将一行的信息保存在一起。这种差异看起来似乎无关紧要,但实际上这种设计决策的影响很深。在面向列的数据库中,添加列是相当简易的,而且是逐行完成的。每一行可以有一组不同的列,或完全没有,允许表保持稀疏(sparse),而不会产生空值的存储成本。在结构方面,列型数据库大约介于关系数据库和键-值存储库之间。

在列型数据库市场中,竞争相比关系数据库或键-值存储较少。三种最流行的产品是HBase(在第4章中介绍)、Cassandra和Hypertable。HBase

在我们介绍的所有非关系数据库中,这个面向列的数据库与关系模型最为相似。以Google的BigTable论文作为蓝图,HBase建立在Hadoop(一个mapreduce引擎)之上,其设计目标是在常用硬件的集群上横向伸缩。HBase 保证了强一致性并提供带行和列的表的功能,这使得SQL粉丝有宾至如归的感觉。它直接支持版本控制和压缩,这令它在“大数据”的世界中与众不同。1.2.4 文档型数据库

当然,面向文档的数据库存储的就是文档。简而言之,文档就像是哈希,具有一个独一无二的标识符(ID)字段和值,值可能是任何类型,包括更多的哈希。文档可以包含嵌套的结构,因此,它们表现出了高度的灵活性,允许有可变域。该系统对输入的数据很少有限制,只要它满足基本要求,可以表示为一个文档。在建索引、自由定义的查询、复制、一致性及其他设计决策等方面,不同的文档数据库采取了不同的方法。需要了解这些差异,及其对特定使用场景的影响,才能在它们之间做出明智地选择。

文档数据库市场中的两大开源产品是MongoDB(在第5章中介绍)和CouchDB(在第6章中介绍)。1.MongoDB

MongoDB的设计目标是支持巨大的数据(名字mongo是从单词humongous中提取的)。Mongo服务器的配置试图保持一致性:如果你写入了什么内容,随后的读取将得到相同的值(直到下次更新)。这一特性吸引了那些具有RDBMS背景的人。它也提供了一些原子读写操作(如递增一个值),以及对嵌套文档结构的深层查询。MongoDB 利用 JavaScript 作为查询语言,支持简单的查询和复杂的mapreduce工作。2.CouchDB

CouchDB 的目标是各种部署场景,从数据中心到桌面,一直到智能手机。CouchDB是用Erlang编写的,具有独特的坚固性,这一点在大部分其他数据库中是缺乏的。由于它的数据文件几乎不可摧毁,即使是面对间歇性的连接丢失或硬件故障,CouchDB也仍能保持高可用性。像Mongo一样,CouchDB的原生查询语言是JavaScript。视图包括mapreduce函数,它们以文档的形式存储并在节点之间复制,像任何其他的数据一样。1.2.5 图数据库

这是一种不太常用的数据库类型,图数据库善于处理高度互联的数据。图数据库包含节点及节点之间的关系。节点和关系可以有一些属性(一些键-值对),用来存储数据。图数据库的真正实力是按照关系遍历节点。

第7章讨论现在最流行的图数据库Neo4j。Neo4j

在遍历自我引用或以其他方式杂乱地连接在一起的数据时,其他数据库常常操作失败。这正是Neo4j使人眼前一亮的地方。使用图数据库的好处在于能够快速在节点和关系之间移动,找到相关数据。图数据库经常用在社交网络应用中,它们因灵活性而受到关注,Neo4j是其中的佼佼者。1.2.6 混合使用多种数据库

在实际环境中,各种数据库经常一起使用。使用单一的关系数据库仍然很常见,但随着时间的推移,流行的做法是同时使用几种数据库,利用它们各自的长处,创建一个生态系统,比其各部分的功能总和更强大、更全面、更健壮。这种做法叫做多持久并存(Polyglot Persistence),第9章将进一步讨论这一主题。1.3 前进和提升

我们正处在数据存储选择的寒武纪大爆炸之中,很难准确预测未来会如何发展。但我们可以相当肯定,任何一种特定策略(关系型或其他类型)都不大可能大获全胜。相反,我们将看到越来越多的专用数据库,每种适合一组特定(但肯定有重叠)的理想问题。今天有一些工作需要关系数据库的专业知识(DBA),同样,我们会看到对应的非关系领域的增长。

与编程语言和程序库一样,数据库是另一套工具,每个开发人员都应该知道数据库。每一个好木匠必须了解他的工具箱里有什么。就像所有好的建筑师一样,如果你不熟悉可供你使用的多种选择,就不会成为一名大师。

本书就像是一个车间里的速成课。在本书中,你会挥动锤子,转动电钻,使用射钉枪,并最终能够建立比鸟笼子更大、更复杂的东西。闲话少说,我们来使用我们的第一个数据库:PostgreSQL。第2章 PostgreSQL

PostgreSQL是数据库世界里的“锤子”。它既广为人知,又容易获得,还很坚固,如果你抡得够猛,它所能解决的问题数量惊人。如果不了解这个最常用的工具,你就不可能成为建筑专家。

PostgreSQL是一个关系数据库管理系统,即它是以集合理论为基础的系统,在实现上,它定义为一些二维表,表中包含数据行和具有严格数据类型的列。虽然人们对新兴数据库越来越有兴趣,但关系数据库仍然是最流行的数据库,而且这种趋势可能会保持很长一段时间。

关系数据库流行的原因,不仅在于其庞大的特性集(触发器、存储过程、高级索引)、数据的安全性(符合ACID),或符合大多数人的思维方式(许多程序员以关系的方式说话和思考),还在于它们的查询灵活性。与其他某些数据存储库相比,你不必事先知道要如何使用这些数据。如果关系数据模式是规范的,那么查询就可以很灵活。PostgreSQL是最好的开源关系数据库例子。2.1 这就是Post-greS-Q-L

在本书提到的数据库中,PostgreSQL是历史最悠久、实战经验最丰富的。它的扩展包括自然语言解析、多维索引、地理查询、自定义数据类型等。它具有高级的事务处理能力,支持十几种不同语言的存储过程,能在各种平台上运行。PostgreSQL 内置支持 Unicode、序列、表继承、子查询,而且是市场上遵循ANSI SQL标准最好的关系数据库之一。它快速可靠,可以处理 TB 量级的数据,并且已经在一些高知名度的生产系统上得到验证,如Skype、法国储蓄银行(CNAF)和美国联邦航空局(FAA)。

那么,名字是怎么来的呢

自1995年以来,PostgreSQL就以目前的项目形态存在,但它的起源相当久远。20世纪 70 年代初,最初的项目产生于加州大学伯克利分校,叫做交互式图形和检索系统(Interactive Graphics and Retrieval System),或简称为“Ingres”。在 20世纪 80年代,推出了一个改进版本,post-Ingres,简称为Postgres。虽然该项目于1993年在伯克利大学终结,但开源社区取得了该项目的源码,并将其发布为PostgreSQL95。后来于1996年更名为PostgreSQL,表示对新的SQL标准的支持,此后一直沿用这个名字。1

可以用多种方式安装PostgreSQL,这取决于你的操作系统。除了安装核心组件,还需要在PostgreSQL上安装扩展包,用到以下扩展包:tablefunc、dict_xsyn、fuzzystrmatch、pg_trgm和cube。可以2参考网站上的安装指南。

注释1 http://www.postgresQL.org/download/

注释2 http://www.postgresQL.org/docs/9.0/static/contrib.html

安装PostgreSQL之后,使用下面的命令创建一个名为book的数据库:

$ createdb book

接下来,我们将在本章中使用book数据库。运行下面的命令,以确保你需要的扩展包已经正确安装。

$ psql book -c "SELECT '1'::cube;"

如果你看到一条错误消息,请查看官网的文档,以获得更多的信息。2.2 第1天:关系、CRUD和联接

我们虽然不会把你当作是一个关系数据库专家,但是确实会假设你曾用过一两个数据库。这些数据库很可能是关系型的。我们将开始创建自己的数据表,并填充数据。然后尝试查询一些行。最后探讨关系数据库中非常重要的表联接。

就像大多数数据库一样,Postgres 提供一个后台服务进程(Backend),它完成所有数据处理工作,还提供一个命令行客户端程序,通过它连接到运行中的服务进程。服务进程默认监听5432端口,可以用psql这个命令行工具连接。

数学关系

关系数据库的名称源于它们包含关系(即表),它们是元组(即行)的集合,元组又将属性映射到原子值(例如,{name: 'Genghis Khan',p.died_at_age: 65})。可用的属性通过头部的属性元组来定义,这些属性映射到某个域或限制的类型(即列;例如,{name: string,age: int})。这是关系结构的要点。

尽管听起来数学味很浓,但是实现比名字所暗示的更具有现实意义。那么,为什么要提到这些?我们正试图说明,关系数据库的关系是因为它的数学基础,不是因为表通过外键彼此“关联”。这样的限制是否存在并不是关键。

虽然许多数学关系你看不到,但模型的力量肯定是蕴藏在数学之中。这种魔法允许用户提出功能强大的查询,然后让系统基于预定义的模式进行优化。RDBMS 基于集合理论的一个分支,名为关系代数,它包括选择(WHERE...)、投影(SELECT...)、笛卡尔积(JOIN...)等操作,如图2-1所示。

如果将关系想象为一张物理表(数组的数组,在数据库入门课中无数次重复过),可能在实践中造成痛苦,如编写遍历所有行的代码。关系查询的描述性远胜于此,它源于一个数学分支,名为元组关系演算,可以转换为关系代数。PostgreSQL 和其他的 RDBMS 通过执行这个转换优化了查询,简化了代数运算。你可以看到,图2-2中的SQL,与图2-1中的SQL是一样的。图2-1 关系代数和SQL图2-2 元组关系演算和SQL

$ psql book

以管理员用户运行的话,PostgreSQL的提示符是数据库的名字后面跟一个‘#’,如果是普通用户,后面跟的是‘$’。这个命令行程序的内置文档是所有命令行程序中最好的。输入‘\h’,可以列出有关SQL命令的信息,\? 列出以反斜杠开始的psql特有命令的帮助信息。可以使用下列方式找到每个SQL命令的使用详细信息:

book=# \h CREATE INDEX

Command: CREATE INDEX

Description: define a new index

Syntax:

CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ name ] ON table [ USING method ]

  ( { column | ( expression ) } [ opclass ] [ ASC | DESC ] [ NULLS { FIRST | ...

  [ WITH ( storage_parameter = value [, ... ] ) ]

  [ TABLESPACE tablespace ]

  [ WHERE predicate ]

在我们深入探讨PostgreSQL之前,最好先熟悉这个有用的工具。还可以熟悉一些常见命令值,如SELECT或CREATE TABLE。2.2.1 从SQL开始

PostgreSQL遵循SQL惯例,称关系为表(TABLE),属性为列(COLUMN),元组为行(ROW)。虽然你可能会遇到一些数学术语,如关系、属性和元组,为了保持一致性,我们将使用这些术语,如关系、属性和元组。有关这些概念的更多信息,请参阅 2.2 节的“数学关系”。

关于CRUD

CRUD是一个助记符,帮助记忆数据管理基本操作:创建、读取、更新和删除(Create, Read,Update,Delete)。这些操作一般对应插入新记录(创建),修改现有记录(更新),删除不再需要的记录(删除)。你使用数据库时所有的其他操作(你可以梦想到的任何疯狂查询)都是读操作。如果能进行CRUD操作,你就能做任何事。2.2.2 使用表

PostgreSQL是关系型的数据管理系统,所以需要事先进行设计。要先设计好数据库的表,然后插入符合数据库定义的数据。

创建表包括为它命名,定义所有列及其类型,以及定义(可选的)约束信息。每张表都应该指定唯一的标识符列,以标识特定的行。该标识符称为主键(PRIMARY KEY)。创建countries表的SQL语句如下所示:

CREATE TABLE countries (

  country_code char(2) PRIMARY KEY,

  country_name text UNIQUE

);

这张新表将存储一些行,其中每一行由两个字节的国家代码作为标识,国家名也是唯一的。这两列都有约束,主键约束 country_code 列不允许有重复的国家代码,所以只有一个 us 和一个 gb 可以存在表中。尽管 country_name 不是主键,但是明确地给予country_name类似的唯一性约束。可以用如下语句插入几行来填充这张counties表。

INSERT INTO countries (country_code, country_name)

VALUES ('us','United States'), ('mx','Mexico'), ('au','Australia'),

    ('gb','United Kingdom'), ('de','Germany'), ('ll','Loompaland');

让我们来测试一下唯一性约束。如果尝试添加包含重复的 country_name 的行,就会因为唯一性约束而不允许插入。约束是PostgreSQL这样的关系数据库用来确保数据完整的方法。

INSERT INTO countries

VALUES ('uk','United Kingdom');

ERROR: duplicate key value violates unique constraint "countries_country_name_key"

DETAIL: Key (country_name)=(United Kingdom) already exists.

通过SELECT...FROM table语句进行查询,可以验证相关的行是否已经插入。

SELECT *

FROM countries;

country_code | country_name

-------------+---------------

us      | United States

mx      | Mexico

au      | Australia

gb      | United Kingdom

de      | Germany

ll      | Loompaland

(6 rows)

根据正规的地图,可以知道 Loompaland 不是真实存在的地方,所以让我们从表中删除它。用WHERE子句指定要删除的行,country_code等于ll的行将被删除。

DELETE FROM countries

WHERE country_code = 'll';

只有实际存在的国家留在了countries表中,让我们再添加一个cities表。为保证所有插入的country_code都在countries表中,将添加关键字REFERENCES。因为country_code列引用了另一张表的键,所以它称为外键约束。

CREATE TABLE cities (

 name text NOT NULL,

 postal_code varchar(9) CHECK (postal_code <> ''),

 country_code char(2) REFERENCES countries,

 PRIMARY KEY (country_code, postal_code)

);

这一次,cities表中的name列的约束是不允许其值为NULL的。postal_code列的约束,是其值不能是空字符串(<>表示不等于)。

此外,因为主键唯一地标识一行,所以定义了一个复合键:country_code +postal_code。它们共同作为一行的唯一的标识符。

Postgres 也有丰富的数据类型,刚才看到了三种不同的字符串表示:text(任意长度的字符串),varchar(9)(长度可达9个字节的字符串)和char(2)(正好两个字节的字符串)。

定义了数据表后,让我们插入Toronto,CA。

INSERT INTO cities

VALUES ('Toronto','M4C1B5','ca');

ERROR: insert or update on table "cities" violates foreign key constraint

  "cities_country_code_fkey"

DETAIL: Key (country_code)=(ca) is not present in table "countries".

这个操作失败并不是什么坏事!因为 country_code 需要参考 countries,所以 country_ code必须存在于 countries表中,这称为保持参照完整性,参见图2-3,它确保数据始终是正确的。值得指出的是,NULL 对 cities.country_code是有效的,因为NULL代表一个值空缺。如果你不想允许country_code引用为NULL,可以这样定义cities表的列:country_code char(2)REFERENCES countries NOT NULL。

现在我们再试试插入一个美国城市的数据。

INSERT INTO cities

VALUES ('Portland','87200','us');

INSERT 0 1图2-3 PREFERENCE关键字约束字段参照另一张表的主键。

当然,这是一次成功的插入。但是我们输入了错误的邮政编码。波特兰(Portland)正确的邮政编码是97205,但我们不必删除并重新插入,可以直接更新这一行。

UPDATE cities

SET postal_code = '97205'

WHERE name = 'Portland';

现在已经可以创建、读取、更新、删除表中的行了。2.2.3 使用联接的查询

在本书中学习的所有其他数据库,也都可以执行CRUD操作。但PostgreSQL这样的关系数据库有独特的能力,能够在读取表时对表进行联接。联接本质上是以某种方式联接两个独立的表,并返回一张结果表。这有点像拼字游戏,打散单词的字母卡片,重新拼接字母,从而得到新的词。

联接的基本形式是内联接(inner join)。最简单的形式就是,使用ON关键字指定匹配的两列(每张表一列)

SELECT cities.*, country_name

FROM cities INNER JOIN countries

 ON cities.country_code = countries.country_code;

country_code |   name | postal_code | country_name

-------------+---------+-------------+----------------

us      | Portland | 97205    | United States

联接返回单张表,其中包含cities表的所有列的值,再加上匹配的countries表中country_name的值。

也可以联接诸如cities这样有复合主键的表。为了测试复合联接,我创建一张新表,来存储场地(venue)的列表。

某个国家和一个邮政编码组成一个场所。外键必须引用 Cities 表的两个主键列。(MATCH FULL是一个约束,确保两个值都存在,或两者均为NULL。)

CREATE TABLE venues (

 venue_id SERIAL PRIMARY KEY,

 name varchar(255),

 street_address text,

 type char(7) CHECK ( type in ('public','private') ) DEFAULT 'public',

 postal_code varchar(9),

 country_code char(2),

 FOREIGN KEY (country_code, postal_code)

  REFERENCES cities (country_code, postal_code) MATCH FULL

);

其中venue_id列是一种常见的主键设置:设置为自动递增整数(1,2,3,4,…)。可以使用 SERIAL 关键字来定义这个标识符( MySQL 有一个类似的构造,称为AUTO_INCREMENT)。

INSERT INTO venues (name, postal_code, country_code)

VALUES ('Crystal Ballroom', '97205', 'us');

虽然没有设置venue_id的值,但创建行时会填充它。

回到复合联接。联接enues表和cities表需要用到两个外键列。为了减少输入量,可以在表名后面直接加别名,它们中间的AS是可选的(例如,venues v或venues AS v)。

SELECT v.venue_id, v.name, c.name

FROM venues v INNER JOIN cities c

  ON v.postal_code=c.postal_code AND v.country_code=c.country_code;

venue_id | name | name

---------+---------+---------

   1 | Crystal Ballroom | Portland

可以选择指定 PostgreSQL 在插入后返回一些列,方法是让请求以 RETURNING语句结尾。

INSERT INTO venues (name, postal_code, country_code)

VALUES ('Voodoo Donuts', '97205', 'us') RETURNING venue_id;

 id

- - - -

 2

无须执行另一个查询,就可以得到新插入的venue_id值。2.2.4 外联接

除了内联接,PostgreSQL也可以执行外联接(outer join)。外联接是合并两张表的一种方式,不论另一张表中是否存在匹配的列值,第一张表的结果总是必须返回。

最简单的方法是举一个例子,但是首先我们需要创建一张名为 events 的新表。events表应该有这些列:SERIAL整数event_id、title、starts和ends(类型为时间戳),以及venue_id(引用venues的外键)。图2-4展示了一个数据库的定义图,它涵盖了到目前为止我们创建的所有表。图2-4 鱼尾纹实体关系图(ERD)

创建events表后,插入以下值(时间戳作为字符串插入,例如,2012-02-15 17:30),两个节日,以及我们不会详加讨论的一个俱乐部。

   title   |     starts   |    ends    | venue_id | event_id

-----------------+---------------------+---------------------+---------+--------

 LARP Club    | 2012-02-15 17:30:00 | 2012-02-15 19:30:00 | 2 |  1

 April Fools Day | 2012-04-01 00:00:00 | 2012-04-01 23:59:00 |  |  2

 Christmas Day  | 2012-12-25 00:00:00 | 2012-12-25 23:59:00 |  |  3

我们先来做一个查询,使用内联接返回一个事件的标题和场地名称(INNER JOIN中的INNER并不是必需的,所以这里省略它)。

SELECT e.title, v.name

FROM events e JOIN venues v

 ON e.venue_id = v.venue_id;

   title |  name

-------------+--------------

LARP Club  | Voodoo Donuts

只有列值匹配,INNER JOIN才会返回一行。因为不能有空的venues.venue_id,所以两个空events.venue_id没有关联到任何事情。要查询所有的事件,不管它们是否有场地,我们需要一个左外连接(LEFT OUTER JOIN,简写为LEFT JOIN)。

SELECT e.title, v.name

FROM events e LEFT JOIN venues v

试读结束[说明:试读内容隐藏了图片]

下载完整电子书


相关推荐

最新文章


© 2020 txtepub下载