打印

PostgreSQL 8.1 中文文档

4.2.8. 类型转换
一个类型转换声明一个从一种数据类型到另外一种数据类型的转换。 PostgreSQL 接受两种等效的类型转换语法:

CAST ( expression AS type )
expression::typeCAST 语法遵循 SQL;:: 的语法是 PostgreSQL 传统用法。

如果对一个已知类型的值表达式应用转换,它代表一个运行时类型转换。 只有在定义了合适的类型转换操作的情况下,该转换才能成功。 请注意这一点和用于常量的转换略有区别,如 Section 4.1.2.5 所示。 一个应用于某个未修饰的字串文本的转换表示给一个字串文本数值赋予一个初始化类型, 因此它对于任何类型都会成功(如果字串文本的内容符合该数据类型的输入语法接受。)

如果对于一个值表达式生成的数值对某类型而言不存在混淆的情况, 那么我们可以省略明确的类型转换(比如,在给一个表字段赋值的时候); 在这样的情况下,系统将自动附加一个类型转换。 不过,自动转换只适用于那些系统表中标记着 "OK to apply implicitly" 的转换函数。 其它转换函数必须用明确的转换语法调用。 这些限制是为了避免一些怪异的转换被应用。

我们也可以用函数样的语法声明一个类型转换:

typename ( expression )不过,这个方法只能用于那些名字同时也是有效函数名字的类型。 比如,double precision 就不能这么用, 但是等效的 float8 可以。同样,interval, time,和 timestamp 如果加了双引号也只能这么用, 因为存在语法冲突。因此,函数样的类型转换会导致不一致, 所以可能应该避免在新应用中这么用。 (函数样语法实际上就似乎一个函数调用。如果使用两种标准转换语法做运行时转换, 那么它将在内部调用一个已注册得函数执行转换。通常, 这种转换函数和它们得输出类型同名,但是这个要点可不是那些可以移植的程序可以依赖的东西。)

TOP

4.2.9. 标量子查询
一个标量子查询是一个放在圆括弧里的普通 SELECT查询, 它只返回只有一个字段的一行。(参阅 Chapter 7 获取有关写查询的信息。) 该 SELECT 将被执行, 而其单个返回值将在周围的值表达式中使用。 把一个返回超过一行或者超过一列的查询用做标量查询是错误的。 (不过,在特定的执行中,子查询不返回行则不算错误;标量结果认为是NULL。) 该子查询可以引用周围查询的变量,那些变量也是在计算任意子查询的时候当做常量使用的。 又见 Section 9.16。

比如,下面的查询找出每个州中的最大人口数量的城市:

SELECT name, (SELECT max(pop) FROM cities WHERE cities.state = states.name)
FROM states;
4.2.10. 数组构造器
一个数组构造器是一个表达式,它从它的成员元素上构造一个数组值。 一个简单的数组构造器由关键字 ARRAY,一个左方括弧 [, 一个或多个表达式(用逗号分隔)表示数组元素值,以及最后一个右方括弧 ]。 比如

SELECT ARRAY[1,2,3+4];
  array
---------
{1,2,7}
(1 row)数组元素类型是成员表达式的公共类型,使用和 UNION 或 CASE 构造一样的规则决定。 (参阅 Section 10.5)。

多维数组值可以通过嵌套数组构造器的方法来制作。 在内层构造器里,关键字 ARRAY 可以省略。比如,下面的两句生成同样的结果:

SELECT ARRAY[ARRAY[1,2], ARRAY[3,4]];
     array
---------------
{{1,2},{3,4}}
(1 row)

SELECT ARRAY[[1,2],[3,4]];
     array
---------------
{{1,2},{3,4}}
(1 row)因为多维数组必须是方形,同层的内层构造器必须生成同维的子数组。

多维数组构造器元素可以是任何生成合适数组的东西,而不仅仅是一个子 ARRAY 构造。 比如:

CREATE TABLE arr(f1 int[], f2 int[]);

INSERT INTO arr VALUES (ARRAY[[1,2],[3,4]], ARRAY[[5,6],[7,8]]);

SELECT ARRAY[f1, f2, '{{9,10},{11,12}}'::int[]] FROM arr;
                     array
------------------------------------------------
{{{1,2},{3,4}},{{5,6},{7,8}},{{9,10},{11,12}}}
(1 row)
我们也可以从一个子查询的结果中构造一个数组。在这种形式下, 数组构造器是用关键字 ARRAY 后面跟着一个用圆括弧(不是方括弧)包围的子查询。 比如:

SELECT ARRAY(SELECT oid FROM pg_proc WHERE proname LIKE 'bytea%');
                          ?column?
-------------------------------------------------------------
{2011,1954,1948,1952,1951,1244,1950,2005,1949,1953,2006,31}
(1 row)子查询必须返回一个字段。生成的一维数组将为子查询里每行结果生成一个元素, 元素类型匹配子查询的输出字段。

用 ARRAY 建立的数组值的脚标总是从一开始。 有关数组的更多信息,参阅 Section 8.10。

TOP

4.2.11. 行构造
一个行构造器是一个从提供给它的成员字段数值中制作行数值(也叫复合类型值)的表达式。 一个行构造器由关键字 ROW,一个左圆括弧, 零个或者多个用做行字段值的表达式(用逗号分隔),以及最后一个右圆括弧。比如,

SELECT ROW(1,2.5,'this is a test');如果在列表里有多个表达式,那么关键字 ROW 是可选的。

缺省时,ROW 表达式创建的值是一个匿名的记录类型。如果必要,你可以把它转换成一个命名的复合类型 — 既可以是一个表的行类型,也可以是一个用 CREATE TYPE AS 创建的复合类型。 可能会需要一个明确的转换以避免歧义。比如:

CREATE TABLE mytable(f1 int, f2 float, f3 text);

CREATE FUNCTION getf1(mytable) RETURNS int AS 'SELECT $1.f1' LANGUAGE SQL;

-- 因为只有一个 getf1() 存在,所以不需要类型转换
SELECT getf1(ROW(1,2.5,'this is a test'));
getf1
-------
     1
(1 row)

CREATE TYPE myrowtype AS (f1 int, f2 text, f3 numeric);

CREATE FUNCTION getf1(myrowtype) RETURNS int AS 'SELECT $1.f1' LANGUAGE SQL;

-- 现在我们需要类型转换以表明调用哪个函数:
SELECT getf1(ROW(1,2.5,'this is a test'));
ERROR:  function getf1(record) is not unique

SELECT getf1(ROW(1,2.5,'this is a test')::mytable);
getf1
-------
     1
(1 row)

SELECT getf1(CAST(ROW(11,'this is a test',2.5) AS myrowtype));
getf1
-------
    11
(1 row)
行构造器可以用于制作存储在复合类型表字段里面的复合类型值, 或者是传递给一个接受复合类型参数的函数。还有,我们也可以比较两个行数值或者用 IS NULL 或 IS NOT NULL 测试一个行数值,比如

SELECT ROW(1,2.5,'this is a test') = ROW(1, 3, 'not the same');

SELECT ROW(a, b, c) IS NOT NULL FROM table;更多的细节,请参阅 Section 9.17。 行构造还可以用于连接子查询,这些在 Section 9.16 里面有详细讨论。

TOP

4.2.12. 表达式计算规则
子表达式的计算顺序是没有定义的。特别要指出的是, 一个操作符或者函数的输入并不一定是按照从左向右的顺序或者以某种特定的顺序进行计算的。

另外,如果一个表达式的结果可以通过只判断它的一部分就可以得到, 那么其它子表达式就可以完全不计算了。比如,如果我们这么写

SELECT true OR somefunc();那么 somefunc() 就(可能)根本不会被调用。 如果我们写下面的,也可能会是这样

SELECT somefunc() OR true;请注意这里和某些编程语言里的从左向右"短路"是不一样的。

因此,拿那些有副作用的函数作为复杂表达式的一部分是不明智的选择。 在 WHERE 和 HAVING 子句里面依赖副作用或者是计算顺序是特别危险的, 因为这些子句都是作为生成一个执行规划的一部分进行了大量的再处理。 在这些子句里的布尔表达式(AND/OR/NOT 的组合)可以以布尔代数运算律允许的任意方式进行识别。

如果强制计算顺序非常重要,那么可以使用 CASE 构造(参阅 Section 9.13)。 比如,下面是一种视图避免在 WHERE 子句里被零除的不可信的方法:

SELECT ... WHERE x <> 0 AND y/x > 1.5;但是下面这样的是安全的:

SELECT ... WHERE CASE WHEN x <> 0 THEN y/x > 1.5 ELSE false END;用这种风格的 CASE 构造会阻止优化,因此应该只在必要的时候使用。 (在这个特殊的例子里,毫无疑问写成 y > 1.5*x 更好。)



4.2.12. 表达式计算规则
子表达式的计算顺序是没有定义的。特别要指出的是, 一个操作符或者函数的输入并不一定是按照从左向右的顺序或者以某种特定的顺序进行计算的。

另外,如果一个表达式的结果可以通过只判断它的一部分就可以得到, 那么其它子表达式就可以完全不计算了。比如,如果我们这么写

SELECT true OR somefunc();那么 somefunc() 就(可能)根本不会被调用。 如果我们写下面的,也可能会是这样

SELECT somefunc() OR true;请注意这里和某些编程语言里的从左向右"短路"是不一样的。

因此,拿那些有副作用的函数作为复杂表达式的一部分是不明智的选择。 在 WHERE 和 HAVING 子句里面依赖副作用或者是计算顺序是特别危险的, 因为这些子句都是作为生成一个执行规划的一部分进行了大量的再处理。 在这些子句里的布尔表达式(AND/OR/NOT 的组合)可以以布尔代数运算律允许的任意方式进行识别。

如果强制计算顺序非常重要,那么可以使用 CASE 构造(参阅 Section 9.13)。 比如,下面是一种视图避免在 WHERE 子句里被零除的不可信的方法:

SELECT ... WHERE x <> 0 AND y/x > 1.5;但是下面这样的是安全的:

SELECT ... WHERE CASE WHEN x <> 0 THEN y/x > 1.5 ELSE false END;用这种风格的 CASE 构造会阻止优化,因此应该只在必要的时候使用。 (在这个特殊的例子里,毫无疑问写成 y > 1.5*x 更好。)

TOP

Chapter 5. 数据定义
Table of Contents
5.1. 表的基本概念
5.2. 缺省值
5.3. 约束
5.3.1. 检查约束
5.3.2. 非空约束
5.3.3. 唯一约束
5.3.4. 主键
5.3.5. 外键
5.4. 系统字段
5.5. 修改表
5.5.1. 增加字段
5.5.2. 删除字段
5.5.3. 增加约束
5.5.4. 删除约束
5.5.5. 改变一个字段的缺省值
5.5.6. 修改一个字段的数据类型
5.5.7. 给字段改名字
5.5.8. 给字段改名字
5.5.9. 给表改名字
5.6. 权限
5.7. 模式
5.7.1. 创建一个模式
5.7.2. Public 模式
5.7.3. 模式搜索路径
5.7.4. 模式和权限
5.7.5. 系统表模式
5.7.6. 使用方式
5.7.7. 移植性
5.8. 继承
5.9. 分区
5.9.1. 概述
5.9.2. 实现分区
5.9.3. 分区和约束排除
5.10. 其它数据库对象
5.11. 依赖性追踪
本章介绍我们如何创建一个保存我们的数据的数据库结构。 在关系型数据库里,裸数据是存储在表中的,因此本章的大部分内容都将用于介绍如何创建表以及如何修改他们, 以及我们在控制表中存储的数据上有什么可以获得的特性。随后, 我们讨论表是如何能组织成模式的,以及如何给表赋予权限。最后, 我们将简单查看一下影响数据存储的其他因素,比如继承,视图,函数,和触发器。

TOP

5.1. 表的基本概念
关系型数据库中的表非常类似纸面上的一张表:它由行和列组成。 字段的数目是固定的,每个字段都有一个名字。行的数目是变化的 -- 它反映在任意时刻里存储的数据量。SQL 对表中的行的顺序没有任何承诺---除非你要求明确地进行排序。 这些内容在 Chapter 7 里介绍。另外,SQL 并不给行赋予唯一的标识, 因此我们很可能在一个表中有好几个完全相同的行。 这是作为SQL的基础的下层数学模型的必然结果,但是通常是我们不愿意看到的。 本章稍后的部分将讨论如何处理这个问题。

每个字段都有一个数据类型。数据类型约束可以赋予一个字段的可能数值的集合, 并且约束存储在字段里的数据的赋值语义,这样它就可以用于计算。比如, 一个声明为一个数值类型的字段将不会接受任意文本字串,而存储在这样的字段里的数据可以用于数学计算。 相比之下,一个声明为字符字串类型的字段将接受几乎任意类型的数据, 但是它们自身是不能进行数学计算的,不过我们可以进行其他象字串连接这样的操作。

PostgreSQL 包含一套可剪裁的内置数据类型, 这些类型可以适用于许多应用。用户也可以定义它们自己的数据类型。 大多数内置的数据类型有显而易见的名字和语义,因此我们把详细的解释放在了 Chapter 8。 有些常用的数据类型是用于整数的 integer,用于可能为分数的 numeric,用于字符串的 text,用于日期的 date, 用于时间的 time,以及用于包含日期和时间的数值的 timestamp。

要创建一个表,你使用一个命名合适的 CREATE TABLE 命令。 在这个命令里,你至少为新表声明一个名字,字段的名字以及字段的数据类型。比如:

CREATE TABLE my_first_table (
    first_column text,
    second_column integer
);这样就创建了一个有两个字段的叫做 my_first_table 的表。第一个字段的名字是 first_column,数据类型为 text;第二个字段的名字是 second_column, 数据类型是 integer。表和字段的名字遵循我们在 Section 4.1.1 里面解释的标识符语法。 类型名通常也是标识符,但是有一些例外。请注意字段列表是逗号分隔的, 并且用圆括弧包围。

当然,前面的例子是非常虚构的一个例子。通常,你会给你的表和字段名字, 这些字段里存储它们保存的数据。所以还是让我们给一个比较现实的例子:

CREATE TABLE products (
    product_no integer,
    name text,
    price numeric
);(numeric 类型可以存储分数部分,金额很可能有这样的分数部分。)

提示: 如果你创建了许多相互关联的表,那么最好选择一种一致的命名模式来为你的表和字段命名。 比如,表名字可以选择单数或者复数,两种选择都有这样那样的理论家支持。

有一个小限制:一个表能包含的字段数目。 根据字段类型的不同,这个数目可能在250到1600之间。 不过,不管是哪一端的数字,如果你设计的表包含那么多的字段好象都很不可能发生, 否则是设计上有问题的表现。

如果你不再需要这个表,那么你可以用 DROP TABLE 命令删除它。象这样:

DROP TABLE my_first_table;
DROP TABLE products;试图删除一个不存在的表是一个错误。不过,在 SQL 脚本文件里, 我们常见在创建表之前试图无条件删除它,忽略错误信息。

如果你需要修改一个已经存在的表,那么可以看看本章稍后的 Section 5.5。

使用到目前为止讨论的工具我们可以创建功能完整的表。 本章剩下的部分是有关向表定义中增加特性,保证数据完整性,安全性或者便利性的内容。 如果你急于给你的表填充数据,那么你可以忽略余下的部分直接到 Chapter 6,然后在稍后的时候再阅读本章。

TOP

--------------------------------------------------------------------------------

5.2. 缺省值
一个字段可以赋予缺省值。如果新创建了一个数据行,而有些字段的数值没有声明,那么这些字段将被填充与它们各自的缺省值。 一条数据修改命令也可以明确地要求把一个字段设置成为它地缺省值, 而不用事先知道这个数值是什么。(有关数据操作的命令在 Chapter 6。)

如果没有明确声明缺省值,那么缺省值是空。 这么做通常是合理的,因为空值可以认为代表未知数据。

在一个表定义里,缺省值是在字段数据类型后面列出。比如:

CREATE TABLE products (
    product_no integer,
    name text,
    price numeric DEFAULT 9.99
);
缺省值可以是一个表达式,它会在插入缺省值的时候计算(不是创建表的时候)。 一个常见的例子是一个 timestamp 字段可能有缺省值 now(), 这样它就设置为插入行的时刻。 另外一个常见的例子是为每一行生成一个"序列号"。 在 PostgreSQL 里,通常是用类似下面这样的东西生成的

CREATE TABLE products (
    product_no integer DEFAULT nextval('products_product_no_seq'),
    ...
);这里的 nextval() 从一个序列对象 (参阅 Section 9.12)提供后继的数值。 这种做法非常普遍,以至于我们有一个特殊的缩写用于此目的:

CREATE TABLE products (
    product_no SERIAL,
    ...
);SERIAL 缩写在Section 8.1.4 里有进一步描述。

TOP

--------------------------------------------------------------------------------

5.3. 约束
数据类型是约束我们可以在表里存储什么类型的数据的一种方法。 不过,对于许多应用,它们提供的约束实在是太粗糙。比如, 一个包含产品价格的字段可能应该只接受正数。但是没有哪种标准数据类型只接受正数。 另外一个问题是你可能需要根据其他字段或者行的数据来约束字段数据。比如,在一个包含产品信息的表中, 每个产品编号都应该只有一行。

对于这些问题,SQL允许你在字段和表上定义约束。 约束给予你所需要对数据施加的一切控制。如果一个用户企图在一个字段里存储会违反约束的数据,那么就会抛出一个错误。 这种情况同时也适用于数值来自缺省值的情况。

5.3.1. 检查约束
检查约束是最常见的约束类型。它允许你声明在某个字段里的数值必须满足一个布尔表达式。比如,要强制一个正数的产品价格, 你可以用:

CREATE TABLE products (
    product_no integer,
    name text,
    price numeric CHECK (price > 0)
);
如你所见,约束定义在数据类型后面,就好像缺省值定义一样。 缺省值和约束可以用任意的顺序排列。一个检查约束由一个关键字 CHECK 后面跟着一个放在圆括弧里的表达式组成。 检查约束表达式应该包含受约束的字段,否则这个约束就没什么意义了。

你还可以给这个约束一个独立的名字。这样就可以令错误信息更清晰, 并且在你要修改它的时候你可以查询这个约束。语法是:

CREATE TABLE products (
    product_no integer,
    name text,
    price numeric CONSTRAINT positive_price CHECK (price > 0)
);因此,要声明一个命名约束,使用关键字CONSTRAINT, 它后面跟着一个标识符,然后再跟着约束定义。(如果你不用这个方法声明约束, 那么系统为你选择一个名字。)

一个检查约束也可以引用若干个字段。假设你存储一个正常价格和一个折扣价,并且你想保证折扣价比正常价低。

CREATE TABLE products (
    product_no integer,
    name text,
    price numeric CHECK (price > 0),
    discounted_price numeric CHECK (discounted_price > 0),
    CHECK (price > discounted_price)
);
头两个约束看上去应该很面熟。第三个使用了一个新的语法。 它没有附着在某个字段上,它在逗号分隔的字段列表中是以一个独立行的形式出现的。 字段定义和这些约束定义可以以混合的顺序列出。

我们说头两个约束是字段约束,而第三个是表约束,因为它和任何一个字段定义的分开写。 字段约束也可以写成表约束,而反过来很可能不行,因为系统假设字段约束只引用它附着的字段。 (PostgreSQL 并不强制这条规则,但是如果你希望自己的表定义可以和其它数据库系统兼容, 那么你最好还是遵循这条规则。)上面的例子也可以这么写

CREATE TABLE products (
    product_no integer,
    name text,
    price numeric,
    CHECK (price > 0),
    discounted_price numeric,
    CHECK (discounted_price > 0),
    CHECK (price > discounted_price)
);或者是

CREATE TABLE products (
    product_no integer,
    name text,
    price numeric CHECK (price > 0),
    discounted_price numeric,
    CHECK (discounted_price > 0 AND price > discounted_price)
);这只是风格的不同。

和字段约束一样,我们也可以给表约束赋予名称,方法也相同:

CREATE TABLE products (
    product_no integer,
    name text,
    price numeric,
    CHECK (price > 0),
    discounted_price numeric,
    CHECK (discounted_price > 0),
    CONSTRAINT valid_discount CHECK (price > discounted_price)
);
我们还要知道一个检查约束在表达式计算出真或者空值的时候是满足条件的。 因为大多数表达式在任意操作数是空的时候都会得出空值, 所以这些约束不能在受约数字段上禁止空值。要确保一个字段不包含空值,我们可以使用下一节介绍的非空约束。

检查约束还可以用于提高表分区的性能。细节参阅 Section 5.9。

TOP

5.3.2. 非空约束
非空约束只是简单地声明一个字段必须不能是空值。下面是一个语法例子:

CREATE TABLE products (
    product_no integer NOT NULL,
    name text NOT NULL,
    price numeric
);
一个非空约束总是写成一个字段约束。 非空约束在功能上等效于创建一个检查约束 CHECK (column_name IS NOT NULL), 但在 PostgreSQL 里,创建一个明确的 非空约束效率更高。缺点是你不能给这么创建的非空约束一个明确的名字。

当然,一个字段可以有多个约束。只要在一个接着一个写就可以了:

CREATE TABLE products (
    product_no integer NOT NULL,
    name text NOT NULL,
    price numeric NOT NULL CHECK (price > 0)
);它的顺序无所谓。顺序并不影响约束检查的顺序。

NOT NULL 约束有个相反的约束:NULL 约束。这个约束并不意味着该字段必须是空,因为这样的字段也没啥用。 它只是定义了该字段可以为空的这个缺省行为。在 SQL 标准里没有定义 NULL 约束, 因此不应该在可移植的应用中使用它。 (我们在 PostgreSQL 里面增加这个约束只是为了和其它数据库系统兼容。) 不过,有些用户喜欢它,因为这个约束可以让他们很容易在脚本文件里切换约束。比如,你可以从下面这样开始

CREATE TABLE products (
    product_no integer NULL,
    name text NULL,
    price numeric NULL
);然后在需要的时候插入 NOT 关键字。

提示: 在大多数数据库设计里,主要的字段都应该标记为非空。

TOP

5.3.3. 唯一约束
唯一约束保证在一个字段或者一组字段里的数据与表中其它行的数据相比是唯一的。它的语法是

CREATE TABLE products (
    product_no integer UNIQUE,
    name text,
    price numeric
);上面是写成字段约束,下面这个

CREATE TABLE products (
    product_no integer,
    name text,
    price numeric,
    UNIQUE (product_no)
);是写成表约束。

如果一个唯一约束引用一组字段,那么这些字段用逗号分隔列出:

CREATE TABLE example (
    a integer,
    b integer,
    c integer,
    UNIQUE (a, c)
);这样就声明了指定字段的数值的组合,在整个表的范围内是唯一的, 不过这些字段中的某个的数值可以不必是(并且通常也的确可能不是)唯一的。

你也可以给唯一约束赋予自己定义的名字,方法如常:

CREATE TABLE products (
    product_no integer CONSTRAINT must_be_different UNIQUE,
    name text,
    price numeric
);
通常,如果在表中有两行或更多行,而这些行中包含在唯一约束里面的那几个字段都相等,那么就算违反了唯一约束。 但是在这种比较中,空值是认为不相等的。这就意味着,在多字段唯一约束的情况下, 如果在至少一个字段上存在空值,那么我们还是可以存储同样的这种数据行。 这种行为遵循 SQL 标准,但是我们听说其它 SQL 数据库可能不遵循这个标准。因此如果你要开发可移植的程序, 那么最好仔细些。

TOP


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

Designed By 17DST