3.1. 介绍
在前面几章里,我们介绍了使用 SQL 存储和 访问在 PostgreSQL 里的数据的基本方法. 我们现在将讨论一些 SQL 更高级的特性,这些特性 可以简化管理和避免你的数据的丢失或损坏.最后,我们将看看一些 PostgreSQL 的扩展.
本章将不时引用在 Chapter 2 里的例子,并且 对它们进行修改和提高,因此如果你已经看过那章会更好.本章的一些例子也可以在教程目录里的advanced.sql 文件里找到.这个文件还包括一些要装载的例子数据,这些数据没有在这里介绍.(请参考 Section 2.1 获取如何使用该文件的方法.)
3.2. 视图
回头看看在 Section 2.6 里的查询. 假设你的应用对天气记录和城市位置的列表特别感兴趣,而你 又不想每次键入这些查询.那么你可以在查询上创建一个 视图,它给你引用的查询一个普通表的感觉.
CREATE VIEW myview AS
SELECT city, temp_lo, temp_hi, prcp, date, location
FROM weather, cities
WHERE city = name;
SELECT * FROM myview;
灵活使用视图是设计优良的 SQL 数据库的一个关键要素.视图允许我们把表的细节封装起来,这些表可能因你的应用的变化而变化, 但是我们却可以通过视图给用户一个一致的接口.
视图几乎可以在一个真正的表可以使用的任何地方使用. 在其它视图上面再建造视图也是很常见的.
3.3. 外键
回忆一下 Chapter 2 里的 weather 和 cities 表.考虑一下下面的问题∶你想确保没有人可以在 weather 表里插入一条在 cities 表里没有匹配记录的记录.这就叫维护你的表的参考完整性.在简单的数据库系统里,实现(如果也叫实现)这个特性的方法 通常是先看看 cities 表里是否有匹配的记录,然后插入或者拒绝新的 weather 记录.这个方法有许多问题,而且非常不便,因此 PostgreSQL 可以为你做这些.
新的表声明看起来会象下面这样∶
CREATE TABLE cities (
name varchar(80) primary key,
location point
);
CREATE TABLE weather (
city varchar(80) references weather,
temp_lo int,
temp_hi int,
prcp real,
date date
);
然后我们试图插入一条非法的记录∶
INSERT INTO weather VALUES ('Berkeley', 45, 53, 0.0, '1994-11-28');
ERROR: <unnamed> referential integrity violation - key referenced from weather not found in cities
外键的行为可以为你的应用仔细调节.在这份教程里我们就不再多说了,而是请你参考参考手册获取更多的信息.正确使用外键无疑将改进你的数据库应用,所以我们强烈建议你学习它们.
3.4. 事务
事务是所有数据库系统的一个基本概念.一次事务的要点就是它把多个步骤捆绑成了一个单一的,不成功则成仁的操作.其它并行的事务是看不到在这些步骤之间的中间状态的,并且如果发生了一些问题,导致该事务无法完成,那么所有这些步骤都完全不会影响数据库.
比如,假设一个银行的数据库包含各种客户帐户的余额,以及每个分行的总余额.假设我们要记录一次从 Alice 的帐户到 Bob 的帐户的金额为 $100.00 的支付动作.那么,完成这个任务的简单到极点的 SQL 命令象下面这样
UPDATE accounts SET balance = balance - 100.00
WHERE name = 'Alice';
UPDATE branches SET balance = balance - 100.00
WHERE name = (SELECT branch_name FROM accounts WHERE name = 'Alice');
UPDATE accounts SET balance = balance + 100.00
WHERE name = 'Bob';
UPDATE branches SET balance = balance + 100.00
WHERE name = (SELECT branch_name FROM accounts WHERE name = 'Bob');
这些命令的细节在这儿并不重要;重要的是这里牵涉到了好几个独立的更新来完成这个相当简单的操作.我们的银行官员会希望要么所有这些更新都生效,要么全部不起作用.我们当然不希望一次系统崩溃就导致 Bob 收到 100 块不是 Alice 支付的钱,也不希望 Alice 老是不花钱从 Bob 那里拿到物品.我们需要保证∶如果在操作的过程中出了差错, 那么所有这些步骤都不会发生效果.把这些更新组合成一个 事务就给予我们这样的保证.事务被认为是原子的∶从其它事务的角度来看, 它要么是全部发生,要么完全不发生.
我们还需要保证∶一旦一个事务完成并且得到数据库系统的认可,那么它必须被真正永久地存储,并且不会在随后的崩溃中消失.比如,如果我们记录到了一个 Bob 撤单的动作,那么我们不希望 仅仅在他走出银行大门之后的一次崩溃就会导致对他的帐户的 扣减动作消失.一个事务型数据库保证一个事务所做的所有更新 在事务发出完成响应之前都记录到永久的存储中(也就是磁盘).
事务型数据库的另外一个重要的性质和原子更新关系密切∶ 当多个事务并行地运行的时候,那么每个事务都不应看到其它事务 所做的未完成的变化.比如,如果一个事务正忙着计算所有分行的 余额总和,那么它不应该包括来自 Alice 的分行的扣帐和来自 Bob 分行的入帐,反之亦然.所以事务必须是黑白分明的,不仅仅体现在它们 在数据库上产生的永久影响出发,而且体现在它们运转时的自身的可视性上. 一个打开的事务做的更新在它完成之前是其它事务无法看到的,而且所有更新 是同时可见的.
在 PostgreSQL 里,一个事务是通过把SQL 命令用 BEGIN 和 COMMIT 命令包围实现的.因此我们的银行事务实际上看起来象下面这样
BEGIN;
UPDATE accounts SET balance = balance - 100.00
WHERE name = 'Alice';
-- etc etc
COMMIT;
如果在该事务的过程中,我们决定不做提交(可能是我们刚发现 Alice 的 余额是负数),那么我们可以发出 ROLLBACK 命令而不是 COMMIT 命令,那么到目前为止我们的所有更新都会被取消.
PostgreSQL 实际上把每个 SQL 语句当做在一个事务中执行的来看待.如果你没有发出 BEGIN 命令,那么每个独立 的语句都有一个隐含的 BEGIN 和(如果成功的话) COMMIT 语句包围在周围.一组包围在 BEGIN 和 COMMIT 语句中间的语句有时候被称做事务块.
注意: 一些客户库自动发出 BEGIN 和 COMMIT,因此你可能不需要特意请求就可以获取事务块的效果.查看你使用的接口的文档.
3.5. 继承
继承是面向对象的数据库的概念.它开启了数据库设计新的可能性大门.
让我们创建两个表∶一个表 cities 和一个表 capitals.自然,首府(capital)也可以是 城市(cities),因此在列出所有城市时你想要某种方法隐含地显示首府.如果你已经很高明了,那么你可能会创造类似下面这样的大纲∶
CREATE TABLE capitals (
name text,
population real,
altitude int, -- (in ft)
state char(2)
);
CREATE TABLE non_capitals (
name text,
population real,
altitude int -- (in ft)
);
CREATE VIEW cities AS
SELECT name, population, altitude FROM capitals
UNION
SELECT name, population, altitude FROM non_capitals;
如果只是查询,那么这个方法运转得很好,但是如果你需要更新某几行, 那这个方法就很难看了.
一种更好的方法是∶
CREATE TABLE cities (
name text,
population real,
altitude int -- (in ft)
);
CREATE TABLE capitals (
state char(2)
) INHERITS (cities);
在这个例子里,capitals 的一行继承所有来自它的父表,cities的所有字段(name, population,和altitude). 字段 name 的类型是 text,是 PostgreSQL 用于变长字符串的固有类型.州首府有一个额外的字段,州,显示所处的州.在 PostgreSQL 里,一个表可以从零个或者更多其它表中继承过来.
比如,下面的查询找出所有海拔超过 500 英尺的城市的名字,包括州首府∶
SELECT name, altitude
FROM cities
WHERE altitude > 500;
which returns:
name | altitude
-----------+----------
Las Vegas | 2174
Mariposa | 1953
Madison | 845
(3 rows)
另外一方面,下面的查询找出所有不是州首府并且位于海拔大于或等于 500 英尺的城市∶
SELECT name, altitude
FROM ONLY cities
WHERE altitude > 500;
name | altitude
-----------+----------
Las Vegas | 2174
Mariposa | 1953
(2 rows)
这里的 cities 前面的 ONLY 指示系统只对 cities 表运行查询,而不包括继承级别中低于 cities 的表.许多我们已经 讨论过的命令 -- SELECT, UPDATE 和 DELETE -- 支持这个 ONLY 表示法.
3.6. 结论
PostgreSQL 有许多这份教程里没有谈到的特性,因为这份教程主要是面向新 SQL 用户的.这些特性在用户手册和 程序员手册里有更充分的介绍.
如果你觉得自己需要更多介绍性材料,请访问 PostgreSQL网站获取更多资源的联接.

