首先考虑上述提到的与PostgreSQL相关的书籍。我们的许多用户喜欢The Practical SQL Handbook, Bowman, Judith S. 编写,Addison-Wesley公司出版,其他的则喜欢 The Complete Reference SQL, Groff 编写,McGraw-Hill公司出版。
在下列网址上也有很好的教程,他们是
在运行 configure 时加上 --prefix 选项。
缺省情况下,PostgreSQL只允许来自本机且通过 unix 域套接字或TCP/IP方式的连接。 你只有在修改了配置文件postgresql.conf中的listen_addresses,且也在配置文件$PGDATA/pg_hba.conf中 打开了 基于远程电脑( host-based )的身份认证,并重新启动PostgreSQL,否则其他电脑是不能与你的PostgreSQL服务器进行连接的。
有三个主要方面可以提升PostgreSQL的潜能。
这表示你已达到缺省100个并发后台进程数的限制,你需要通过修改postgresql.conf文件中的max_connections值来 增加postmaster的后台并发处理数,修改后需重新启动postmaster。
如果你只是要提取几行数据,并且你在执行查询中知道确切的行数,你可以使用LIMIT功能。 如果有一个索引与 ORDER BY中的条件匹配,PostgreSQL 可能就只处理要求的头几条记录, (否则将对整个查询进行处理直到生成需要的行)。如果在执行查询功能时不知道确切的记录数, 可使用游标(cursor)和FETCH功能。
可使用以下方法提取一行随机记录的:
SELECT cols FROM tab ORDER BY random() LIMIT 1 ;
在psql中使用 \dt 命令来显示数据表的定义,要了解psql中的完整命令列表可使用\? ,另外,你也可以阅读 psql 的源代码 文件pgsql/src/bin/psql/describe.c,它包括为生成psql反斜杠命令的输出的所有 SQL 命令。你还可以带 -E 选项启动 psql, 这样它将打印出你在psql中所给出的命令执行时的内部实际使用的SQL查询语句。PostgreSQL也提供了一个兼容SQL的INFORMATION SCHEMA接口, 你可以从这里获取关于数据库的信息。
在系统中也有一些以pg_ 打头的系统表也描述了表的定义。
使用 psql -l 指令可以列出所有的数据库。
也可以浏览一下 pgsql/src/tutorial/syscat.source文件,它列举了很多可从数据库系统表中获取信息的SELECT语法。
在8.0及以后的版本里更改一个字段的数据类型很容易,可使用 ALTER TABLE ALTER COLUMN TYPE 。
在以前的版本中,可以这样做:
BEGIN; ALTER TABLE tab ADD COLUMN new_col new_data_type; UPDATE tab SET new_col = CAST(old_col AS new_data_type); ALTER TABLE tab DROP COLUMN old_col; COMMIT;
你然后可以使用VACUUM FULL tab 指令来使系统收回无效数据所占用的空间。
下面是一些限制:
单个数据库最大尺寸? 无限制(已存在有 32TB 的数据库) 单个表的最大尺寸? 32 TB 一行记录的最大尺寸? 400 GB 一个字段的最大尺寸? 1 GB 一个表里最大行数? 无限制 一个表里最大列数? 250-1600 (与列类型有关) 一个表里的最大索引数量? 无限制
当然,实际上没有真正的无限制,还是要受系统可用磁盘空间、可用内存/交换区的制约。 事实上,当上述这些数值变得异常地大时,系统性能也会受很大影响。
单表的最大大小 32 TB 不需要操作系统对单个文件也需这么大的支持。大表用多个 1 GB 的文件存储,因此文件系统大小的限制是不重要的。
如果缺省的块大小增长到 32K ,最大的单表大小和最大列数还可以增加到四倍。
有一个限制就是不能对大小多于2000字节的列创建索引。幸运地是这样的索引很少用到。通过对多字节列的内容进行MD5哈稀运算结果进行函数索引可对列的唯一性得到保证, 并且全文检索允许对列中的单词进行搜索。
一个 Postgres 数据库(存储一个文本文件)所占用的空间最多可能需要相当于这个文本文件自身大小5倍的磁盘空间。
例如,假设有一个 100,000 行的文件,每行有一个整数和一个文本描述。 假设文本串的平均长度为20字节。文本文件占用 2.8 MB。存放这些数据的PostgreSQL数据库文件大约是 6.4 MB:
24 字节: 每行的头(大约值) 24 字节: 一个整数型字段和一个文本型字段 + 4 字节: 页面内指向元组的指针 ---------------------------------------- 52 字节每行
PostgreSQL 数据页的大小是 8192 字节 (8 KB),则:
8192 字节每页 ------------------- = 158 行/数据页(向下取整) 52 字节每行
100000 数据行 -------------------- = 633 数据页(向上取整) 158 行/数据页
633 数据页 * 8192 字节/页 = 5,185,536 字节(5.2 MB)
索引不需要这么多的额外消耗,但也确实包括被索引的数据,因此它们也可能很大。
空值NULL存放在位图中,因此占用很少的空间。
并非每个查询都会自动使用索引。只有在表的大小超过一个最小值,并且查询只会选中表中较小比例的记录时才会采用索引。 这是因为索引扫描引起的随即磁盘存取可能比直接地读取表(顺序扫描)更慢。
为了判断是否使用索引,PostgreSQL必须获得有关表的统计值。这些统计值可以使用 VACUUM ANALYZE,或 ANALYZE 获得。 使用统计值,优化器知道表中有多少行,就能够更好地判断是否利用索引。 统计值对确定优化的连接顺序和连接方法也很有用。在表的内容发生变化时,应定期进行统计值的更新收集。
索引通常不用于 ORDER BY 或执行连接。对一个大表的一次顺序扫描再做一次排序通常比索引扫描要快。然而,如果将 LIMIT 和 ORDER BY 结合在一起使用的话,通常将会使用索引,因为这时仅返回表中的一小部分记录。
如果你确信PostgreSQL的优化器使用顺序扫描是不正确的,你可以使用SET enable_seqscan TO 'off'指令来关闭顺序扫描, 然后再次运行查询,你就可以看出使用一个索引扫描是否确实要快一些。
当使用通配符操作,例如 LIKE 或 ~ 时,索引只能在特定的情况下使用:
参考 EXPLAIN 手册页。
操作符 ~ 处理正则表达式匹配,而 ~* 处理大小写无关的正则表达式匹配。大小写无关的 LIKE 变种成为 ILIKE。
大小写无关的等式比较通常写做:
SELECT * FROM tab WHERE lower(col) = 'abc';
这样将不会使用标准的索引。但是可以创建一个在这种情况下使用的表达式索引:
CREATE INDEX tabindex ON tab (lower(col));
如果上述索引在创建时加入UNIQUE约束,虽然索引字段自身内容可以存储大小写不限的内容,但如果有UNIQUE约束后,这些内容不能仅仅是大小写不同(否则会造成冲突)。为了保证不发生这种情况,可以使用CHECK约束条件或是触发器在录入时进行限制。
用 IS NULL 和 IS NOT NULL 测试这个字段,具体方法如下:
SELECT * FROM tab WHERE col IS NULL;
为了可以处理可能存在的NULL值,可以按如下方式使用COALESCE() :
SELECT COALESCE(col1, ‘’) || COALESCE(col2, ‘’) FROM tab
为了能对含 NULL字段排序,可在 ORDER BY 条件中使用 IS NULL和 IS NOT NULL 修饰符,条件为真 true 将比条件为假false 排在前面,下面的例子就会将含 NULL 的记录排在结果的上面部分:
SELECT * FROM tab ORDER BY (col IS NOT NULL)
类型 内部名称 说明 VARCHAR(n) varchar 指定了最大长度,变长字符串,不足定义长度的部分不补齐 CHAR(n) bpchar 定长字符串,实际数据不足定义长度时,以空格补齐 TEXT text 没有特别的上限限制(仅受行的最大长度限制) BYTEA bytea 变长字节序列(使用NULL字符也是允许的) "char" char 单个字符
在系统表和在一些错误信息里你将看到内部名称。
上面所列的前四种类型是"varlena"(变长)类型(也就是说,开头的四个字节是长度,后面才是数据)。 于是实际占用的空间比声明的大小要多一些。 然而这些类型如定义很长时都可以被压缩存储,因此磁盘空间也可能比预想的要少。
VARCHAR(n) 在存储限制了最大长度的变长字符串是最好的。 TEXT 适用于存储最大可达 1G左右但未定义限制长度的字符串。
CHAR(n) 最适合于存储长度相同的字符串。 CHAR(n)会根据所给定的字段长度以空格补足(不足的字段内容), 而 VARCHAR(n) 只存储所给定的数据内容。 BYTEA 用于存储二进制数据,尤其是包含 NULL 字节的值。这些类型具有差不多的性能。
PostgreSQL 支持 SERIAL 数据类型。(字段定义为SERIAL后)将自动创建一个序列生成器,例如:
CREATE TABLE person ( id SERIAL, name TEXT );
会自动转换为以下SQL语句:
CREATE SEQUENCE person_id_seq; CREATE TABLE person ( id INT4 NOT NULL DEFAULT nextval('person_id_seq'), name TEXT );
参考 create_sequence 手册页获取关于序列生成器的更多信息。
最简单的提取插入记录的序列号的方法是使用RETURNING功能,使用4.11.1例子中的表,则例子如下:
INSERT INTO person (name) VALUES ('Blaise Pascal') RETURNING id;
上述查询执行后会返回id,你也可以使用nextval()函数先获取当前id的数值,然后使用该值进行INSERT,或者是在执行INSERT后执行currval()函数也可以达到目的。
不会。currval() 返回的是你本次会话进程所赋的值而不是所有用户的当前值。
为了提高并发性,序列号在需要的时候赋予正在运行的事务,并且在事务结束之前不进行锁定, 这就会导致异常中止的事务后,序列号会出现间隔。
PostgreSQL 里创建的每一行记录都会获得一个唯一的OID,除非在创建表时使用WITHOUT OIDS选项。 OID创建时会自动生成一个4字节的整数,所有 OID 在相应PostgreSQL服务器中均是唯一的。 然而,它在超过40亿时将溢出, OID此后会出现重复。PostgreSQL 在它的内部系统表里使用 OID 在表之间建立联系。
在用户的数据表中,最好是使用SERIAl来代替OID 因为SERIAL只要保证在单个表中的数值是唯一的就可以了,这样它溢出的可能性就非常小了, SERIAL8可用来保存8字节的序列数值。
CTID 用于标识带着数据块(地址)和(块内)偏移的特定的物理行。 CTID 在记录被更改或重载后发生改变。索引数据使用它们指向物理行。
这很可能是系统的虚拟内存用光了,或者内核对某些资源有较低的限制值。在启动 postmaster 之前试试下面的命令:
ulimit -d 262144 limit datasize 256m
取决于你用的 shell,上面命令只有一条能成功,但是它将把你的进程数据段限制设得比较高, 因而也许能让查询完成。这条命令应用于当前进程,以及所有在这条命令运行后创建的子进程。 如果你是在运行SQL客户端时因为后台返回了太多的数据而出现问题,请在运行客户端之前执行上述命令。
从 psql 里,输入 SELECT version();指令。
使用 CURRENT_TIMESTAMP:
CREATE TABLE test (x int, modtime TIMESTAMP DEFAULT CURRENT_TIMESTAMP );
PostgreSQL 采用标准的 SQL 语法支持外连接。这里是两个例子:
SELECT * FROM t1 LEFT OUTER JOIN t2 ON (t1.col = t2.col);
或是
SELECT * FROM t1 LEFT OUTER JOIN t2 USING (col);
这两个等价的查询在 t1.col 和 t2.col 上做连接,并且返回 t1 中所有未连接的行(那些在 t2 中没有匹配的行)。 右[外]连接(RIGHT OUTER JOIN)将返回 t2 中未连接的行。 完全外连接(FULL OUTER JOIN)将返回 t1 和 t2 中未连接的行。 关键字 OUTER 在左[外]连接、右[外]连接和完全[外]连接中是可选的,普通连接被称为内连接(INNER JOIN)。
没有办法查询当前数据库之外的数据库。 因为PostgreSQL要加载与数据库相关的系统目录(系统表),因此跨数据库的查询如何执行是不定的。
附加增值模块contrib/dblink允许采用函数调用实现跨库查询。当然用户也可以同时连接到不同的数据库执行查询然后在客户端合并结果。
在函数中返回数据记录集的功能是很容易使用的,详情参见: http://www.postgresql.org/docs/techdocs.17
在PostgreSQL 8.3版本之前,PL/PgSQL会缓存函数的脚本内容,由此带来的一个不好的副作用是若一个 PL/PgSQL 函数访问了一个临时表,然后该表被删除并重建了,则再次调用该函数将失败, 因为缓存的函数内容仍然指向旧的临时表。解决的方法是在 PL/PgSQL 中用EXECUTE 对临时表进行访问。这样会保证查询在执行前总会被重新解析。
这个问题在8.3及以后的版本中不会再出现。
一个 Postgres 数据库(存储一个文本文件)所占用的空间最多可能需要相当于这个文本文件自身大小5倍的磁盘空间。
例如,假设有一个 100,000 行的文件,每行有一个整数和一个文本描述。 假设文本串的平均长度为20字节。文本文件占用 2.8 MB。存放这些数据的PostgreSQL数据库文件大约是 6.4 MB:
24 字节: 每行的头(大约值) 24 字节: 一个整数型字段和一个文本型字段 + 4 字节: 页面内指向元组的指针 ---------------------------------------- 52 字节每行
PostgreSQL 数据页的大小是 8192 字节 (8 KB),则:
8192 字节每页 ------------------- = 158 行/数据页(向下取整) 52 字节每行
100000 数据行 -------------------- = 633 数据页(向上取整) 158 行/数据页
633 数据页 * 8192 字节/页 = 5,185,536 字节(5.2 MB)
索引不需要这么多的额外消耗,但也确实包括被索引的数据,因此它们也可能很大。
空值NULL存放在位图中,因此占用很少的空间。
并非每个查询都会自动使用索引。只有在表的大小超过一个最小值,并且查询只会选中表中较小比例的记录时才会采用索引。 这是因为索引扫描引起的随即磁盘存取可能比直接地读取表(顺序扫描)更慢。
为了判断是否使用索引,PostgreSQL必须获得有关表的统计值。这些统计值可以使用 VACUUM ANALYZE,或 ANALYZE 获得。 使用统计值,优化器知道表中有多少行,就能够更好地判断是否利用索引。 统计值对确定优化的连接顺序和连接方法也很有用。在表的内容发生变化时,应定期进行统计值的更新收集。
索引通常不用于 ORDER BY 或执行连接。对一个大表的一次顺序扫描再做一次排序通常比索引扫描要快。然而,如果将 LIMIT 和 ORDER BY 结合在一起使用的话,通常将会使用索引,因为这时仅返回表中的一小部分记录。
如果你确信PostgreSQL的优化器使用顺序扫描是不正确的,你可以使用SET enable_seqscan TO 'off'指令来关闭顺序扫描, 然后再次运行查询,你就可以看出使用一个索引扫描是否确实要快一些。
当使用通配符操作,例如 LIKE 或 ~ 时,索引只能在特定的情况下使用:
参考 EXPLAIN 手册页。
操作符 ~ 处理正则表达式匹配,而 ~* 处理大小写无关的正则表达式匹配。大小写无关的 LIKE 变种成为 ILIKE。
大小写无关的等式比较通常写做:
SELECT * FROM tab WHERE lower(col) = 'abc';
这样将不会使用标准的索引。但是可以创建一个在这种情况下使用的表达式索引:
CREATE INDEX tabindex ON tab (lower(col));
如果上述索引在创建时加入UNIQUE约束,虽然索引字段自身内容可以存储大小写不限的内容,但如果有UNIQUE约束后,这些内容不能仅仅是大小写不同(否则会造成冲突)。为了保证不发生这种情况,可以使用CHECK约束条件或是触发器在录入时进行限制。
用 IS NULL 和 IS NOT NULL 测试这个字段,具体方法如下:
SELECT * FROM tab WHERE col IS NULL;
为了可以处理可能存在的NULL值,可以按如下方式使用COALESCE() :
SELECT COALESCE(col1, ‘’) || COALESCE(col2, ‘’) FROM tab
为了能对含 NULL字段排序,可在 ORDER BY 条件中使用 IS NULL和 IS NOT NULL 修饰符,条件为真 true 将比条件为假false 排在前面,下面的例子就会将含 NULL 的记录排在结果的上面部分:
SELECT * FROM tab ORDER BY (col IS NOT NULL)
类型 内部名称 说明 VARCHAR(n) varchar 指定了最大长度,变长字符串,不足定义长度的部分不补齐 CHAR(n) bpchar 定长字符串,实际数据不足定义长度时,以空格补齐 TEXT text 没有特别的上限限制(仅受行的最大长度限制) BYTEA bytea 变长字节序列(使用NULL字符也是允许的) "char" char 单个字符
在系统表和在一些错误信息里你将看到内部名称。
上面所列的前四种类型是"varlena"(变长)类型(也就是说,开头的四个字节是长度,后面才是数据)。 于是实际占用的空间比声明的大小要多一些。 然而这些类型如定义很长时都可以被压缩存储,因此磁盘空间也可能比预想的要少。
VARCHAR(n) 在存储限制了最大长度的变长字符串是最好的。 TEXT 适用于存储最大可达 1G左右但未定义限制长度的字符串。
CHAR(n) 最适合于存储长度相同的字符串。 CHAR(n)会根据所给定的字段长度以空格补足(不足的字段内容), 而 VARCHAR(n) 只存储所给定的数据内容。 BYTEA 用于存储二进制数据,尤其是包含 NULL 字节的值。这些类型具有差不多的性能。
PostgreSQL 支持 SERIAL 数据类型。(字段定义为SERIAL后)将自动创建一个序列生成器,例如:
CREATE TABLE person ( id SERIAL, name TEXT );
会自动转换为以下SQL语句:
CREATE SEQUENCE person_id_seq; CREATE TABLE person ( id INT4 NOT NULL DEFAULT nextval('person_id_seq'), name TEXT );
参考 create_sequence 手册页获取关于序列生成器的更多信息。
最简单的提取插入记录的序列号的方法是使用RETURNING功能,使用4.11.1例子中的表,则例子如下:
INSERT INTO person (name) VALUES ('Blaise Pascal') RETURNING id;
上述查询执行后会返回id,你也可以使用nextval()函数先获取当前id的数值,然后使用该值进行INSERT,或者是在执行INSERT后执行currval()函数也可以达到目的。
不会。currval() 返回的是你本次会话进程所赋的值而不是所有用户的当前值。
为了提高并发性,序列号在需要的时候赋予正在运行的事务,并且在事务结束之前不进行锁定, 这就会导致异常中止的事务后,序列号会出现间隔。
PostgreSQL 里创建的每一行记录都会获得一个唯一的OID,除非在创建表时使用WITHOUT OIDS选项。 OID创建时会自动生成一个4字节的整数,所有 OID 在相应PostgreSQL服务器中均是唯一的。 然而,它在超过40亿时将溢出, OID此后会出现重复。PostgreSQL 在它的内部系统表里使用 OID 在表之间建立联系。
在用户的数据表中,最好是使用SERIAl来代替OID 因为SERIAL只要保证在单个表中的数值是唯一的就可以了,这样它溢出的可能性就非常小了, SERIAL8可用来保存8字节的序列数值。
CTID 用于标识带着数据块(地址)和(块内)偏移的特定的物理行。 CTID 在记录被更改或重载后发生改变。索引数据使用它们指向物理行。
这很可能是系统的虚拟内存用光了,或者内核对某些资源有较低的限制值。在启动 postmaster 之前试试下面的命令:
ulimit -d 262144 limit datasize 256m
取决于你用的 shell,上面命令只有一条能成功,但是它将把你的进程数据段限制设得比较高, 因而也许能让查询完成。这条命令应用于当前进程,以及所有在这条命令运行后创建的子进程。 如果你是在运行SQL客户端时因为后台返回了太多的数据而出现问题,请在运行客户端之前执行上述命令。
从 psql 里,输入 SELECT version();指令。
使用 CURRENT_TIMESTAMP:
CREATE TABLE test (x int, modtime TIMESTAMP DEFAULT CURRENT_TIMESTAMP );
PostgreSQL 采用标准的 SQL 语法支持外连接。这里是两个例子:
SELECT * FROM t1 LEFT OUTER JOIN t2 ON (t1.col = t2.col);
或是
SELECT * FROM t1 LEFT OUTER JOIN t2 USING (col);
这两个等价的查询在 t1.col 和 t2.col 上做连接,并且返回 t1 中所有未连接的行(那些在 t2 中没有匹配的行)。 右[外]连接(RIGHT OUTER JOIN)将返回 t2 中未连接的行。 完全外连接(FULL OUTER JOIN)将返回 t1 和 t2 中未连接的行。 关键字 OUTER 在左[外]连接、右[外]连接和完全[外]连接中是可选的,普通连接被称为内连接(INNER JOIN)。
没有办法查询当前数据库之外的数据库。 因为PostgreSQL要加载与数据库相关的系统目录(系统表),因此跨数据库的查询如何执行是不定的。
附加增值模块contrib/dblink允许采用函数调用实现跨库查询。当然用户也可以同时连接到不同的数据库执行查询然后在客户端合并结果。
在函数中返回数据记录集的功能是很容易使用的,详情参见: http://www.postgresql.org/docs/techdocs.17
在PostgreSQL 8.3版本之前,PL/PgSQL会缓存函数的脚本内容,由此带来的一个不好的副作用是若一个 PL/PgSQL 函数访问了一个临时表,然后该表被删除并重建了,则再次调用该函数将失败, 因为缓存的函数内容仍然指向旧的临时表。解决的方法是在 PL/PgSQL 中用EXECUTE 对临时表进行访问。这样会保证查询在执行前总会被重新解析。
这个问题在8.3及以后的版本中不会再出现。