打印

PostgreSQL 8.1 中文文档

5.3.4. 主键
从技术上来讲,主键约束只是唯一约束和非空约束的组合。 所以,下面两个表定义接受同样的数据:

CREATE TABLE products (
    product_no integer UNIQUE NOT NULL,
    name text,
    price numeric
);
CREATE TABLE products (
    product_no integer PRIMARY KEY,
    name text,
    price numeric
);
主键也可以约束多于一个字段;其语法类似唯一约束:

CREATE TABLE example (
    a integer,
    b integer,
    c integer,
    PRIMARY KEY (a, c)
);
主键表示一个字段或者是若干个字段的组合可以用于表中的数据行的唯一标识。 (这是定义一个主键的直接结果。请注意一个唯一约束实际上并不能提供一个唯一表示,因为它不排除空值。) 这个功能对文档目的和客户应用都很有用。比如,一个可以修改行数值的 GUI 应用可能需要知道一个表的主键才能唯一地标识一个行。

一个表最多可以有一个主键(但是它可以有多个唯一和非空约束)。 关系型数据库理论告诉我们,每个表都必须有一个主键。PostgreSQL 并不强制这个规则,但我们最好还是遵循它。

TOP

5.3.5. 外键
外键约束声明一个字段(或者一组字段)的数值必须匹配另外一个表中某些行出现的数值。 我们把这个行为称做两个相关表之间的参考完整性。

假设你有个产品表,我们可能使用了好几次:

CREATE TABLE products (
    product_no integer PRIMARY KEY,
    name text,
    price numeric
);让我们假设你有一个存储这些产品的订单的表。 我们想保证订单表只包含实际存在的产品。因此我们在订单表中定义一个外键约束引用产品表:

CREATE TABLE orders (
    order_id integer PRIMARY KEY,
    product_no integer REFERENCES products (product_no),
    quantity integer
);现在,我们不可能创建任何其 product_no 没有在产品表中出现的订单。

在这种情况下我们把订单表叫做引用表, 而产品表是被引用表。类似地也有引用字段和被引用字段。

你也可以把上面地命令简写成

CREATE TABLE orders (
    order_id integer PRIMARY KEY,
    product_no integer REFERENCES products,
    quantity integer
);因为如果缺少字段列表的话,被引用表的主键就会被当作被引用字段使用。

一个外键也可以约束和引用一组字段。同样,也需要写成表约束的形式。 下面是一个捏造出来的语法例子:

CREATE TABLE t1 (
  a integer PRIMARY KEY,
  b integer,
  c integer,
  FOREIGN KEY (b, c) REFERENCES other_table (c1, c2)
);当然,被约束的字段的数目和类型需要和被引用字段的数目和类型一致。

和平常一样,你也可以给外键约束赋予你自己的名字。

一个表可以包含多于一个外键约束。这个特性用于实现表之间多对多的 关系,比如你有关于产品和订单的表,但现在你想允许一个订单可以包含 多种产品(上面那个结构是不允许这么做的)。你可以使用这样的结构:

CREATE TABLE products (
    product_no integer PRIMARY KEY,
    name text,
    price numeric
);

CREATE TABLE orders (
    order_id integer PRIMARY KEY,
    shipping_address text,
    ...
);

CREATE TABLE order_items (
    product_no integer REFERENCES products,
    order_id integer REFERENCES orders,
    quantity integer,
    PRIMARY KEY (product_no, order_id)
);注意最后的表的主键和外键是重叠的。

我们知道外键不允许创建和任何产品都无关的订单。 但是如果一个订单创建之后,而其引用的产品被删除了会怎么办? SQL 也允许你处理这个问题。简单说,我们有几种选择:



不允许删除一个被引用的产品

同时也删除订单

其它的?


为了说明这个问题,让我们对上面的多对多的关系例子制定下面的 策略:如果有人想删除一种仍然被一个订单引用的产品(通过 order_items),那么我们不允许她这么做。 如果有人删除了一个订单,那么订单项也被删除。

CREATE TABLE products (
    product_no integer PRIMARY KEY,
    name text,
    price numeric
);

CREATE TABLE orders (
    order_id integer PRIMARY KEY,
    shipping_address text,
    ...
);

CREATE TABLE order_items (
    product_no integer REFERENCES products ON DELETE RESTRICT,
    order_id integer REFERENCES orders ON DELETE CASCADE,
    quantity integer,
    PRIMARY KEY (product_no, order_id)
);
限制和级联删除是两种最常见的选项。RESTRICT 禁止删除被引用的行。 NO ACTION 的意思是如果在检查约束的时候,如果还存在任何引用行,则抛出错误; 如果你不声明任何东西,那么它就是缺省的行为。 (这两个选择的实际区别是,NO ACTION 允许约束检查推迟到事务的晚些时候,而 RESTRICT 不行。) CASCADE 声明在删除一个被引用的行的时候,引用它的行也会被自动删除掉。 在外键字段上的动作还有两个选项: SET NULL 和 SET DEFAULT。 这样会导致在被引用行删除的时候,引用它们的字段分别设置为空或者缺省值。 请注意这些选项并不能让你逃脱被观察和约束的境地。 比如,如果一个动作声明 SET DEFAULT,但是缺省值并不能满足外键,那么动作就会失败。

类似 ON DELETE,还有 ON UPDATE 选项,它是在被引用字段修改(更新)的时候调用的。可用的动作是一样的。

有关更新和删除数据的更多信息可以在 Chapter 6 里找到。

最后,我们应该说明的是,一个外键必须要么引用一个主键,要么引用一个唯一约束。 如果外键引用了一个唯一约束,那么在如何匹配空值这个问题上还有一些其它的可能性。 这些东西都在 CREATE TABLE 里的 CREATE TABLE 中解释。

TOP

晕了..俺菜鸟..看晕菜了..

TOP

5.4. 系统字段
每个表都有几个系统字段,这些字段是由系统隐含定义的。 因此,这些名字不能用于用户定义的字段名。 (请注意这些限制与这个名字是否关键字无关;把名字用引号括起来并不能让你逃离这些限制。) 你实际上不需要注意这些字段,只要知道它们存在就可以了。


oid
行的对象标识符(对象 ID)。这个字段只有在创建表的时候使用了 WITH OIDS,或者是设置了配置参数 default_with_oids 时出现。 这个字段的类型是 oid(和字段同名); 参阅 Section 8.12 获取有关这种类型的更多信息。

tableoid
包含本行的表的 OID。这个字段对那些从继承层次中选取的查询特别有用(参阅 Section 5.8), 因为如果没有它的话,我们就很难说明一行来自哪个独立的表。 tableoid 可以和 pg_class 的 oid 字段连接起来获取表名字。

xmin
插入该行版本的事务的标识(事务 ID)。(注意:在这个环境里, 一个行版本是一行的一个状态;一行的每次更新都为同一个逻辑行创建一个新的行版本。)

cmin
在插入事务内部的命令标识(从零开始)。

xmax
删除事务的标识(事务ID),如果不是被删除的行版本,那么是零。 在一个可见行版本里,这个字段有可能是非零。这通常意味着删除事务还没有提交, 或者是一个删除的企图被回滚掉了。

cmax
在删除事务内部的命令标识符,或者是零。

ctid
一个行版本在它所处的表内的物理位置。请注意,尽管 ctid 可以用于非常快速地定位行版本,但每次 VACUUM FULL 之后, 一个行的 ctid 都会被更新或者移动。 因此 ctid 是不能作为长期的行标识符的。 应该使用OID,或者更好是用户定义的序列号,来标识一个逻辑行。

OID 是 32 位的量,是在同一个集群内通用的计数器上赋值的。 对于一个大型或者长时间使用的数据库,这个计数器是有可能重叠的。 因此,假设 OID 是唯一的是非常错误的,除非你自己采取了措施来保证它们是唯一的。 如果你需要标识表中的行,我们强烈建议使用序列号生成器。 不过,也可以使用 OID,只要采取几个注意事项即可:



在使用 OID 标识行的每个表的 OID 字段创建一个唯一约束。 在唯一约束(或者唯一索引)存在的时候,系统会注意不去生成一个和现有行相同的 OID。 (当然,只有在表中的数据行少于 2 32 (40 亿)行的时候才是可能的, 而实际上表中的行最好远比这个小,要不性能就会受影响了。)

绝对不要假设 OID 是跨表唯一的;如果你需要全数据库范围内的标识,请使用 tableoid 和行的 OID 的组合。

需要 OID 的表应该带着 WITH OIDS 创建。 对于 PostgreSQL 8.1,WITHOUT OIDS 是缺省。


事务标识符也是 32 位的量。在长时间运转的数据库里,它也可能会重叠。 只要我们采取一些合适的维护步骤,这并不是很要命的问题; 参阅 Chapter 22 获取细节。不过, 在长时间运行的环境里(超过十亿次事务)依赖事务 ID 的唯一性并非明智的做法。

命令标识符也是 32 位的量。这样就在一个事务里有232(四十亿)条 SQL 命令的硬限制。 在现实里这个限制应该不是什么问题 — 注意这个限制是 SQL 命令的条数, 而不是处理的行版本的条数。
燕子去了,有再来的时候;杨柳枯了,有再青的时候;桃花谢了,有再开的时候。但是,聪明的,你告诉我,我们的日子为什么一去不复返呢?——是有人偷了他们罢:那是谁?又藏在何处呢?是他们自己逃走了罢:现在又到了哪里呢?

TOP

5.5. 修改表
如果你创建了一个表后发现自己犯了一个错误,或者是应用的需求 发生了变化,那么你可以删除这个表然后重新创建它。但是如果这个 表已经填充了好多数据了,或者是该表已经被其它数据库对象引用, (比如一个外键约束) 那这可不是一个方便的选项。因此 PostgreSQL 提供了一族命令用于修改现有表。 请注意它在概念上和修改一个表中包含的数据是不一样的: 这里我们感兴趣的是修改一个表的定义,或者说结构。

你可以



增加字段,

删除字段,

增加约束,

删除约束,

修改缺省值,

修改字段数据类型,

重命名字段,

重命名表。

所有这些动作都是用 ALTER TABLE 命令执行的。

5.5.1. 增加字段
要增加一个字段,使用下面这样的命令:

ALTER TABLE products ADD COLUMN description text;新增的字段对于表中已经存在的行而言最初将先填充所给出的缺省值 (如果你没有声明DEFAULT子句,那么缺省是空值)。

你也可以同时在该字段上定义约束,使用通常的语法:

ALTER TABLE products ADD COLUMN description text CHECK (description <> '');实际上,所有在 CREATE TABLE 里描述的可以应用于字段之选项都可以在这里使用。 不过,我们要注意的是缺省值必须满足给出的约束,否则 ADD 将会失败。 另外,你可以在你正确填充了新字段的数值之后再增加约束(见下文)。

5.5.2. 删除字段
要删除一个字段,使用下面这样的命令:

ALTER TABLE products DROP COLUMN description;不管字段里有啥数据,都会小时。和这个字段相关的约束也会被删除。 不过,如果这个字段被另外一个表的外键所引用,PostgreSQL 则不会隐含地删除该约束。你可以通过使用 CASCADE 来授权删除任何依赖该字段的东西:

ALTER TABLE products DROP COLUMN description CASCADE;参阅 Section 5.11 获取有关这些操作背后的机制的信息。
燕子去了,有再来的时候;杨柳枯了,有再青的时候;桃花谢了,有再开的时候。但是,聪明的,你告诉我,我们的日子为什么一去不复返呢?——是有人偷了他们罢:那是谁?又藏在何处呢?是他们自己逃走了罢:现在又到了哪里呢?

TOP

5.5.3. 增加约束
要增加一个约束,使用表约束语法。比如:

ALTER TABLE products ADD CHECK (name <> '');
ALTER TABLE products ADD CONSTRAINT some_name UNIQUE (product_no);
ALTER TABLE products ADD FOREIGN KEY (product_group_id) REFERENCES product_groups;要增加一个不能写成表约束的非空约束,使用下面语法:

ALTER TABLE products ALTER COLUMN product_no SET NOT NULL;
这个约束将立即进行检查,所以表在添加约束之前必须符合约束条件。

5.5.4. 删除约束
要删除一个约束,你需要知道它的名字。如果你给了它一个名字, 那么事情就好办了。否则系统会分配一个生成的名字,这样你就需要 把它找出来了。psql 的命令 \d tablename 在这儿可以帮忙; 其它接口可能也提供了检查表的细节的方法。然后就是这条命令:

ALTER TABLE products DROP CONSTRAINT some_name;(如果你在处理一个生成的约束名,比如 $2,别忘了你需要给它 添加双引号,让它成为一个有效的标识符。)

和删除字段一样,如果你想删除有着被依赖关系地约束,你需要用 CASCADE。 一个例子是某个外键约束依赖被引用字段上的唯一约束或者主键约束。

除了非空约束外,所有约束类型都这么用。要删除非空类型,用

ALTER TABLE products ALTER COLUMN product_no DROP NOT NULL;(要记得非空约束没有名字。)

5.5.5. 改变一个字段的缺省值
要给一个字段设置缺省值,使用一个象下面这样的命令:

ALTER TABLE products ALTER COLUMN price SET DEFAULT 7.77;请注意这么做不会影响任何表中现有的数据行, 它只是为将来 INSERT 命令改变缺省值。

要删除缺省值,用

ALTER TABLE products ALTER COLUMN price DROP DEFAULT;这样实际上相当于把缺省设置为空。 结果是,如果我们删除一个还没有定义的缺省值不算错误,因为缺省隐含就是空值。

5.5.6. 修改一个字段的数据类型
把一个字段转换成另外一种数据类型,使用下面的命令:

ALTER TABLE products ALTER COLUMN price TYPE numeric(10,2);只有在字段里现有的每个项都可以用一个隐含的类型转换转换城新的类型时才可能成功。 如果需要更复杂的转换,你可以增加一个 USING 子句,它声明如何从旧值里计算新值。

PostgreSQL 将试图把字段的缺省值(如果存在)转换成新的类型, 还有涉及该字段的任何约束。但是这些转换可能失败,或者可能生成奇怪的结果。 在修改某字段类型之前,你最好删除那些约束,然后再把自己手工修改过的添加上去。

5.5.7. 给字段改名字
重命名一个字段:

ALTER TABLE products RENAME COLUMN product_no TO product_number;
5.5.8. 给字段改名字
给一个字段重命名:

ALTER TABLE products RENAME COLUMN product_no TO product_number;
5.5.9. 给表改名字
给一个表重命名:

ALTER TABLE products RENAME TO items
燕子去了,有再来的时候;杨柳枯了,有再青的时候;桃花谢了,有再开的时候。但是,聪明的,你告诉我,我们的日子为什么一去不复返呢?——是有人偷了他们罢:那是谁?又藏在何处呢?是他们自己逃走了罢:现在又到了哪里呢?

TOP

5.6. 权限
如果你创建了一个数据库对象,那么你就成为它的所有者。 缺省时,只有一个对象的所有者可以在对象上做任何事情。 为了允许其它用户使用它,我们必须赋予他们权限。 (不过拥有超级用户权限的用户总是可以访问任何对象。)

注意: 为了改变一个表,索引,序列,或者视图的所有者, 使用 ALTER TABLE 命令。

有好几种不同的权限:SELECT,INSERT, UPDATE,DELETE,RULE, REFERENCES,TRIGGER,CREATE, TEMPORARY,EXECUTE,USAGE。 适用于特定对象的权限因对象类型(表、函数等)而异。 和 ALL PRIVILEGES。有关 PostgreSQL 所支持的不同类型的权限的完整信息,请参考 GRANT 的手册页。下面的章节将为你展示如何利用这些权限。

修改或者删除一个对象的权限永远是所有者独有的权限。

注意: 要改变一个表,索引,序列,或者视图的所有者,使用 ALTER TABLE 命令。 还有对应其它对象类型的 ALTER 命令。

要赋予一个权限,我们使用 GRANT 命令。 因此,如果 joe 是一个现存的用户,而 accounts 是一个已经存在的表, 更新表的权限可以用下面的命令赋予

GRANT UPDATE ON accounts TO joe;要给一个组赋予权限,使用

GRANT SELECT ON accounts TO GROUP staff;名字叫PUBLIC的特殊"用户"可以用于 将权限赋予系统中的每一个用户。在声明权限的位置写 ALL 则将所有的与该对象类型相关的权限都赋予出去。

要撤销一个权限,使用合适的 REVOKE 命令:

REVOKE ALL ON accounts FROM PUBLIC;对象所有者的特殊权限(也就是做 DROP,GRANT, REVOKE,等等的权限)总是隐含地属于所有者,并且不能 赋予或者撤销。但是对象所有者可以选择撤销自己的普通权限, 比如把一个表做成对他自己和别人都是只读的。

最初,只有对象所有者(或者超级用户)可以赋予或者撤销对象的权限。 但是,我们可以赋予一个 "with grant option" 权限,这样就给接受权限的人以授予该权限给其它人的权限。 如果授予选项后来被撤销,那么所有那些从这个接受者接受了权限的用户(直接或者通过级连的授权)都将失去该权限。 细节详见 GRANT 和 REVOKE 手册页。
燕子去了,有再来的时候;杨柳枯了,有再青的时候;桃花谢了,有再开的时候。但是,聪明的,你告诉我,我们的日子为什么一去不复返呢?——是有人偷了他们罢:那是谁?又藏在何处呢?是他们自己逃走了罢:现在又到了哪里呢?

TOP

5.7. 模式
一个 PostgreSQL 数据库集群 包含一个或多个命名的数据库。用户和用户组在整个集群的范围内 是共享的,但是其它数据并不是共享的。任何给定的与服务器的客户连接都只 能访问在一个数据库里的数据,就是那个在连接请求里声明的。

注意: 一个集群的用户并不一定要有访问集群内所有数据库的权限。 共享用户名的意思是不能有同名用户,也就是,在同一个集群里的两个 数据库里都有叫 joe 的用户;但是系统可以配置成 只允许 joe 访问某些数据库。

一个数据库包含一个或多个命名的 模式, 模式又包含表。模式还包含其它命名的对象,包括数据类型,函数, 以及操作符。同一个对象名可以在不同的模式里使用而不会导致冲突; 比如,schema1 和 myschema 都可以包含叫做 mytable 的表。和数据库不同,模式不是严格分离的: 一个用户可以访问他所连接的数据库中的任意模式中的对象, 只要他有权限。

我们需要模式的原因有好多:



允许多个用户使用一个数据库而不会干扰其它用户。

把数据库对象组织成逻辑组,让它们更便于管理。

第三方的应用可以放在不同的模式中, 这样它们就不会和其它对象的名字冲突。

模式类似于操作系统层次的目录,只不过模式不能嵌套。
燕子去了,有再来的时候;杨柳枯了,有再青的时候;桃花谢了,有再开的时候。但是,聪明的,你告诉我,我们的日子为什么一去不复返呢?——是有人偷了他们罢:那是谁?又藏在何处呢?是他们自己逃走了罢:现在又到了哪里呢?

TOP

5.7.1. 创建一个模式
要创建一个模式,使用命令 CREATE SCHEMA。 给出你选择的模式名字。比如:

CREATE SCHEMA myschema;
要创建或者访问在模式中的对象,写出一个受修饰的名字, 这个名字包含模式名以及表(对象名),它们之间用一个句点分开:

schema.table这个方式在任何需要表名字的地方都可用,包括后面章节讨论的表修改命令和数据访问命令。 (出于简化,我们将只讨论表,这个概念适用于所有其它有名对象类型,比如类型和函数。)

实际上,更一般的语法是

database.schema.table这个语法也可以使用,但目前它只是为了和 SQL 标准形式上(pro forma)兼容。 如果你写了一个数据库名,那么它必须和你当前连接的数据库同名。

要在新模式里创建一个表,用

CREATE TABLE myschema.mytable (
...
);
如果一个模式是空的(所有它里面的对象都已经删除),那么删除一个模式的命令

DROP SCHEMA myschema;要删除一个包含所有对象的模式,使用

DROP SCHEMA myschema CASCADE;参阅 Section 5.11 获取躲藏在这些动作背后 的东西的一般机制的描述。

通常你想创建一个别人拥有的模式(因为这是一种限制你的用户 在定义良好的模式中的活动的方法)。其语法如下:

CREATE SCHEMA schemaname AUTHORIZATION username;你甚至可以省略模式名字,这时模式名将和用户名同名。 参阅 Section 5.7.6 获取这种情况 的适用场合。

以 pg_ 开头的模式名是保留给系统使用的, 用户不能创建这样的名字。
燕子去了,有再来的时候;杨柳枯了,有再青的时候;桃花谢了,有再开的时候。但是,聪明的,你告诉我,我们的日子为什么一去不复返呢?——是有人偷了他们罢:那是谁?又藏在何处呢?是他们自己逃走了罢:现在又到了哪里呢?

TOP

5.7.2. Public 模式
在前面的小节里,我们没有声明任何模式名字就创建了表。 缺省时,这样的表(以及其他对象)都自动放到一个叫做"public" 的模式中去了。每个新数据库都包含一个这样的模式。因此,下面的命令是等效的:

CREATE TABLE products ( ... );和

CREATE TABLE public.products ( ... );
5.7.3. 模式搜索路径
全称的名字写起来非常费劲,并且我们最好不要在应用里直接 写上特定的模式名。因此,表通常都是用未修饰的名字 引用的,这样的名字里只有表名字。系统通过查找一个搜索路径 来判断一个表究竟是哪个表,这个路径是一个需要查找的模式列表。 在搜索路径里找到的第一个表将被当作选定的表。如果在搜索路径中 没有匹配表,那么就报告一个错误,即使匹配表的名字在数据库其它的 模式中存在也如此。

在搜索路径中的第一个模式叫做当前模式。除了是搜索的第一个模式之外, 它还是在 CREATE TABLE 没有声明模式名的时候,新建表 所在的地方。

要显示当前搜索路径,使用下面的命令:

SHOW search_path;在缺省的设置中,返回下面的东西:

search_path
--------------
$user,public第一个元素声明将要搜索一个和当前用户同名的模式。 因为还没有这样的模式存在,所以这条记录被忽略。第二个元素指向 我们已经看过的公共模式。

搜索路径中存在的第一个模式是创建新对象的缺省位置。 这就是为什么缺省的对象都会创建在 public 模式里的原因。 如果在任何其它环境中引用对象,而且没有用模式修饰 (表修改,数据变更,或者查询命令),那么系统会遍历 搜索路径,直到找到一个匹配的对象。因此,在缺省的配置里, 任何未修饰的访问同样也只能引用 public 模式。

要把新的模式放到路径中来,我们用

SET search_path TO myschema,public;(我们在这里省略了 $user 是因为我们 并不是立即需要它。)然后我们就可以不用加模式修饰访问 表了:

DROP TABLE mytable;同样,因为 myschema 是路径中的第一个元素, 新对象缺省时将创建在这里。

我们也可以写成

SET search_path TO myschema;然后我们如果不明确修饰的话,就不能再访问 public 模式了。 public 模式没有任何特殊之处,只不过它缺省时就存在。 我们也可以把它删除了。

又见 Section 9.19 获取其它操作模式搜索路径的方法。

搜索路径对于数据类型名,函数名以及操作符名的运作方式和表名字完全相同。 数据类型和函数名可以象表名字一样加以修饰。如果你需要在表达式里写一个有修饰的操作符名字, 我们有一个特殊的要求:你必须这么写

OPERATOR(schema.operator)这样是为了避免语法歧义。下面是一个例子

SELECT 3 OPERATOR(pg_catalog.+) 4;实际上我们通常依赖搜索路径寻找操作符, 这样就不用写这么难看的东西了。
燕子去了,有再来的时候;杨柳枯了,有再青的时候;桃花谢了,有再开的时候。但是,聪明的,你告诉我,我们的日子为什么一去不复返呢?——是有人偷了他们罢:那是谁?又藏在何处呢?是他们自己逃走了罢:现在又到了哪里呢?

TOP


感谢一直以来您对我们的支持!
当前时区 GMT+8, 现在时间是 2008-8-22 06:37 京ICP证060528 号

Designed By 17DST