这个概念对于很多已经熟悉其他数据库编程的开发人员而言会多少有些陌生,然而它的实现方式和设计原理却是简单易懂,现在就让我们从一个简单的例子开始吧。
1. 第一个继承表:
CREATE TABLE cities ( --父表
name text,
population float,
altitude int
);
CREATE TABLE capitals ( --子表
state char(2)
) INHERITS (cities);
capitals表继承自cities表的所有属性。在PostgreSQL里,一个表可以从零个或多个其它表中继承属性,而且一个查询既可以引用父表中的所有行,也可以引用父表的所有行加上其所有子表的行,其中后者是缺省行为。
MyTest=# INSERT INTO cities values('Las Vegas', 1.53, 2174); --插入父表
INSERT 0 1
MyTest=# INSERT INTO cities values('Mariposa',3.30,1953); --插入父表
INSERT 0 1
MyTest=# INSERT INTO capitals values('Madison',4.34,845,'WI');--插入子表
INSERT 0 1
MyTest=# SELECT name, altitude FROM cities WHERE altitude > 500; --父表和子表的数据均被取出。
name | altitude
-----------+----------
Las Vegas | 2174
Mariposa | 1953
Madison | 845
(3 rows)
MyTest=# SELECT name, altitude FROM capitals WHERE altitude > 500; --只有子表的数据被取出。
name | altitude
---------+----------
Madison | 845
(1 row)
如果希望只从父表中提取数据,则需要在SQL中加入ONLY关键字,如:
MyTest=# 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"符号。
在执行整表数据删除时,如果直接truncate父表,此时父表和其所有子表的数据均被删除,如果只是truncate子表,那么其父表的数据将不会变化,只是子表中的数据被清空。
MyTest=# TRUNCATE TABLE cities; --父表和子表的数据均被删除。
TRUNCATE TABLE
MyTest=# SELECT * FROM capitals;
name | population | altitude | state
------+------------+----------+-------
(0 rows)
2. 确定数据来源:
有时候你可能想知道某条记录来自哪个表。在每个表里我们都有一个系统隐含字段tableoid,它可以告诉你表的来源:
MyTest=# SELECT tableoid, name, altitude FROM cities WHERE altitude > 500;
tableoid | name | altitude
----------+-----------+----------
16532 | Las Vegas | 2174
16532 | Mariposa | 1953
16538 | Madison | 845
(3 rows)
以上的结果只是给出了tableoid,仅仅通过该值,我们还是无法看出实际的表名。要完成此操作,我们就需要和系统表pg_class进行关联,以通过tableoid字段从该表中提取实际的表名,见以下查询:
MyTest=# SELECT p.relname, c.name, c.altitude FROM cities c,pg_class p WHERE c.altitude > 500 and c.tableoid = p.oid;
relname | name | altitude
----------+-----------+----------
cities | Las Vegas | 2174
cities | Mariposa | 1953
capitals | Madison | 845
(3 rows)
3. 数据插入的注意事项:
继承并不自动从INSERT或者COPY中向继承级别中的其它表填充数据。在我们的例子里,下面的INSERT语句不会成功:
INSERT INTO cities (name, population, altitude, state) VALUES ('New York', NULL, NULL, 'NY');
我们可能希望数据被传递到capitals表里面去,但是这是不会发生的:INSERT总是插入明确声明的那个表。
4. 多表继承:
一个表可以从多个父表继承,这种情况下它拥有父表们的字段的总和。子表中任意定义的字段也会加入其中。如果同一个字段名出现在多个父表中,或者同时出现在父表和子表的定义里,那么这些字段就会被"融合",这样在子表里面就只有一个这样的字段。要想融合,字段必须是相同的数据类型,否则就会抛出一个错误。融合的字段将会拥有它所继承的字段的所有约束。
CREATE TABLE parent1 (FirstCol integer);
CREATE TABLE parent2 (FirstCol integer, SecondCol varchar(20));
CREATE TABLE parent3 (FirstCol varchar(200));
--子表child1将同时继承自parent1和parent2表,而这两个父表中均包含integer类型的FirstCol字段,因此child1可以创建成功。
CREATE TABLE child1 (MyCol timestamp) INHERITS (parent1,parent2);
--子表child2将不会创建成功,因为其两个父表中均包含FirstCol字段,但是它们的类型不相同。
CREATE TABLE child2 (MyCol timestamp) INHERITS (parent1,parent3);
--子表child3同样不会创建成功,因为它和其父表均包含FirstCol字段,但是它们的类型不相同。
CREATE TABLE child3 (FirstCol varchar(20)) INHERITS(parent1);
5. 继承和权限:
表访问权限并不会自动继承。因此,一个试图访问父表的用户还必须具有访问它的所有子表的权限,或者使用ONLY关键字只从父表中提取数据。在向现有的继承层次添加新的子表的时候,请注意给它赋予所有权限。
继承特性的一个严重的局限性是索引(包括唯一约束)和外键约束只施用于单个表,而不包括它们的继承的子表。这一点不管对引用表还是被引用表都是事实,因此在上面的例子里,如果我们声明cities.name为UNIQUE或者是一个PRIMARY KEY,那么也不会阻止capitals表拥有重复了名字的cities数据行。 并且这些重复的行缺省时在查询cities表的时候会显示出来。实际上,缺省时capitals将完全没有唯一约束,因此可能包含带有同名的多个行。你应该给capitals增加唯一约束,但是这样做也不会避免与cities的重复。类似,如果我们声明cities.name REFERENCES某些其它的表,这个约束不会自动广播到capitals。在这种条件下,你可以通过手工给capitals 增加同样的REFERENCES约束来做到这点。
二、分区表:
1. 概述分区表:
分区的意思是把逻辑上的一个大表分割成物理上的几块儿,分区可以提供若干好处:
1). 某些类型的查询性能可以得到极大提升。
2). 更新的性能也可以得到提升,因为表的每块的索引要比在整个数据集上的索引要小。如果索引不能全部放在内存里,那么在索引上的读和写都会产生更多的磁盘访问。
3). 批量删除可以用简单地删除某个分区来实现。
4). 将很少用的数据可以移动到便宜的、慢一些地存储介质上。
假设当前的数据库并不支持分区表,而我们的应用所需处理的数据量也非常大,对于这种应用场景,我们不得不人为的将该大表按照一定的规则,手工拆分成多个小表,让每个小表包含不同区间的数据。这样一来,我们就必须在数据插入、更新、删除和查询之前,先计算本次的指令需要操作的小表。对于有些查询而言,由于查询区间可能会跨越多个小表,这样我们又不得不将多个小表的查询结果进行union操作,以合并来自多个表的数据,并最终形成一个结果集返回给客户端。可见,如果我们正在使用的数据库不支持分区表,那么在适合其应用的场景下,我们就需要做很多额外的编程工作以弥补这一缺失。然而需要说明的是,尽管功能可以勉强应付,但是性能却和分区表无法相提并论。
目前PostgreSQL支持的分区形式主要为以下两种:
1). 范围分区: 表被一个或者多个键字字段分区成"范围",在这些范围之间没有重叠的数值分布到不同的分区里。比如,我们可以为特定的商业对象根据数据范围分区,或者根据标识符范围分区。
2). 列表分区: 表是通过明确地列出每个分区里应该出现那些键字值实现的。
2. 实现分区:
1). 创建"主表",所有分区都从它继承。
CREATE TABLE measurement ( --主表
city_id int NOT NULL,
logdate date NOT NULL,
peaktemp int,
);
2). 创建几个"子"表,每个都从主表上继承。通常,这些"子"表将不会再增加任何字段。我们将把子表称作分区,尽管它们就是普通的PostgreSQL表。
CREATE TABLE measurement_yy04mm02 ( ) INHERITS (measurement);
CREATE TABLE measurement_yy04mm03 ( ) INHERITS (measurement);
...
CREATE TABLE measurement_yy05mm11 ( ) INHERITS (measurement);
CREATE TABLE measurement_yy05mm12 ( ) INHERITS (measurement);
CREATE TABLE measurement_yy06mm01 ( ) INHERITS (measurement);
上面创建的子表,均已年、月的形式进行范围划分,不同年月的数据将归属到不同的子表内。这样的实现方式对于清空分区数据而言将极为方便和高效,即直接执行DROP TABLE语句删除相应的子表,之后在根据实际的应用考虑是否重建该子表(分区)。相比于直接DROP子表,PostgreSQL还提供了另外一种更为方便的方式来管理子表:
ALTER TABLE measurement_yy06mm01 NO INHERIT measurement;
和直接DROP相比,该方式仅仅是使子表脱离了原有的主表,而存储在子表中的数据仍然可以得到访问,因为此时该表已经被还原成一个普通的数据表了。这样对于数据库的DBA来说,就可以在此时对该表进行必要的维护操作,如数据清理、归档等,在完成诸多例行性的操作之后,就可以考虑是直接删除该表(DROP TABLE),还是先清空该表的数据(TRUNCATE TABLE),之后再让该表重新继承主表,如:
ALTER TABLE measurement_yy06mm01 INHERIT measurement;
3). 给分区表增加约束,定义每个分区允许的健值。同时需要注意的是,定义的约束要确保在不同的分区里不会有相同的键值。因此,我们需要将上面"子"表的定义修改为以下形式:
CREATE TABLE measurement_yy04mm02 (
CHECK ( logdate >= DATE '2004-02-01' AND logdate < DATE '2004-03-01')
) INHERITS (measurement);
CREATE TABLE measurement_yy04mm03 (
CHECK (logdate >= DATE '2004-03-01' AND logdate < DATE '2004-04-01')
) INHERITS (measurement);
...
CREATE TABLE measurement_yy05mm11 (
CHECK (logdate >= DATE '2005-11-01' AND logdate < DATE '2005-12-01')
) INHERITS (measurement);
CREATE TABLE measurement_yy05mm12 (
CHECK (logdate >= DATE '2005-12-01' AND logdate < DATE '2006-01-01')
) INHERITS (measurement);
CREATE TABLE measurement_yy06mm01 (
CHECK (logdate >= DATE '2006-01-01' AND logdate < DATE '2006-02-01')
) INHERITS (measurement);
4). 尽可能基于键值创建索引。如果需要,我们也同样可以为子表中的其它字段创建索引。
CREATE INDEX measurement_yy04mm02_logdate ON measurement_yy04mm02 (logdate);
CREATE INDEX measurement_yy04mm03_logdate ON measurement_yy04mm03 (logdate);
...
CREATE INDEX measurement_yy05mm11_logdate ON measurement_yy05mm11 (logdate);
CREATE INDEX measurement_yy05mm12_logdate ON measurement_yy05mm12 (logdate);
CREATE INDEX measurement_yy06mm01_logdate ON measurement_yy06mm01 (logdate);
5). 定义一个规则或者触发器,把对主表的修改重定向到适当的分区表。
如果数据只进入最新的分区,我们可以设置一个非常简单的规则来插入数据。我们必须每个月都重新定义这个规则,即修改重定向插入的子表名,这样它总是指向当前分区。
CREATE OR REPLACE RULE measurement_current_partition AS
ON INSERT TO measurement
DO INSTEAD
INSERT INTO measurement_yy06mm01 VALUES (NEW.city_id, NEW.logdate, NEW.peaktemp);
其中NEW是关键字,表示新数据字段的集合。这里可以通过点(.)操作符来获取集合中的每一个字段。
我们可能想插入数据并且想让服务器自动定位应该向哪个分区插入数据。我们可以用像下面这样的更复杂的规则集来实现这个目标。
CREATE RULE measurement_insert_yy04mm02 AS
ON INSERT TO measurement WHERE (logdate >= DATE '2004-02-01' AND logdate < DATE '2004-03-01')
DO INSTEAD
INSERT INTO measurement_yy04mm02 VALUES (NEW.city_id, NEW.logdate, NEW.peaktemp);
...
CREATE RULE measurement_insert_yy05mm12 AS
ON INSERT TO measurement WHERE (logdate >= DATE '2005-12-01' AND logdate < DATE '2006-01-01')
DO INSTEAD
INSERT INTO measurement_yy05mm12 VALUES (NEW.city_id, NEW.logdate, NEW.peaktemp);
CREATE RULE measurement_insert_yy06mm01 AS
ON INSERT TO measurement WHERE (logdate >= DATE '2006-01-01' AND logdate < DATE '2006-02-01')
DO INSTEAD
INSERT INTO measurement_yy06mm01 VALUES (NEW.city_id, NEW.logdate, NEW.peaktemp);
请注意每个规则里面的WHERE子句正好匹配其分区的CHECK约束。
可以看出,一个复杂的分区方案可能要求相当多的DDL。在上面的例子里我们需要每个月创建一次新分区,因此写一个脚本自动生成需要的DDL是明智的。除此之外,我们还不难推断出,分区表对于新数据的批量插入操作有一定的抑制,这一点在Oracle中也同样如此。
除了上面介绍的通过Rule的方式重定向主表的数据到各个子表,我们还可以通过触发器的方式来完成此操作,相比于基于Rule的重定向方法,基于触发器的方式可能会带来更好的插入效率,特别是针对非批量插入的情况。然而对于批量插入而言,由于Rule的额外开销是基于表的,而不是基于行的,因此效果会好于触发器方式。另一个需要注意的是,copy操作将会忽略Rules,如果我们想要通过COPY方法来插入数据,你只能将数据直接copy到正确的子表,而不是主表。这种限制对于触发器来说是不会造成任何问题的。基于Rule的重定向方式还存在另外一个问题,就是当插入的数据不在任何子表的约束中时,PostgreSQL也不会报错,而是将数据直接保留在主表中。
6). 添加新分区:
这里将介绍两种添加新分区的方式,第一种方法简单且直观,我们只是创建新的子表,同时为其定义新的检查约束,如:
CREATE TABLE measurement_y2008m02 (
CHECK ( logdate >= DATE '2008-02-01' AND logdate < DATE '2008-03-01' )
) INHERITS (measurement);
第二种方法的创建步骤相对繁琐,但更为灵活和实用。见以下四步:
/* 创建一个独立的数据表(measurement_y2008m02),该表在创建时以将来的主表(measurement)为模板,包含模板表的缺省值(DEFAULTS)和一致性约束(CONSTRAINTS)。*/
CREATE TABLE measurement_y2008m02
(LIKE measurement INCLUDING DEFAULTS INCLUDING CONSTRAINTS);
/* 为该表创建未来作为子表时需要使用的检查约束。*/
ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
CHECK (logdate >= DATE '2008-02-01' AND logdate < DATE '2008-03-01');
/* 导入数据到该表。下面只是给出一种导入数据的方式作为例子。在导入数据之后,如有可能,还可以做进一步的数据处理,如数据转换、过滤等。*/
\copy measurement_y2008m02 from 'measurement_y2008m02'
/* 在适当的时候,或者说在需要的时候,让该表继承主表。*/
ALTER TABLE measurement_y2008m02 INHERIT measurement;
7). 确保postgresql.conf里的配置参数constraint_exclusion是打开的。没有这个参数,查询不会按照需要进行优化。这里我们需要做的是确保该选项在配置文件中没有被注释掉。
/> pwd
/opt/PostgreSQL/9.1/data
/> cat postgresql.conf | grep "constraint_exclusion"
constraint_exclusion = partition # on, off, or partition
3. 分区和约束排除:
约束排除(Constraint exclusion)是一种查询优化技巧,它改进了用上面方法定义的表分区的性能。比如:
SET constraint_exclusion = on;
SELECT count(*) FROM measurement WHERE logdate >= DATE '2006-01-01';
如果没有约束排除,上面的查询会扫描measurement表中的每一个分区。打开了约束排除之后,规划器将检查每个分区的约束然后再视图证明该分区不需要被扫描,因为它不能包含任何符合WHERE子句条件的数据行。如果规划器可以证明这个,它就把该分区从查询规划里排除出去。
你可以使用EXPLAIN命令显示一个规划在constraint_exclusion打开和关闭情况下的不同。用上面方法设置的表的典型的缺省规划是:
SET constraint_exclusion = off;
EXPLAIN SELECT count(*) FROM measurement WHERE logdate >= DATE '2006-01-01';
QUERY PLAN
-----------------------------------------------------------------------------------------------
Aggregate (cost=158.66..158.68 rows=1 width=0)
-> Append (cost=0.00..151.88 rows=2715 width=0)
-> Seq Scan on measurement (cost=0.00..30.38 rows=543 width=0)
Filter: (logdate >= '2006-01-01'::date)
-> Seq Scan on measurement_yy04mm02 measurement (cost=0.00..30.38 rows=543 width=0)
Filter: (logdate >= '2006-01-01'::date)
-> Seq Scan on measurement_yy04mm03 measurement (cost=0.00..30.38 rows=543 width=0)
Filter: (logdate >= '2006-01-01'::date)
...
-> Seq Scan on measurement_yy05mm12 measurement (cost=0.00..30.38 rows=543 width=0)
Filter: (logdate >= '2006-01-01'::date)
-> Seq Scan on measurement_yy06mm01 measurement (cost=0.00..30.38 rows=543 width=0)
Filter: (logdate >= '2006-01-01'::date)
从上面的查询计划中可以看出,PostgreSQL扫描了所有分区。下面我们再看一下打开约束排除之后的查询计划:
SET constraint_exclusion = on;
EXPLAIN SELECT count(*) FROM measurement WHERE logdate >= DATE '2006-01-01';
QUERY PLAN
-----------------------------------------------------------------------------------------------
Aggregate (cost=63.47..63.48 rows=1 width=0)
-> Append (cost=0.00..60.75 rows=1086 width=0)
-> Seq Scan on measurement (cost=0.00..30.38 rows=543 width=0)
Filter: (logdate >= '2006-01-01'::date)
-> Seq Scan on measurement_yy06mm01 measurement (cost=0.00..30.38 rows=543 width=0)
Filter: (logdate >= '2006-01-01'::date)
请注意,约束排除只由CHECK约束驱动,而不会由索引驱动。
目前版本的PostgreSQL中该配置的缺省值是partition,该值是介于on和off之间的一种行为方式,即规划器只会将约束排除应用于基于分区表的查询,而on设置则会为所有查询都进行约束排除,那么对于普通数据表而言,也将不得不承担由该机制而产生的额外开销。
约束排除在使用时有以下几点注意事项:
1). 约束排除只是在查询的WHERE子句包含约束的时候才生效。一个参数化的查询不会被优化,因为在运行时规划器不知道该参数会选择哪个分区。因此像CURRENT_DATE这样的函数必须避免。把分区键值和另外一个表的字段连接起来也不会得到优化。
2). 在CHECK约束里面要避免跨数据类型的比较,因为目前规划器会无法证明这样的条件为假。比如,下面的约束会在x是整数字段的时候可用,但是在x是一个bigint的时候不能用:
CHECK (x = 1)
对于bigint字段,我们必须使用类似下面这样的约束:
CHECK (x = 1::bigint)
这个问题并不仅仅局限于bigint数据类型,它可能会发生在任何约束的缺省数据类型与其比较的字段的数据类型不匹配的场合。在提交的查询里的跨数据类型的比较通常是OK的,只是不能在CHECK条件里。
3). 在主表上的UPDATE和DELETE命令并不执行约束排除。
4). 在规划器进行约束排除时,主表上的所有分区的所有约束都将会被检查,因此,大量的分区会显著增加查询规划的时间。
5). 在执行ANALYZE语句时,要为每一个分区都执行该命令,而不是仅仅对主表执行该命令。
一、数值类型:
下面是PostgreSQL所支持的数值类型的列表和简单说明:
名字 | 存储空间 | 描述 | 范围 |
smallint | 2 字节 | 小范围整数 | -32768 到 +32767 |
integer | 4 字节 | 常用的整数 | -2147483648 到 +2147483647 |
bigint | 8 字节 | 大范围的整数 | -9223372036854775808 到 9223372036854775807 |
decimal | 变长 | 用户声明精度,精确 | 无限制 |
numeric | 变长 | 用户声明精度,精确 | 无限制 |
real | 4 字节 | 变精度,不精确 | 6 位十进制数字精度 |
double | 8 字节 | 变精度,不精确 | 15 位十进制数字精度 |
serial | 4 字节 | 自增整数 | 1 到 +2147483647 |
bigserial | 8 字节 | 大范围的自增整数 | 1 到 9223372036854775807 |
1. 整数类型:
类型smallint、integer和bigint存储各种范围的全部是数字的数,也就是没有小数部分的数字。试图存储超出范围以外的数值将导致一个错误。常用的类型是integer,因为它提供了在范围、存储空间和性能之间的最佳平衡。一般只有在磁盘空间紧张的时候才使用smallint。而只有在integer的范围不够的时候才使用bigint,因为前者(integer)绝对快得多。
2. 任意精度数值:
类型numeric可以存储最多1000位精度的数字并且准确地进行计算。因此非常适合用于货币金额和其它要求计算准确的数量。不过,numeric类型上的算术运算比整数类型或者浮点数类型要慢很多。
numeric字段的最大精度和最大比例都是可以配置的。要声明一个类型为numeric的字段,你可以用下面的语法:
NUMERIC(precision,scale)
比如数字23.5141的精度为6,而刻度为4。
在目前的PostgreSQL版本中,decimal和numeric是等效的。
3. 浮点数类型:
数据类型real和double是不准确的、牺牲精度的数字类型。不准确意味着一些数值不能准确地转换成内部格式并且是以近似的形式存储的,因此存储后再把数据打印出来可能显示一些缺失。
4. Serial(序号)类型:
serial和bigserial类型不是真正的类型,只是为在表中设置唯一标识做的概念上的便利。
CREATE TABLE tablename (
colname SERIAL
);
等价于
CREATE SEQUENCE tablename_colname_seq;
CREATE TABLE tablename(
colname integer DEFAULT nextval('tablename_colname_seq') NOT NULL
);
这样,我们就创建了一个整数字段并且把它的缺省数值安排为从一个序列发生器取值。应用了一个NOT NULL约束以确保空值不会被插入。在大多数情况下你可能还希望附加一个UNIQUE或者PRIMARY KEY约束避免意外地插入重复的数值,但这个不是自动发生的。因此,如果你希望一个序列字段有一个唯一约束或者一个主键,那么你现在必须声明,就像其它数据类型一样。
还需要另外说明的是,一个serial类型创建的序列在其所属字段被删除时,该序列也将被自动删除,但是其它情况下是不会被删除的。因此,如果你想用同一个序列发生器同时给几个字段提供数据,那么就应该以独立对象的方式创建该序列发生器。
二、字符类型:
下面是PostgreSQL所支持的字符类型的列表和简单说明:
名字 | 描述 |
varchar(n) | 变长,有长度限制 |
char(n) | 定长,不足补空白 |
text | 变长,无长度限制 |
SQL 定义了两种基本的字符类型,varchar(n)和char(n),这里的n是一个正整数。两种类型都可以存储最多n个字符长的字串,试图存储更长的字串到这些类型的字段里会产生一个错误,除非超出长度的字符都是空白,这种情况下该字串将被截断为最大长度。如果没有长度声明,char等于char(1),而varchar则可以接受任何长度的字串。
MyTest=> CREATE TABLE testtable(first_col varchar(2));
CREATE TABLE
MyTest=> INSERT INTO testtable VALUES('333'); --插入字符串的长度,超过其字段定义的长度,因此报错。
ERROR: value too long for type character varying(2)
--插入字符串中,超出字段定义长度的部分是空格,因此可以插入,但是空白符被截断。
MyTest=> INSERT INTO testtable VALUES('33 ');
INSERT 0 1
MyTest=> SELECT * FROM testtable;
first_col
-----------
33
(1 row)
这里需要注意的是,如果是将数值转换成char(n)或者varchar(n),那么超长的数值将被截断成n个字符,而不会抛出错误。
MyTest=> select 1234::varchar(2);
varchar
---------
12
(1 row)
最后需要提示的是,这三种类型之间没有性能差别,只不过是在使用char类型时增加了存储尺寸。虽然在某些其它的数据库系统里,char(n)有一定的性能优势,但在PostgreSQL里没有。在大多数情况下,应该使用text或者varchar。
三、日期/时间类型:
下面是PostgreSQL所支持的日期/时间类型的列表和简单说明:
名字 | 存储空间 | 描述 | 最低值 | 最高值 | 分辨率 |
timestamp[无时区] | 8字节 | 包括日期和时间 | 4713 BC | 5874897AD | 1毫秒/14位 |
timestamp[含时区] | 8字节 | 日期和时间,带时区 | 4713 BC | 5874897AD | 1毫秒/14位 |
interval | 12字节 | 时间间隔 | -178000000年 | 178000000年 | 1毫秒/14位 |
date | 4字节 | 只用于日期 | 4713 BC | 32767AD | 1天 |
time[无时区] | 8字节 | 只用于一日内时间 | 00:00:00 | 24:00:00 | 1毫秒/14位 |
1. 日期/时间输入:
任何日期或者时间的文本输入均需要由单引号包围,就象一个文本字符串一样。
1). 日期:
以下为合法的日期格式列表:
例子 | 描述 |
January 8, 1999 | 在任何datestyle输入模式下都无歧义 |
1999-01-08 | ISO-8601格式,任何方式下都是1999年1月8号,(建议格式) |
1/8/1999 | 歧义,在MDY下是1月8号;在 DMY模式下读做8月1日 |
1/18/1999 | 在MDY模式下读做1月18日,其它模式下被拒绝 |
01/02/03 | MDY模式下的2003年1月2日;DMY模式下的2003年2月1日;YMD 模式下的2001年2月3日 |
1999-Jan-08 | 任何模式下都是1月8日 |
Jan-08-1999 | 任何模式下都是1月8日 |
08-Jan-1999 | 任何模式下都是1月8日 |
99-Jan-08 | 在YMD模式下是1月8日,否则错误 |
08-Jan-99 | 1月8日,除了在YMD模式下是错误的之外 |
Jan-08-99 | 1月8日,除了在YMD模式下是错误的之外 |
19990108 | ISO-8601; 任何模式下都是1999年1月8日 |
990108 | ISO-8601; 任何模式下都是1999年1月8日 |
2). 时间:
以下为合法的时间格式列表:
例子 | 描述 |
04:05:06.789 | ISO 8601 |
04:05:06 | ISO 8601 |
04:05 | ISO 8601 |
040506 | ISO 8601 |
04:05 AM | 与04:05一样;AM不影响数值 |
04:05 PM | 与16:05一样;输入小时数必须 <= 12 |
04:05:06.789-8 | ISO 8601 |
04:05:06-08:00 | ISO 8601 |
04:05-08:00 | ISO 8601 |
040506-08 | ISO 8601 |
3). 时间戳:
时间戳类型的有效输入由一个日期和时间的联接组成,后面跟着一个可选的时区。因此,1999-01-08 04:05:06和1999-01-08 04:05:06 -8:00都是有效的数值。
2. 示例:
1). 在插入数据之前先查看datestyle系统变量的值:
MyTest=> show datestyle;
DateStyle
-----------
ISO, YMD
(1 row)
2). 创建包含日期、时间和时间戳类型的示例表:
MyTest=> CREATE TABLE testtable (id integer, date_col date, time_col time, timestamp_col timestamp);
CREATE TABLE
3). 插入数据:
MyTest=> INSERT INTO testtable(id,date_col) VALUES(1, DATE'01/02/03'); --datestyle为YMD
INSERT 0 1
MyTest=> SELECT id, date_col FROM testtable;
id | date_col
----+------------
1 | 2001-02-03
(1 row)
MyTest=> set datestyle = MDY;
SET
MyTest=> INSERT INTO testtable(id,date_col) VALUES(2, DATE'01/02/03'); --datestyle为MDY
INSERT 0 1
MyTest=> SELECT id,date_col FROM testtable;
id | date_col
----+------------
1 | 2001-02-03
2 | 2003-01-02
MyTest=> INSERT INTO testtable(id,time_col) VALUES(3, TIME'10:20:00'); --插入时间。
INSERT 0 1
MyTest=> SELECT id,time_col FROM testtable WHERE time_col IS NOT NULL;
id | time_col
----+----------
3 | 10:20:00
(1 row)
MyTest=> INSERT INTO testtable(id,timestamp_col) VALUES(4, DATE'01/02/03');
INSERT 0 1
MyTest=> INSERT INTO testtable(id,timestamp_col) VALUES(5, TIMESTAMP'01/02/03 10:20:00');
INSERT 0 1
MyTest=> SELECT id,timestamp_col FROM testtable WHERE timestamp_col IS NOT NULL;
id | timestamp_col
----+---------------------
4 | 2003-01-02 00:00:00
5 | 2003-01-02 10:20:00
(2 rows)
四、布尔类型:
PostgreSQL支持标准的SQL boolean数据类型。boolean只能有两个状态之一:真(True)或 假(False)。该类型占用1个字节。
"真"值的有效文本值是:
TRUE
't'
'true'
'y'
'yes'
'1'
而对于"假"而言,你可以使用下面这些:
FALSE
'f'
'false'
'n'
'no'
'0'
见如下使用方式:
MyTest=> CREATE TABLE testtable (a boolean, b text);
CREATE TABLE
MyTest=> INSERT INTO testtable VALUES(TRUE, 'sic est');
INSERT 0 1
MyTest=> INSERT INTO testtable VALUES(FALSE, 'non est');
INSERT 0 1
MyTest=> SELECT * FROM testtable;
a | b
---+---------
t | sic est
f | non est
(2 rows)
MyTest=> SELECT * FROM testtable WHERE a;
a | b
---+---------
t | sic est
(1 row)
MyTest=> SELECT * FROM testtable WHERE a = true;
a | b
---+---------
t | sic est
(1 row)
五、位串类型:
位串就是一串1和0的字串。它们可以用于存储和视觉化位掩码。我们有两种类型的SQL位类型:bit(n)和bit varying(n); 这里的n是一个正整数。bit类型的数据必须准确匹配长度n; 试图存储短些或者长一些的数据都是错误的。类型bit varying数据是最长n的变长类型;更长的串会被拒绝。写一个没有长度的bit等效于bit(1),没有长度的bit varying相当于没有长度限制。
针对该类型,最后需要提醒的是,如果我们明确地把一个位串值转换成bit(n),那么它的右边将被截断或者在右边补齐零,直到刚好n位,而不会抛出任何错误。类似地,如果我们明确地把一个位串数值转换成bit varying(n),如果它超过n位,那么它的右边将被截断。 见如下具体使用方式:
MyTest=> CREATE TABLE testtable (a bit(3), b bit varying(5));
CREATE TABLE
MyTest=> INSERT INTO testtable VALUES (B'101', B'00');
INSERT 0 1
MyTest=> INSERT INTO testtable VALUES (B'10', B'101');
ERROR: bit string length 2 does not match type bit(3)
MyTest=> INSERT INTO testtable VALUES (B'10'::bit(3), B'101');
INSERT 0 1
MyTest=> SELECT * FROM testtable;
a | b
-----+-----
101 | 00
100 | 101
(2 rows)
MyTest=> SELECT B'11'::bit(3);
bit
-----
110
(1 row)
六、数组:
1. 数组类型声明:
1). 创建字段含有数组类型的表。
CREATE TABLE sal_emp (
name text,
pay_by_quarter integer[] --还可以定义为integer[4]或integer ARRAY[4]
);
2). 插入数组数据:
MyTest=# INSERT INTO sal_emp VALUES ('Bill', '{11000, 12000, 13000, 14000}');
INSERT 0 1
MyTest=# INSERT INTO sal_emp VALUES ('Carol', ARRAY[21000, 22000, 23000, 24000]);
INSERT 0 1
MyTest=# SELECT * FROM sal_emp;
name | pay_by_quarter
--------+---------------------------
Bill | {11000,12000,13000,14000}
Carol | {21000,22000,23000,24000}
(2 rows)
2. 访问数组:
和其他语言一样,PostgreSQL中数组也是通过下标数字(写在方括弧内)的方式进行访问,只是PostgreSQL中数组元素的下标是从1开始n结束。
MyTest=# SELECT pay_by_quarter[3] FROM sal_emp;
pay_by_quarter
----------------
13000
23000
(2 rows)
MyTest=# SELECT name FROM sal_emp WHERE pay_by_quarter[1] <> pay_by_quarter[2];
name
------
Bill
Carol
(2 rows)
PostgreSQL中还提供了访问数组范围的功能,即ARRAY[脚标下界:脚标上界]。
MyTest=# SELECT name,pay_by_quarter[1:3] FROM sal_emp;
name | pay_by_quarter
--------+---------------------
Bill | {11000,12000,13000}
Carol | {21000,22000,23000}
(2 rows)
3. 修改数组:
1). 代替全部数组值:
--UPDATE sal_emp SET pay_by_quarter = ARRAY[25000,25000,27000,27000] WHERE name = 'Carol'; 也可以。
MyTest=# UPDATE sal_emp SET pay_by_quarter = '{31000,32000,33000,34000}' WHERE name = 'Carol';
UPDATE 1
MyTest=# SELECT * FROM sal_emp;
name | pay_by_quarter
--------+---------------------------
Bill | {11000,12000,13000,14000}
Carol | {31000,32000,33000,34000}
(2 rows)
2). 更新数组中某一元素:
MyTest=# UPDATE sal_emp SET pay_by_quarter[4] = 15000 WHERE name = 'Bill';
UPDATE 1
MyTest=# SELECT * FROM sal_emp;
name | pay_by_quarter
--------+---------------------------
Carol | {31000,32000,33000,34000}
Bill | {11000,12000,13000,15000}
(2 rows)
3). 更新数组某一范围的元素:
MyTest=# UPDATE sal_emp SET pay_by_quarter[1:2] = '{37000,37000}' WHERE name = 'Carol';
UPDATE 1
MyTest=# SELECT * FROM sal_emp;
name | pay_by_quarter
--------+---------------------------
Bill | {11000,12000,13000,15000}
Carol | {37000,37000,33000,34000}
(2 rows)
4). 直接赋值扩大数组:
MyTest=# UPDATE sal_emp SET pay_by_quarter[5] = 45000 WHERE name = 'Bill';
UPDATE 1
MyTest=# SELECT * FROM sal_emp;
name | pay_by_quarter
--------+---------------------------------
Carol | {37000,37000,33000,34000}
Bill | {11000,12000,13000,15000,45000}
(2 rows)
4. 在数组中检索:
1). 最简单直接的方法:
SELECT * FROM sal_emp WHERE pay_by_quarter[1] = 10000 OR
pay_by_quarter[2] = 10000 OR
pay_by_quarter[3] = 10000 OR
pay_by_quarter[4] = 10000;
2). 更加有效的方法:
SELECT * FROM sal_emp WHERE 10000 = ANY (pay_by_quarter); --数组元素中有任何一个等于10000,where条件将成立。
SELECT * FROM sal_emp WHERE 10000 = ALL (pay_by_quarter); --只有当数组中所有的元素都等于10000时,where条件才成立。
七、复合类型:
PostgreSQL中复合类型有些类似于C语言中的结构体,也可以被视为Oracle中的记录类型,但是还是感觉复合类型这个命名比较贴切。它实际上只是一个字段名和它们的数据类型的列表。PostgreSQL允许像简单数据类型那样使用复合类型。比如,表字段可以声明为一个复合类型。
1. 声明复合类型:
下面是两个简单的声明示例:
CREATE TYPE complex AS (
r double,
i double
);
CREATE TYPE inventory_item AS (
name text,
supplier_id integer,
price numeric
);
和声明一个数据表相比,声明类型时需要加AS关键字,同时在声明TYPE时不能定义任何约束。下面我们看一下如何在表中指定复合类型的字段,如:
CREATE TABLE on_hand (
item inventory_item,
count integer
);
最后需要指出的是,在创建表的时候,PostgreSQL也会自动创建一个与该表对应的复合类型,名字与表字相同,即表示该表的复合类型。
2. 复合类型值输入:
我们可以使用文本常量的方式表示复合类型值,即在圆括号里包围字段值并且用逗号分隔它们。你也可以将任何字段值用双引号括起,如果值本身包含逗号或者圆括号,那么就用双引号括起,对于上面的inventory_item复合类型的输入如下:
'("fuzzy dice",42,1.99)'
如果希望类型中的某个字段为NULL,只需在其对应的位置不予输入即可,如下面的输入中price字段的值为NULL,
'("fuzzy dice",42,)'
如果只是需要一个空字串,而非NULL,写一对双引号,如:
'("",42,)'
在更多的场合中PostgreSQL推荐使用ROW表达式来构建复合类型值,使用该种方式相对简单,无需考虑更多标识字符问题,如:
ROW('fuzzy dice', 42, 1.99)
ROW('', 42, NULL)
注:对于ROW表达式,如果里面的字段数量超过1个,那么关键字ROW就可以省略,因此以上形式可以简化为:
('fuzzy dice', 42, 1.99)
('', 42, NULL)
3. 访问复合类型:
访问复合类型中的字段和访问数据表中的字段在形式上极为相似,只是为了对二者加以区分,PostgreSQL设定在访问复合类型中的字段时,类型部分需要用圆括号括起,以避免混淆,如:
SELECT (item).name FROM on_hand WHERE (item).price > 9.99;
如果在查询中也需要用到表名,那么表名和类型名都需要被圆括号括起,如:
SELECT (on_hand.item).name FROM on_hand WHERE (on_hand.item).price > 9.99;
4. 修改复合类型:
见如下几个示例:
--直接插入复合类型的数据,这里是通过ROW表达式来完成的。
INSERT INTO on_hand(item) VALUES(ROW("fuzzy dice",42,1.99));
--在更新操作中,也是可以通过ROW表达式来完成。
UPDATE on_hand SET item = ROW("fuzzy dice",42,1.99) WHERE count = 0;
--在更新复合类型中的一个字段时,我们不能在SET后面出现的字段名周围加圆括号,
--但是在等号右边的表达式里引用同一个字段时却需要圆括号。
UPDATE on_hand SET item.price = (item).price + 1 WHERE count = 0;
--可以在插入中,直接插入复合类型中字段。
INSERT INTO on_hand (item.supplier_id, item.price) VALUES(100, 2.2);
一、逻辑操作符:
常用的逻辑操作符有:AND、OR和NOT。其语义与其它编程语言中的逻辑操作符完全相同。
二、比较操作符:
下面是PostgreSQL中提供的比较操作符列表:
操作符 | 描述 |
< | 小于 |
> | 大于 |
<= | 小于或等于 |
>= | 大于或等于 |
= | 等于 |
!= | 不等于 |
比较操作符可以用于所有可以比较的数据类型。所有比较操作符都是双目操作符,且返回boolean类型。除了比较操作符以外,我们还可以使用BETWEEN语句,如:
a BETWEEN x AND y 等效于 a >= x AND a <= y
a NOT BETWEEN x AND y 等效于 a < x OR a > y
三、 数学函数和操作符:
下面是PostgreSQL中提供的数学操作符列表:
操作符 | 描述 | 例子 | 结果 |
+ | 加 | 2 + 3 | 5 |
- | 减 | 2 - 3 | -1 |
* | 乘 | 2 * 3 | 6 |
/ | 除 | 4 / 2 | 2 |
% | 模 | 5 % 4 | 1 |
^ | 幂 | 2.0 ^ 3.0 | 8 |
|/ | 平方根 | |/ 25.0 | 5 |
||/ | 立方根 | ||/ 27.0 | 3 |
! | 阶乘 | 5 ! | 120 |
!! | 阶乘 | !! 5 | 120 |
@ | 绝对值 | @ -5.0 | 5 |
& | 按位AND | 91 & 15 | 11 |
| | 按位OR | 32 | 3 | 35 |
# | 按位XOR | 17 # 5 | 20 |
~ | 按位NOT | ~1 | -2 |
<< | 按位左移 | 1 << 4 | 16 |
>> | 按位右移 | 8 >> 2 | 2 |
按位操作符只能用于整数类型,而其它的操作符可以用于全部数值数据类型。按位操作符还可以用于位串类型bit和bit varying,
下面是PostgreSQL中提供的数学函数列表,需要说明的是,这些函数中有许多都存在多种形式,区别只是参数类型不同。除非特别指明,任何特定形式的函数都返回和它的参数相同的数据类型。
函数 | 返回类型 |
描述 | 例子 |
结果 |
abs(x) | 绝对值 | abs(-17.4) | 17.4 | |
cbrt(double) | 立方根 | cbrt(27.0) | 3 | |
ceil(double/numeric) | 不小于参数的最小的整数 | ceil(-42.8) | -42 | |
degrees(double) |
把弧度转为角度 | degrees(0.5) | 28.6478897565412 | |
exp(double/numeric) | 自然指数 | exp(1.0) | 2.71828182845905 | |
floor(double/numeric) | 不大于参数的最大整数 | floor(-42.8) | -43 | |
ln(double/numeric) | 自然对数 | ln(2.0) | 0.693147180559945 | |
log(double/numeric) | 10为底的对数 | log(100.0) | 2 | |
log(b numeric,x numeric) | numeric指定底数的对数 | log(2.0, 64.0) | 6.0000000000 | |
mod(y, x) | 取余数 | mod(9,4) | 1 | |
pi() |
double | "π"常量 | pi() |
3.14159265358979 |
power(a double, b double) | double | 求a的b次幂 | power(9.0, 3.0) | 729 |
power(a numeric, b numeric) | numeric | 求a的b次幂 | power(9.0, 3.0) | 729 |
radians(double) | double | 把角度转为弧度 | radians(45.0) | 0.785398163397448 |
random() | double | 0.0到1.0之间的随机数值 | random() | |
round(double/numeric) | 圆整为最接近的整数 | round(42.4) | 42 | |
round(v numeric, s int) | numeric | 圆整为s位小数数字 | round(42.438,2) | 42.44 |
sign(double/numeric) | 参数的符号(-1,0,+1) |
sign(-8.4) | -1 | |
sqrt(double/numeric) | 平方根 | sqrt(2.0) | 1.4142135623731 | |
trunc(double/numeric) | 截断(向零靠近) | trunc(42.8) | 42 | |
trunc(v numeric, s int) | numeric | 截断为s小数位置的数字 | trunc(42.438,2) | 42.43 |
三角函数列表:
函数 | 描述 |
acos(x) | 反余弦 |
asin(x) | 反正弦 |
atan(x) | 反正切 |
atan2(x, y) | 正切 y/x 的反函数 |
cos(x) | 余弦 |
cot(x) | 余切 |
sin(x) | 正弦 |
tan(x) | 正切 |
四、字符串函数和操作符:
下面是PostgreSQL中提供的字符串操作符列表:
函数 | 返回类型 | 描述 | 例子 | 结果 |
string || string | text | 字串连接 | 'Post' || 'greSQL' | PostgreSQL |
bit_length(string) | int | 字串里二进制位的个数 | bit_length('jose') | 32 |
char_length(string) | int | 字串中的字符个数 | char_length('jose') | 4 |
convert(string using conversion_name) | text | 使用指定的转换名字改变编码。 | convert('PostgreSQL' using iso_8859_1_to_utf8) | 'PostgreSQL' |
lower(string) | text | 把字串转化为小写 | lower('TOM') | tom |
octet_length(string) | int | 字串中的字节数 | octet_length('jose') | 4 |
overlay(string placing string from int [for int]) | text | 替换子字串 | overlay('Txxxxas' placing 'hom' from 2 for 4) | Thomas |
position(substring in string) | int | 指定的子字串的位置 | position('om' in 'Thomas') | 3 |
substring(string [from int] [for int]) | text | 抽取子字串 | substring('Thomas' from 2 for 3) | hom |
substring(string from pattern) | text | 抽取匹配 POSIX 正则表达式的子字串 | substring('Thomas' from '...$') | mas |
substring(string from pattern for escape) | text | 抽取匹配SQL正则表达式的子字串 | substring('Thomas' from '%#"o_a#"_' for '#') | oma |
trim([leading | trailing | both] [characters] from string) | text | 从字串string的开头/结尾/两边/ 删除只包含characters(缺省是一个空白)的最长的字串 | trim(both 'x' from 'xTomxx') | Tom |
upper(string) | text | 把字串转化为大写。 | upper('tom') | TOM |
ascii(text) | int | 参数第一个字符的ASCII码 | ascii('x') | 120 |
btrim(string text [, characters text]) | text | 从string开头和结尾删除只包含在characters里(缺省是空白)的字符的最长字串 | btrim('xyxtrimyyx','xy') | trim |
chr(int) | text | 给出ASCII码的字符 | chr(65) | A |
convert(string text, [src_encoding name,] dest_encoding name) | text | 把字串转换为dest_encoding | convert( 'text_in_utf8', 'UTF8', 'LATIN1') | 以ISO 8859-1编码表示的text_in_utf8 |
initcap(text) | text | 把每个单词的第一个子母转为大写,其它的保留小写。单词是一系列字母数字组成的字符,用非字母数字分隔。 | initcap('hi thomas') | Hi Thomas |
length(string text) | int | string中字符的数目 | length('jose') | 4 |
lpad(string text, length int [, fill text]) | text | 通过填充字符fill(缺省时为空白),把string填充为长度length。 如果string已经比length长则将其截断(在右边)。 | lpad('hi', 5, 'xy') | xyxhi |
ltrim(string text [, characters text]) | text | 从字串string的开头删除只包含characters(缺省是一个空白)的最长的字串。 | ltrim('zzzytrim','xyz') | trim |
md5(string text) | text | 计算给出string的MD5散列,以十六进制返回结果。 | md5('abc') | |
repeat(string text, number int) | text | 重复string number次。 | repeat('Pg', 4) | PgPgPgPg |
replace(string text, from text, to text) | text | 把字串string里出现地所有子字串from替换成子字串to。 | replace('abcdefabcdef', 'cd', 'XX') | abXXefabXXef |
rpad(string text, length int [, fill text]) | text | 通过填充字符fill(缺省时为空白),把string填充为长度length。如果string已经比length长则将其截断。 | rpad('hi', 5, 'xy') | hixyx |
rtrim(string text [, character text]) | text | 从字串string的结尾删除只包含character(缺省是个空白)的最长的字 | rtrim('trimxxxx','x') | trim |
split_part(string text, delimiter text, field int) | text | 根据delimiter分隔string返回生成的第field个子字串(1 Base)。 | split_part('abc~@~def~@~ghi', '~@~', 2) | def |
strpos(string, substring) | text | 声明的子字串的位置。 | strpos('high','ig') | 2 |
substr(string, from [, count]) | text | 抽取子字串。 | substr('alphabet', 3, 2) | ph |
to_ascii(text [, encoding]) | text | 把text从其它编码转换为ASCII。 | to_ascii('Karel') | Karel |
to_hex(number int/bigint) | text | 把number转换成其对应地十六进制表现形式。 | to_hex(9223372036854775807) | 7fffffffffffffff |
translate(string text, from text, to text) | text | 把在string中包含的任何匹配from中的字符的字符转化为对应的在to中的字符。 | translate('12345', '14', 'ax') | a23x5 |
五、位串函数和操作符:
对于类型bit和bit varying,除了常用的比较操作符之外,还可以使用以下列表中由PostgreSQL提供的位串函数和操作符,其中&、|和#的位串操作数必须等长。在移位的时候,保留原始的位串的的长度。
操作符 | 描述 | 例子 | 结果 |
|| | 连接 | B'10001' || B'011' | 10001011 |
& | 按位AND | B'10001' & B'01101' | 00001 |
| | 按位OR | B'10001' | B'01101' | 11101 |
# | 按位XOR | B'10001' # B'01101' | 11100 |
~ | 按位NOT | ~ B'10001' | 01110 |
<< | 按位左移 | B'10001' << 3 | 01000 |
>> | 按位右移 | B'10001' >> 2 | 00100 |
除了以上列表中提及的操作符之外,位串还可以使用字符串函数:length, bit_length, octet_length, position, substring。此外,我们还可以在整数和bit之间来回转换,如:
MyTest=# SELECT 44::bit(10);
bit
------------
0000101100
(1 row)
MyTest=# SELECT 44::bit(3);
bit
-----
100
(1 row)
MyTest=# SELECT cast(-44 as bit(12));
bit
--------------
111111010100
(1 row)
MyTest=# SELECT '1110'::bit(4)::integer;
int4
------
14
(1 row)
注意:如果只是转换为"bit",意思是转换成bit(1),因此只会转换成整数的最低位。
六、模式匹配:
PostgreSQL中提供了三种实现模式匹配的方法:SQL LIKE操作符,更近一些的SIMILAR TO操作符,和POSIX-风格正则表达式。
1. LIKE:
string LIKE pattern [ ESCAPE escape-character ]
string NOT LIKE pattern [ ESCAPE escape-character ]
每个pattern定义一个字串的集合。如果该string包含在pattern代表的字串集合里,那么LIKE表达式返回真。和我们想象的一样,如果LIKE返回真,那么NOT LIKE表达式返回假,反之亦然。在pattern里的下划线(_)代表匹配任何单个字符,而一个百分号(%)匹配任何零或更多字符,如:
'abc' LIKE 'abc' true
'abc' LIKE 'a%' true
'abc' LIKE '_b_' true
'abc' LIKE 'c' false
要匹配文本的下划线或者百分号,而不是匹配其它字符,在pattern里相应的字符必须前导转义字符。缺省的转义字符是反斜杠,但是你可以用ESCAPE子句指定一个。要匹配转义字符本身,写两个转义字符。我们也可以通过写成ESCAPE ''的方式有效地关闭转义机制,此时,我们就不能关闭下划线和百分号的特殊含义了。
关键字ILIKE可以用于替换LIKE,令该匹配就当前的区域设置是大小写无关的。这个特性不是SQL标准,是PostgreSQL的扩展。操作符~~等效于LIKE, 而~~*对应ILIKE。还有!~~和!~~*操作符分别代表NOT LIKE和NOT ILIKE。所有这些操作符都是PostgreSQL特有的。
2. SIMILAR TO正则表达式:
SIMILAR TO根据模式是否匹配给定的字符串而返回真或者假。
string SIMILAR TO pattern [ESCAPE escape-character]
string NOT SIMILAR TO pattern [ESCAPE escape-character]
它和LIKE非常类似,支持LIKE的通配符('_'和'%')且保持其原意。除此之外,SIMILAR TO还支持一些自己独有的元字符,如:
1). | 标识选择(两个候选之一)。
2). * 表示重复前面的项零次或更多次。
3). + 表示重复前面的项一次或更多次。
4). 可以使用圆括弧()把项组合成一个逻辑项。
5). 一个方括弧表达式[...]声明一个字符表,就像POSIX正则表达式一样。
见如下示例:
'abc' SIMILAR TO 'abc' true
'abc' SIMILAR TO 'a' false
'abc' SIMILAR TO '%(b|d)%' true
'abc' SIMILAR TO '(b|c)%' false
带三个参数的substring,substring(string from pattern for escape-character),提供了一个从字串中抽取一个匹配SQL正则表达式模式的子字串的函数。和SIMILAR TO一样,声明的模式必须匹配整个数据串,否则函数失效并返回NULL。为了标识在成功的时候应该返回的模式部分,模式必须出现后跟双引号(")的两个转义字符。匹配这两个标记之间的模式的字串将被返回,如:
MyTest=# SELECT substring('foobar' from '%#"o_b#"%' FOR '#'); --这里#是转义符,双引号内的模式是返回部分。
substring
-----------
oob
(1 row)
MyTest=# SELECT substring('foobar' from '#"o_b#"%' FOR '#'); --foobar不能完全匹配后面的模式,因此返回NULL。
substring
-----------
(1 row)
七、数据类型格式化函数:
PostgreSQL格式化函数提供一套有效的工具用于把各种数据类型(日期/时间、integer、floating point和numeric)转换成格式化的字符串以及反过来从格式化的字符串转换成指定的数据类型。下面列出了这些函数,它们都遵循一个公共的调用习惯:第一个参数是待格式化的值,而第二个是定义输出或输出格式的模板。
函数 | 返回类型 | 描述 | 例子 |
to_char(timestamp, text) | text | 把时间戳转换成字串 | to_char(current_timestamp, 'HH12:MI:SS') |
to_char(interval, text) | text | 把时间间隔转为字串 | to_char(interval '15h 2m 12s', 'HH24:MI:SS') |
to_char(int, text) | text | 把整数转换成字串 | to_char(125, '999') |
to_char(double precision, text) | text | 把实数/双精度数转换成字串 | to_char(125.8::real, '999D9') |
to_char(numeric, text) | text | 把numeric转换成字串 | to_char(-125.8, '999D99S') |
to_date(text, text) | date | 把字串转换成日期 | to_date('05 Dec 2000', 'DD Mon YYYY') |
to_timestamp(text, text) | timestamp | 把字串转换成时间戳 | to_timestamp('05 Dec 2000', 'DD Mon YYYY') |
to_timestamp(double) | timestamp | 把UNIX纪元转换成时间戳 | to_timestamp(200120400) |
to_number(text, text) | numeric | 把字串转换成numeric | to_number('12,454.8-', '99G999D9S') |
1. 用于日期/时间格式化的模式:
模式 | 描述 |
HH | 一天的小时数(01-12) |
HH12 | 一天的小时数(01-12) |
HH24 | 一天的小时数(00-23) |
MI | 分钟(00-59) |
SS | 秒(00-59) |
MS | 毫秒(000-999) |
US | 微秒(000000-999999) |
AM | 正午标识(大写) |
Y,YYY | 带逗号的年(4和更多位) |
YYYY | 年(4和更多位) |
YYY | 年的后三位 |
YY | 年的后两位 |
Y | 年的最后一位 |
MONTH | 全长大写月份名(空白填充为9字符) |
Month | 全长混合大小写月份名(空白填充为9字符) |
month | 全长小写月份名(空白填充为9字符) |
MON | 大写缩写月份名(3字符) |
Mon | 缩写混合大小写月份名(3字符) |
mon | 小写缩写月份名(3字符) |
MM | 月份号(01-12) |
DAY | 全长大写日期名(空白填充为9字符) |
Day | 全长混合大小写日期名(空白填充为9字符) |
day | 全长小写日期名(空白填充为9字符) |
DY | 缩写大写日期名(3字符) |
Dy | 缩写混合大小写日期名(3字符) |
dy | 缩写小写日期名(3字符) |
DDD | 一年里的日子(001-366) |
DD | 一个月里的日子(01-31) |
D | 一周里的日子(1-7;周日是1) |
W | 一个月里的周数(1-5)(第一周从该月第一天开始) |
WW | 一年里的周数(1-53)(第一周从该年的第一天开始) |
2. 用于数值格式化的模板模式:
模式 | 描述 |
9 | 带有指定数值位数的值 |
0 | 带前导零的值 |
.(句点) | 小数点 |
,(逗号) | 分组(千)分隔符 |
PR | 尖括号内负值 |
S | 带符号的数值 |
L | 货币符号 |
D | 小数点 |
G | 分组分隔符 |
MI | 在指明的位置的负号(如果数字 < 0) |
PL | 在指明的位置的正号(如果数字 > 0) |
SG | 在指明的位置的正/负号 |
八、时间/日期函数和操作符:
1. 下面是PostgreSQL中支持的时间/日期操作符的列表:
操作符 | 例子 | 结果 |
+ | date '2001-09-28' + integer '7' | date '2001-10-05' |
+ | date '2001-09-28' + interval '1 hour' | timestamp '2001-09-28 01:00' |
+ | date '2001-09-28' + time '03:00' | timestamp '2001-09-28 03:00' |
+ | interval '1 day' + interval '1 hour' | interval '1 day 01:00' |
+ | timestamp '2001-09-28 01:00' + interval '23 hours' | timestamp '2001-09-29 00:00' |
+ | time '01:00' + interval '3 hours' | time '04:00' |
- | - interval '23 hours' | interval '-23:00' |
- | date '2001-10-01' - date '2001-09-28' | integer '3' |
- | date '2001-10-01' - integer '7' | date '2001-09-24' |
- | date '2001-09-28' - interval '1 hour' | timestamp '2001-09-27 23:00' |
- | time '05:00' - time '03:00' | interval '02:00' |
- | time '05:00' - interval '2 hours' | time '03:00' |
- | timestamp '2001-09-28 23:00' - interval '23 hours' | timestamp '2001-09-28 00:00' |
- | interval '1 day' - interval '1 hour' | interval '23:00' |
- | timestamp '2001-09-29 03:00' - timestamp '2001-09-27 12:00' | interval '1 day 15:00' |
* | interval '1 hour' * double precision '3.5' | interval '03:30' |
/ | interval '1 hour' / double precision '1.5' | interval '00:40' |
2. 日期/时间函数:
函数 | 返回类型 | 描述 | 例子 | 结果 |
age(timestamp, timestamp) | interval | 减去参数,生成一个使用年、月的"符号化"的结果 | age('2001-04-10', timestamp '1957-06-13') | 43 years 9 mons 27 days |
age(timestamp) | interval | 从current_date减去得到的数值 | age(timestamp '1957-06-13') | 43 years 8 mons 3 days |
current_date | date | 今天的日期 | ||
current_time | time | 现在的时间 | ||
current_timestamp | timestamp | 日期和时间 | ||
date_part(text, timestamp) | double | 获取子域(等效于extract) | date_part('hour', timestamp '2001-02-16 20:38:40') | 20 |
date_part(text, interval) | double | 获取子域(等效于extract) | date_part('month', interval '2 years 3 months') | 3 |
date_trunc(text, timestamp) | timestamp | 截断成指定的精度 | date_trunc('hour', timestamp '2001-02-16 20:38:40') | 2001-02-16 20:00:00+00 |
extract(field from timestamp) | double | 获取子域 | extract(hour from timestamp '2001-02-16 20:38:40') | 20 |
extract(field from interval) | double | 获取子域 | extract(month from interval '2 years 3 months') | 3 |
localtime | time | 今日的时间 | ||
localtimestamp | timestamp | 日期和时间 | ||
now() | timestamp | 当前的日期和时间(等效于 current_timestamp) | ||
timeofday() | text | 当前日期和时间 |
3. EXTRACT,date_part函数支持的field:
域 | 描述 | 例子 | 结果 |
CENTURY | 世纪 | EXTRACT(CENTURY FROM TIMESTAMP '2000-12-16 12:21:13'); | 20 |
DAY | (月分)里的日期域(1-31) | EXTRACT(DAY from TIMESTAMP '2001-02-16 20:38:40'); | 16 |
DECADE | 年份域除以10 | EXTRACT(DECADE from TIMESTAMP '2001-02-16 20:38:40'); | 200 |
DOW | 每周的星期号(0-6;星期天是0) (仅用于timestamp) | EXTRACT(DOW FROM TIMESTAMP '2001-02-16 20:38:40'); | 5 |
DOY | 一年的第几天(1 -365/366) (仅用于 timestamp) | EXTRACT(DOY from TIMESTAMP '2001-02-16 20:38:40'); | 47 |
HOUR | 小时域(0-23) | EXTRACT(HOUR from TIMESTAMP '2001-02-16 20:38:40'); | 20 |
MICROSECONDS | 秒域,包括小数部分,乘以 1,000,000。 | EXTRACT(MICROSECONDS from TIME '17:12:28.5'); | 28500000 |
MILLENNIUM | 千年 | EXTRACT(MILLENNIUM from TIMESTAMP '2001-02-16 20:38:40'); | 3 |
MILLISECONDS | 秒域,包括小数部分,乘以 1000。 | EXTRACT(MILLISECONDS from TIME '17:12:28.5'); | 28500 |
MINUTE | 分钟域(0-59) | EXTRACT(MINUTE from TIMESTAMP '2001-02-16 20:38:40'); | 38 |
MONTH | 对于timestamp数值,它是一年里的月份数(1-12);对于interval数值,它是月的数目,然后对12取模(0-11) | EXTRACT(MONTH from TIMESTAMP '2001-02-16 20:38:40'); | 2 |
QUARTER | 该天所在的该年的季度(1-4)(仅用于 timestamp) | EXTRACT(QUARTER from TIMESTAMP '2001-02-16 20:38:40'); | 1 |
SECOND | 秒域,包括小数部分(0-59[1]) | EXTRACT(SECOND from TIMESTAMP '2001-02-16 20:38:40'); | 40 |
WEEK | 该天在所在的年份里是第几周。 | EXTRACT(WEEK from TIMESTAMP '2001-02-16 20:38:40'); | 7 |
YEAR | 年份域 | EXTRACT(YEAR from TIMESTAMP '2001-02-16 20:38:40'); | 2001 |
4. 当前日期/时间:
我们可以使用下面的函数获取当前的日期和/或时间∶
CURRENT_DATE
CURRENT_TIME
CURRENT_TIMESTAMP
CURRENT_TIME (precision)
CURRENT_TIMESTAMP (precision)
LOCALTIME
LOCALTIMESTAMP
LOCALTIME (precision)
LOCALTIMESTAMP (precision)
九、序列操作函数:
序列对象(也叫序列生成器)都是用CREATE SEQUENCE创建的特殊的单行表。一个序列对象通常用于为行或者表生成唯一的标识符。下面序列函数,为我们从序列对象中获取最新的序列值提供了简单和并发读取安全的方法。
函数 | 返回类型 | 描述 |
nextval(regclass) | bigint | 递增序列对象到它的下一个数值并且返回该值。这个动作是自动完成的。即使多个会话并发运行nextval,每个进程也会安全地收到一个唯一的序列值。 |
currval(regclass) | bigint | 在当前会话中返回最近一次nextval抓到的该序列的数值。(如果在本会话中从未在该序列上调用过 nextval,那么会报告一个错误。)请注意因为此函数返回一个会话范围的数值,而且也能给出一个可预计的结果,因此可以用于判断其它会话是否执行过nextval。 |
lastval() | bigint | 返回当前会话里最近一次nextval返回的数值。这个函数等效于currval,只是它不用序列名为参数,它抓取当前会话里面最近一次nextval使用的序列。如果当前会话还没有调用过nextval,那么调用lastval将会报错。 |
setval(regclass, bigint) | bigint | 重置序列对象的计数器数值。设置序列的last_value字段为指定数值并且将其is_called字段设置为true,表示下一次nextval将在返回数值之前递增该序列。 |
setval(regclass, bigint, boolean) | bigint | 重置序列对象的计数器数值。功能等同于上面的setval函数,只是is_called可以设置为true或false。如果将其设置为false,那么下一次nextval将返回该数值,随后的nextval才开始递增该序列。 |
对于regclass参数,仅需用单引号括住序列名即可,因此它看上去就像文本常量。为了达到和处理普通SQL对象一样的兼容性,这个字串将被转换成小写,除非该序列名是用双引号括起,如:
nextval('foo') --操作序列号foo
nextval('FOO') --操作序列号foo
nextval('"Foo"') --操作序列号Foo
SELECT setval('foo', 42); --下次nextval将返回43
SELECT setval('foo', 42, true);
SELECT setval('foo', 42, false); --下次nextval将返回42
十、条件表达式:
1. CASE:
SQL CASE表达式是一种通用的条件表达式,类似于其它语言中的if/else语句。
CASE WHEN condition THEN result
[WHEN ...]
[ELSE result]
END
condition是一个返回boolean的表达式。如果为真,那么CASE表达式的结果就是符合条件的result。如果结果为假,那么以相同方式搜寻随后的WHEN子句。如果没有WHEN condition为真,那么case表达式的结果就是在ELSE子句里的值。如果省略了ELSE子句而且没有匹配的条件,结果为NULL,如:
MyTest=> SELECT * FROM testtable;
i
---
1
2
3
(3 rows)
MyTest=> SELECT i, CASE WHEN i=1 THEN 'one'
MyTest-> WHEN i=2 THEN 'two'
MyTest-> ELSE 'other'
MyTest-> END
MyTest-> FROM testtable;
i | case
---+-------
1 | one
2 | two
3 | other
(3 rows)
注:CASE表达式并不计算任何对于判断结果并不需要的子表达式。
2. COALESCE:
COALESCE返回它的第一个非NULL的参数的值。它常用于在为显示目的检索数据时用缺省值替换NULL值。
COALESCE(value[, ...])
和CASE表达式一样,COALESCE将不会计算不需要用来判断结果的参数。也就是说,在第一个非空参数右边的参数不会被计算。
3. NULLIF:
当且仅当value1和value2相等时,NULLIF才返回NULL。否则它返回value1。
NULLIF(value1, value2)
MyTest=> SELECT NULLIF('abc','abc');
nullif
--------
(1 row)
MyTest=> SELECT NULLIF('abcd','abc');
nullif
--------
abcd
(1 row)
4. GREATEST和LEAST:
GREATEST和LEAST函数从一个任意的数字表达式列表里选取最大或者最小的数值。列表中的NULL数值将被忽略。只有所有表达式的结果都是NULL的时候,结果才会是NULL。
GREATEST(value [, ...])
LEAST(value [, ...])
MyTest=> SELECT GREATEST(1,3,5);
greatest
----------
5
(1 row)
MyTest=> SELECT LEAST(1,3,5,NULL);
least
-------
1
(1 row)
十一、数组函数和操作符:
1. PostgreSQL中提供的用于数组的操作符列表:
操作符 | 描述 | 例子 | 结果 |
= | 等于 | ARRAY[1.1,2.1,3.1]::int[] = ARRAY[1,2,3] | t |
<> | 不等于 | ARRAY[1,2,3] <> ARRAY[1,2,4] | t |
< | 小于 | ARRAY[1,2,3] < ARRAY[1,2,4] | t |
> | 大于 | ARRAY[1,4,3] > ARRAY[1,2,4] | t |
<= | 小于或等于 | ARRAY[1,2,3] <= ARRAY[1,2,3] | t |
>= | 大于或等于 | ARRAY[1,4,3] >= ARRAY[1,4,3] | t |
|| | 数组与数组连接 | ARRAY[1,2,3] || ARRAY[4,5,6] | {1,2,3,4,5,6} |
|| | 数组与数组连接 | ARRAY[1,2,3] || ARRAY[[4,5,6],[7,8,9]] | {{1,2,3},{4,5,6},{7,8,9}} |
|| | 元素与数组连接 | 3 || ARRAY[4,5,6] | {3,4,5,6} |
|| | 元素与数组连接 | ARRAY[4,5,6] || 7 | {4,5,6,7} |
2. PostgreSQL中提供的用于数组的函数列表:
函数 | 返回类型 | 描述 | 例子 | 结果 |
array_cat(anyarray, anyarray) | anyarray | 连接两个数组 | array_cat(ARRAY[1,2,3], ARRAY[4,5]) | {1,2,3,4,5} |
array_append(anyarray, anyelement) | anyarray | 向一个数组末尾附加一个元素 | array_append(ARRAY[1,2], 3) | {1,2,3} |
array_prepend(anyelement, anyarray) | anyarray | 向一个数组开头附加一个元素 | array_prepend(1, ARRAY[2,3]) | {1,2,3} |
array_dims(anyarray) | text | 返回一个数组维数的文本表示 | array_dims(ARRAY[[1,2,3], [4,5,6]]) | [1:2][1:3] |
array_lower(anyarray, int) | int | 返回指定的数组维数的下界 | array_lower(array_prepend(0, ARRAY[1,2,3]), 1) | 0 |
array_upper(anyarray, int) | int | 返回指定数组维数的上界 | array_upper(ARRAY[1,2,3,4], 1) | 4 |
array_to_string(anyarray, text) | text | 使用提供的分隔符连接数组元素 | array_to_string(ARRAY[1, 2, 3], '~^~') | 1~^~2~^~3 |
string_to_array(text, text) | text[] | 使用指定的分隔符把字串拆分成数组元素 | string_to_array('xx~^~yy~^~zz', '~^~') | {xx,yy,zz} |
十二、系统信息函数:
1. PostgreSQL中提供的和数据库相关的函数列表:
名字 | 返回类型 | 描述 |
current_database() | name | 当前数据库的名字 |
current_schema() | name | 当前模式的名字 |
current_schemas(boolean) | name[] | 在搜索路径中的模式名字 |
current_user | name | 目前执行环境下的用户名 |
inet_client_addr() | inet | 连接的远端地址 |
inet_client_port() | int | 连接的远端端口 |
inet_server_addr() | inet | 连接的本地地址 |
inet_server_port() | int | 连接的本地端口 |
session_user | name | 会话用户名 |
pg_postmaster_start_time() | timestamp | postmaster启动的时间 |
user | name | current_user |
version() | text | PostgreSQL版本信息 |
2. 允许用户在程序里查询对象访问权限的函数:
名字 | 描述 | 可用权限 |
has_table_privilege(user,table,privilege) | 用户是否有访问表的权限 | SELECT/INSERT/UPDATE/DELETE/RULE/REFERENCES/TRIGGER |
has_table_privilege(table,privilege) | 当前用户是否有访问表的权限 | SELECT/INSERT/UPDATE/DELETE/RULE/REFERENCES/TRIGGER |
has_database_privilege(user,database,privilege) | 用户是否有访问数据库的权限 | CREATE/TEMPORARY |
has_database_privilege(database,privilege) | 当前用户是否有访问数据库的权限 | CREATE/TEMPORARY |
has_function_privilege(user,function,privilege) | 用户是否有访问函数的权限 | EXECUTE |
has_function_privilege(function,privilege) | 当前用户是否有访问函数的权限 | EXECUTE |
has_language_privilege(user,language,privilege) | 用户是否有访问语言的权限 | USAGE |
has_language_privilege(language,privilege) | 当前用户是否有访问语言的权限 | USAGE |
has_schema_privilege(user,schema,privilege) | 用户是否有访问模式的权限 | CREAT/USAGE |
has_schema_privilege(schema,privilege) | 当前用户是否有访问模式的权限 | CREAT/USAGE |
has_tablespace_privilege(user,tablespace,privilege) | 用户是否有访问表空间的权限 | CREATE |
has_tablespace_privilege(tablespace,privilege) | 当前用户是否有访问表空间的权限 | CREATE |
注:以上函数均返回boolean类型。要评估一个用户是否在权限上持有赋权选项,给权限键字附加 WITH GRANT OPTION;比如 'UPDATE WITH GRANT OPTION'。
3. 模式可视性查询函数:
那些判断一个对象是否在当前模式搜索路径中可见的函数。 如果一个表所在的模式在搜索路径中,并且没有同名的表出现在搜索路径的更早的地方,那么就说这个表视可见的。 它等效于表可以不带明确模式修饰进行引用。
名字 | 描述 | 应用类型 |
pg_table_is_visible(table_oid) | 该表/视图是否在搜索路径中可见 | regclass |
pg_type_is_visible(type_oid) | 该类/视图型是否在搜索路径中可见 | regtype |
pg_function_is_visible(function_oid) | 该函数是否在搜索路径中可见 | regprocedure |
pg_operator_is_visible(operator_oid) | 该操作符是否在搜索路径中可见 | regoperator |
pg_opclass_is_visible(opclass_oid) | 该操作符表是否在搜索路径中可见 | regclass |
pg_conversion_is_visible(conversion_oid) | 转换是否在搜索路径中可见 | regoperator |
注:以上函数均返回boolean类型,所有这些函数都需要对象 OID 标识作为检查的对象。
postgres=# SELECT pg_table_is_visible('testtable'::regclass);
pg_table_is_visible
---------------------
t
(1 row)
4. 系统表信息函数:
名字 | 返回类型 | 描述 |
format_type(type_oid,typemod) | text | 获取一个数据类型的SQL名称 |
pg_get_viewdef(view_oid) | text | 为视图获取CREATE VIEW命令 |
pg_get_viewdef(view_oid,pretty_bool) | text | 为视图获取CREATE VIEW命令 |
pg_get_ruledef(rule_oid) | text | 为规则获取CREATE RULE命令 |
pg_get_ruledef(rule_oid,pretty_bool) | text | 为规则获取CREATE RULE命令 |
pg_get_indexdef(index_oid) | text | 为索引获取CREATE INDEX命令 |
pg_get_indexdef(index_oid,column_no,pretty_bool) | text | 为索引获取CREATE INDEX命令, 如果column_no不为零,则是只获取一个索引字段的定义 |
pg_get_triggerdef(trigger_oid) | text | 为触发器获取CREATE [CONSTRAINT] TRIGGER |
pg_get_constraintdef(constraint_oid) | text | 获取一个约束的定义 |
pg_get_constraintdef(constraint_oid,pretty_bool) | text | 获取一个约束的定义 |
pg_get_expr(expr_text,relation_oid) | text | 反编译一个表达式的内部形式,假设其中的任何Vars都引用第二个参数指出的关系 |
pg_get_expr(expr_text,relation_oid, pretty_bool) | text | 反编译一个表达式的内部形式,假设其中的任何Vars都引用第二个参数指出的关系 |
pg_get_userbyid(roleid) | name | 获取给出的ID的角色名 |
pg_get_serial_sequence(table_name,column_name) | text | 获取一个serial或者bigserial字段使用的序列名字 |
pg_tablespace_databases(tablespace_oid) | setof oid | 获取在指定表空间(OID表示)中拥有对象的一套数据库的OID的集合 |
这些函数大多数都有两个变种,其中一个可以选择对结果的"漂亮的打印"。 漂亮打印的格式更容易读,但是缺省的格式更有可能被将来的PostgreSQL版本用同样的方法解释;如果是用于转储,那么尽可能避免使用漂亮打印。 给漂亮打印参数传递false生成的结果和那个没有这个参数的变种生成的结果完全一样。
十三、系统管理函数:
1. 查询以及修改运行时配置参数的函数:
名字 | 返回类型 | 描述 |
current_setting(setting_name) | text | 当前设置的值 |
set_config(setting_name,new_value,is_local) | text | 设置参数并返回新值 |
current_setting用于以查询形式获取setting_name设置的当前数值。它和SQL命令SHOW是等效的。 比如:
MyTest=# SELECT current_setting('datestyle');
current_setting
-----------------
ISO, YMD
(1 row)
set_config将参数setting_name设置为new_value。如果is_local设置为true,那么新数值将只应用于当前事务。如果你希望新的数值应用于当前会话,那么应该使用false。它等效于SQL命令SET。比如:
MyTest=# SELECT set_config('log_statement_stats','off', false);
set_config
------------
off
(1 row)
2. 数据库对象尺寸函数:
名字 | 返回类型 | 描述 |
pg_tablespace_size(oid) | bigint | 指定OID代表的表空间使用的磁盘空间 |
pg_tablespace_size(name) | bigint | 指定名字的表空间使用的磁盘空间 |
pg_database_size(oid) | bigint | 指定OID代表的数据库使用的磁盘空间 |
pg_database_size(name) | bigint | 指定名称的数据库使用的磁盘空间 |
pg_relation_size(oid) | bigint | 指定OID代表的表或者索引所使用的磁盘空间 |
pg_relation_size(text) | bigint | 指定名称的表或者索引使用的磁盘空间。这个名字可以用模式名修饰 |
pg_total_relation_size(oid) | bigint | 指定OID代表的表使用的磁盘空间,包括索引和压缩数据 |
pg_total_relation_size(text) | bigint | 指定名字的表所使用的全部磁盘空间,包括索引和压缩数据。表名字可以用模式名修饰。 |
pg_size_pretty(bigint) | text | 把字节计算的尺寸转换成一个人类易读的尺寸单位 |
3. 数据库对象位置函数:
名字 | 返回类型 | 描述 |
pg_relation_filenode(relationregclass) | oid | 获取指定对象的文件节点编号(通常为对象的oid值)。 |
pg_relation_filepath(relationregclass) | text | 获取指定对象的完整路径名。 |
mydatabase=# select pg_relation_filenode('testtable');
pg_relation_filenode
----------------------
17877
(1 row)
mydatabase=# select pg_relation_filepath('testtable');
pg_relation_filepath
----------------------------------------------
pg_tblspc/17633/PG_9.1_201105231/17636/17877
(1 row)
一、索引的类型:
PostgreSQL提供了多 种索引类型:B-Tree、Hash、GiST和GIN,由于它们使用了不同的算法,因此每种索引类型都有其适合的查询类型,缺省时,CREATE INDEX命令将创建B-Tree索引。
1. B-Tree:
CREATE TABLE test1 (
id integer,
content varchar
);
CREATE INDEX test1_id_index ON test1 (id);
B-Tree索引主要用于等于和范围查询,特别是当索引列包含操作符" <、<=、=、>=和>"作为查询条件时,PostgreSQL的查询规划器都会考虑使用B-Tree索引。在使用BETWEEN、IN、IS NULL和IS NOT NULL的查询中,PostgreSQL也可以使用B-Tree索引。然而对于基于模式匹配操作符的查询,如LIKE、ILIKE、~和 ~*,仅当模式存在一个常量,且该常量位于模式字符串的开头时,如col LIKE 'foo%'或col ~ '^foo',索引才会生效,否则将会执行全表扫描,如:col LIKE '%bar'。
2. Hash:
CREATE INDEX name ON table USING hash (column);
散列(Hash)索引只能处理简单的等于比较。当索引列使用等于操作符进行比较时,查询规划器会考虑使用散列索引。
这里需要额外说明的是,PostgreSQL散列索引的性能不比B-Tree索引强,但是散列索引的尺寸和构造时间则更差。另外,由于散列索引操作目前没有记录WAL日志,因此一旦发生了数据库崩溃,我们将不得不用REINDEX重建散列索引。
3. GiST:
GiST索引不是一种单独的索引类型,而是一种架构,可以在该架构上实现很多不同的索引策略。从而可以使GiST索引根据不同的索引策略,而使用特定的操作符类型。
4. GIN:
GIN索引是反转索引,它可以处理包含多个键的值(比如数组)。与GiST类似,GIN同样支持用户定义的索引策略,从而可以使GIN索引根据不同的索引策略,而使用特定的操作符类型。作为示例,PostgreSQL的标准发布中包含了用于一维数组的GIN操作符类型,如:<@、@>、=、&&等。
二、复合索引:
PostgreSQL中的索引可以定义在数据表的多个字段上,如:
CREATE TABLE test2 (
major int,
minor int,
name varchar
}
CREATE INDEX test2_mm_idx ON test2 (major, minor);
在当前的版本中,只有B-tree、GiST和GIN支持复合索引,其中最多可以声明32个字段。
1. B-Tree类型的复合索引:
在B-Tree类型的复合索引中,该索引字段的任意子集均可用于查询条件,不过,只有当复合索引中的第一个索引字段(最左边)被包含其中时,才可以获得最高效率。
2. GiST类型的复合索引:
在GiST类型的复合索引中,只有当第一个索引字段被包含在查询条件中时,才能决定该查询会扫描多少索引数据,而其他索引字段上的条件只是会限制索引返回的条目。假如第一个索引字段上的大多数数据都有相同的键值,那么此时应用GiST索引就会比较低效。
3. GIN类型的复合索引:
与B-Tree和GiST索引不同的是,GIN复合索引不会受到查询条件中使用了哪些索引字段子集的影响,无论是哪种组合,都会得到相同的效率。
使用复合索引应该谨慎。在大多数情况下,单一字段上的索引就已经足够了,并且还节约时间和空间。除非表的使用模式非常固定,否则超过三个字段的索引几乎没什么用处。
三、组合多个索引:
PostgreSQL可以在查询时组合多个索引(包括同一索引的多次使用),来处理单个索引扫描不能实现的场合。与此同时,系统还可以在多个索引扫描之间组成AND和OR的条件。比如,一个类似WHERE x = 42 OR x = 47 OR x = 53 OR x = 99的查询,可以被分解成四个独立的基于x字段索引的扫描,每个扫描使用一个查询子句,之后再将这些扫描结果OR在一起并生成最终的结果。另外一个例子是,如果我们在x和y上分别存在独立的索引,那么一个类似WHERE x = 5 AND y = 6的查询,就会分别基于这两个字段的索引进行扫描,之后再将各自扫描的结果进行AND操作并生成最终的结果行。
为了组合多个索引,系统扫描每个需要的索引,然后在内存里组织一个BITMAP,它将给出索引扫描出的数据在数据表中的物理位置。然后,再根据查询的需要,把这些位图进行AND或者OR的操作并得出最终的BITMAP。最后,检索数据表并返回数据行。表的数据行是按照物理顺序进行访问的,因为这是位图的布局,这就意味着任何原来的索引的排序都将消失。如果查询中有ORDER BY子句,那么还将会有一个额外的排序步骤。因为这个原因,以及每个额外的索引扫描都会增加额外的时间,这样规划器有时候就会选择使用简单的索引扫描,即使有多个索引可用也会如此。
四、唯一索引:
目前,只有B-Tree索引可以被声明为唯一索引。
CREATE UNIQUE INDEX name ON table (column [, ...]);
如果索引声明为唯一索引,那么就不允许出现多个索引值相同的行。我们认为NULL值相互间不相等。
五、表达式索引:
表达式索引主要用于在查询条件中存在基于某个字段的函数或表达式的结果与其他值进行比较的情况,如:
SELECT * FROM test1 WHERE lower(col1) = 'value';
此时,如果我们仅仅是在col1字段上建立索引,那么该查询在执行时一定不会使用该索引,而是直接进行全表扫描。如果该表的数据量较大,那么执行该查询也将会需要很长时间。解决该问题的办法非常简单,在test1表上建立基于col1字段的表达式索引,如:
CREATE INDEX test1_lower_col1_idx ON test1 (lower(col1));
如果我们把该索引声明为UNIQUE,那么它会禁止创建那种col1数值只是大小写有区别的数据行,以及col1数值完全相同的数据行。因此,在表达式上的索引可以用于强制那些无法定义为简单唯一约束的约束。现在让我们再看一个应用表达式索引的例子。
SELECT * FROM people WHERE (first_name || ' ' || last_name) = 'John Smith';
和上面的例子一样,尽管我们可能会为first_name和last_name分别创建独立索引,或者是基于这两个字段的复合索引,在执行该查询语句时,这些索引均不会被使用,该查询能够使用的索引只有我们下面创建的表达式索引。
CREATE INDEX people_names ON people ((first_name || ' ' || last_name));
CREATE INDEX命令的语法通常要求在索引表达式周围书写圆括弧,就像我们在第二个例子里显示的那样。如果表达式只是一个函数调用,那么可以省略,就像我们在第一个例子里显示的那样。
从索引维护的角度来看,索引表达式要相对低效一些,因为在插入数据或者更新数据的时候,都必须为该行计算表达式的结果,并将该结果直接存储到索引里。然而在查询时,PostgreSQL就会把它们看做WHERE idxcol = 'constant',因此搜索的速度等效于基于简单索引的查询。通常而言,我们只是应该在检索速度比插入和更新速度更重要的场景下使用表达式索引。
六、部分索引:
部分索引(partial index)是建立在一个表的子集上的索引,而该子集是由一个条件表达式定义的(叫做部分索引的谓词)。该索引只包含表中那些满足这个谓词的行。
由于不是在所有的情况下都需要更新索引,因此部分索引会提高数据插入和数据更新的效率。然而又因为部分索引比普通索引要小,因此可以更好的提高确实需要索引部分的查询效率。见以下三个示例:
1. 索引字段和谓词条件字段一致:
CREATE INDEX access_log_client_ip_ix ON access_log(client_ip)
WHERE NOT (client_ip > inet '192.168.100.0' AND client_ip < inet '192.168.100.255');
下面的查询将会用到该部分索引:
SELECT * FROM access_log WHERE url = '/index.html' AND client_ip = inet '212.78.10.32';
下面的查询将不会用该部分索引:
一个不能使用这个索引的查询可以是∶
SELECT * FROM access_log WHERE client_ip = inet '192.168.100.23';
2. 索引字段和谓词条件字段不一致:
PostgreSQL支持带任意谓词的部分索引,唯一的约束是谓词的字段也要来自于同样的数据表。注意,如果你希望你的查询语句能够用到部分索引,那么就要求该查询语句的条件部分必须和部分索引的谓词完全匹配。 准确说,只有在PostgreSQL能够识别出该查询的WHERE条件在数学上涵盖了该索引的谓词时,这个部分索引才能被用于该查询。
CREATE INDEX orders_unbilled_index ON orders(order_nr) WHERE billed is not true;
下面的查询一定会用到该部分索引:
SELECT * FROM orders WHERE billed is not true AND order_nr < 10000;
那么对于如下查询呢?
SELECT * FROM orders WHERE billed is not true AND amount > 5000.00;
这个查询将不像上面那个查询这么高效,毕竟查询的条件语句中没有用到索引字段,然而查询条件"billed is not true"却和部分索引的谓词完全匹配,因此PostgreSQL将扫描整个索引。这样只有在索引数据相对较少的情况下,该查询才能更有效一些。
下面的查询将不会用到部分索引。
SELECT * FROM orders WHERE order_nr = 3501;
3. 数据表子集的唯一性约束:
CREATE TABLE tests (
subject text,
target text,
success boolean,
...
);
CREATE UNIQUE INDEX tests_success_constraint ON tests(subject, target) WHERE success;
该部分索引将只会对success字段值为true的数据进行唯一性约束。在实际的应用中,如果成功的数据较少,而不成功的数据较多时,该实现方法将会非常高效。
七、检查索引的使用:
见以下四条建议:
1. 总是先运行ANALYZE。
该命令将会收集表中数值分布状况的统计。在估算一个查询返回的行数时需要这个信息,而规划器则需要这个行数以便给每个可能的查询规划赋予真实的开销值。如果缺乏任何真实的统计信息,那么就会使用一些缺省数值,这样肯定是不准确的。因此,如果还没有运行ANALYZE就检查一个索引的使用状况,那将会是一次失败的检查。
2. 使用真实的数据做实验。
用测试数据填充数据表,那么该表的索引将只会基于测试数据来评估该如何使用索引,而不是对所有的数据都如此使用。比如从100000行中选1000行,规划器可能会考虑使用索引,那么如果从100行中选1行就很难说也会使用索引了。因为100行的数据很可能是存储在一个磁盘页面中,然而没有任何查询规划能比通过顺序访问一个磁盘页面更加高效了。与此同时,在模拟测试数据时也要注意,如果这些数据是非常相似的数据、完全随机的数据,或按照排序顺序插入的数据,都会令统计信息偏离实际数据应该具有的特征。
3. 如果索引没有得到使用,那么在测试中强制它的使用也许会有些价值。有一些运行时参数可以关闭各种各样的查询规划。
4. 强制使用索引用法将会导致两种可能:一是系统选择是正确的,使用索引实际上并不合适,二是查询计划的开销计算并不能反映现实情况。这样你就应该对使用和不使用索引的查询进行计时,这个时候EXPLAIN ANALYZE命令就很有用了。
在SQL的标准中事物隔离级别分为以下四种:
1. 读未提交(Read uncommitted)
2. 读已提交(Read committed)
3. 可重复读(Repeatable read)
4. 可串行化(Serializable)
然而PostgreSQL在9.1之前的版本中只是实现了其中两种,即读已提交和可串行化,如果在实际应用中选择了另外两种,那么PostgreSQL将会自动向更严格的隔离级别调整。在PostgreSQL v9.1的版本中提供了三种实现方式,即在原有的基础上增加了可重复读。在这篇博客中我们将只是针对2)和4)进行说明和比较,因为在9.1中,3)和4)的差别也是非常小的。
读已提交 | 可串行化 | |
PostgreSQL缺省隔离级别 | 是 | 否 |
其它事物未提交数据是否可见 | 不可见 | 不可见 |
执行效率 | 高 | 低 |
适用场景 | 简单SQL逻辑,如果SQL语句中含有嵌套查询,那么在多次SQL查询中将极有可能获得不同版本的数据。 | 复杂SQL逻辑,特别是带有嵌套的查询比较适用。 |
SELECT查询一致性时间点 | 从该SELECT查询开始执行时,在此查询执行期间,任何其它并发事物针对该查询结果集的数据操作都将不会被本次查询读到,即本次查询获取的数据版本是与查询开始执行时的数据版本相一致。 | 从该SELECT查询所在事物开始时,在此查询执行期间,任何其它并发事物针对该查询结果集的数据操作都将不会被本次查询读到,即本次查询获取的数据版本是与查询所在事物开始时的数据版本相一致。 |
同事物内的数据操作是否可见 | 比如在同一个事物内存在update和select操作,即使当前事物尚未提交,update所作的修改,在当前事物后面的select中依然可见。 | 和读已提交相同。 |
同事物内多次相同的select所见的数据是否相同 | 不同,由于该级别select的一致性时间点是该查询开始执行时,而多次查询的时间点将肯定不相同,如果在第一次查询开始到第二次查询开始之间,其它的并发事物修改并提交或当前事物仅修改了查询将要获取的数据,那么这些数据操作的结果将会在第二个查询中有所体现。 | 需要分两步来说,对于同一事物内的修改如果发生在两次查询语句之间,那么第二个查询将会看到这些修改的结果。然而对于其它并发事物的修改,将不会造成任何影响,即两次select的结果是相同的。原因显而易见,该隔离级别的select一致性时间点是与事物开始时相一致的。 |
相同行数据的修改 | 如果此时两个并发事物在修改同一行数据,先修改的事物将会给该行加行级锁,另外一个事物将进入等待状态,直到第一个事物操作该行结束。那么倘若第一个针对该行的修改操作最终被其事物回滚,第二个修改操作在结束等待后,将直接修改该数据。然而如果第一个操作是被正常提交的话,那么就需要进一步判断该操作的类型,如果是删除(delete)该行,第二个修改操作将直接被忽略。如果是update该行的记录,第二个修改操作则需要重新评估该行是否依然符合之前定义的修改条件。 | 和读已提交隔离级别的机制基本相同,只是在第一个修改操作提交后,第二个操作将不再区分之前的修改是delete还是update,而是直接并返回下面信息:Error: Can't serialize access due to concurrent update. 这是因为一个可串行化的事务在可串行化事务开始之后不能更改或者锁住被其他事务更改过的行。因此,当应用收到这样的错误信息时,它应该退出当前的事务然后从头开始重新进行整个事务。在应用程序中,也应该有必要的代码来专门处理该类错误。 |
最后需要说明的是,在绝大多数的情况下,读已提交级别均可适用,而且该级别的并发效率更高。只有在比较特殊的情况下,才手工将当前的事物隔离级别调整为可串行化或可重复读。
一、服务器进程的启动和关闭:
下面是pg_ctl命令的使用方法和常用选项,需要指出的是,该命令是postgres命令的封装体,因此在使用上比直接使用postgres更加方便。
pg_ctl init[db] [-D DATADIR] [-s] [-o "OPTIONS"]
pg_ctl start [-w] [-t SECS] [-D DATADIR] [-s] [-l FILENAME] [-o "OPTIONS"]
pg_ctl stop [-W] [-t SECS] [-D DATADIR] [-s] [-m SHUTDOWN-MODE]
pg_ctl restart [-w] [-t SECS] [-D DATADIR] [-s] [-m SHUTDOWN-MODE]
pg_ctl reload [-D DATADIR] [-s]
pg_ctl status [-D DATADIR]
pg_ctl promote [-D DATADIR] [-s]
选项 | 描述 |
-D | 指定数据库存储的路径 |
-l | 指定服务器进程的日志文件 |
-s | 仅打印错误信息,不打印普通信息 |
-t SECS | 当使用-w选项时等待的秒数 |
-w | 等待直到数据库操作完成(对于stop而言,该选项时缺省选项) |
-W | 不等待任何操作的完成 |
--help | 显示帮助信息 |
--version | 显示版本信息 |
-m | 对于stop和restart操作,可以指定关闭模式 |
系统关闭模式 | |
smart | 不在接收新的连接,直到当前已有的连接都断开之后才退出系统 |
fast | 不在接收新的连接请求,主动关闭已经建立的连接,之后退出系统 |
immediate | 立即退出,但是在restart的时候需要有恢复的操作被执行 |
这里我们只是给出最为常用的使用方式,即数据库服务器的正常启动和关闭。
#start表示启动postgres服务器进程。
#-D指定数据库服务器的初始目录的存放路径。
#-l指定数据库服务器进程的日志文件
/> pg_ctl -w start -D /opt/PostgreSQL/9.1/data -l /opt/PostgreSQL/9.1/data/pg_log/startup.log
#stop表示停止postgres服务器进程
#-m fast在关闭系统时,使用fast的关闭模式。
/> pg_ctl stop -m fast -w -D /opt/PostgreSQL/9.1/data
二、服务器配置:
1. 设置参数:
在PostgreSQL中,所有配置参数名都是大小写不敏感的。每个参数都可以接受四种类型的值,它们分别是布尔、整数、浮点数和字符串。其中布尔值可以是ON、OFF、TRUE、FALSE、YES、NO、1和0。包含这些参数的配置文件是postgresql.conf,该文件通常存放在initdb初始化的数据(data)目录下,见如下配置片段:
# 这是一个注释
log_connections = yes
log_destination = 'syslog'
search_path = '$user, public'
井号(#)开始的行为注释行,如果配置值中包含数字,则需要用单引号括起。如果参数值本身包含单引号,我们可以写两个单引号(推荐方法)或用反斜扛包围。
这里需要注意的是,并非所有配置参数都可以在服务器运行时执行动态修改,有些参数在修改后,只能等到服务器重新启动后才能生效。
PostgreSQL还提供了另外一种修改配置参数的方法,即在命令行上直接执行修改命令,如:
/> postgres -c log_connections=yes -c log_destination='syslog'
如果此时命令行设置的参数和配置文件中的参数相互冲突,那么命令行中给出的参数将覆盖配置文件中已有的参数值。除此之外,我们还可以通过ALTER DATABASE和ALTER USER等PostgreSQL的数据定义命令来分别修改指定数据库或指定用户的配置信息。其中针对数据库的设置将覆盖任何从postgres命令行或者配置文件从给出的设置,然后又会被针对用户的设置覆盖,最后又都会被每会话的选项覆盖。下面是当服务器配置出现冲突时,PostgreSQL服务器将会采用哪种方式的优先级,如:
1). 基于会话的配置;
2). 基于用户的配置;
3). 基于数据库的配置;
4). postgres命令行指定的配置;
5). 配置文件postgresql.conf中给出的配置。
最后需要说明的是,有些设置可以通过PostgreSQL的set命令进行设置,如在psql中我们可以输入:
SET ENABLE_SEQSCAN TO OFF;
也可以通过show命令来显示指定配置的当前值,如:
SHOW ENABLE_SEQSCAN;
与此同时,我们也可以手工查询pg_settings系统表的方式来检索感兴趣的系统参数。
三、内存相关的参数配置:
1. shared_buffers(integer):
设置数据库服务器可以使用的共享内存数量。缺省情况下可以设置为32MB,但是不要少于128KB。因为该值设置的越高对系统的性能越有好处。该配置参数只能在数据库启动时设置。
此时,如果你有一台专用的数据库服务器,其内存为1G或者更多,那么我们推荐将该值设置为系统内存的25%。
2. work_mem(integer):
PostgreSQL在执行排序操作时,会根据work_mem的大小决定是否将一个大的结果集拆分为几个小的和work_mem差不多大小的临时文件。显然拆分的结果是降低了排序的速度。因此增加work_mem有助于提高排序的速度。然而需要指出的是,如果系统中同时存在多个排序操作,那么每个操作在排序时使用的内存数量均为work_mem,因此在我们设置该值时需要注意这一问题。
3. maintence_work_mem(integer):
指定在维护性操作中使用的最大内存数,如VACUUM、CREATE INDEX和ALTER TABLE ADD FOREIGN KEY等,该配置的缺省值为16MB。因为每个会话在同一时刻只能执行一个该操作,所以使用的频率不高,但是这些指令往往消耗较多的系统资源,因此应该尽快让这些指令快速执行完毕。
一、恢复磁盘空间:
在PostgreSQL中,使用delete和update语句删除或更新的数据行并没有被实际删除,而只是在旧版本数据行的物理地址上将该行的状态置为已删除或已过期。因此当数据表中的数据变化极为频繁时,那么在一段时间之后该表所占用的空间将会变得很大,然而数据量却可能变化不大。要解决该问题,需要定期对数据变化频繁的数据表执行VACUUM操作。
VACUUM命令存在两种形式,VACUUM和VACUUM FULL,它们之间的区别见如下表格:
无VACUUM | VACUUM | VACUUM FULL | |
删除大量数据之后 | 只是将删除数据的状态置为已删除,该空间不能记录被重新使用。 | 如果删除的记录位于表的末端,其所占用的空间将会被物理释放并归还操作系统。如果不是末端数据,该命令会将指定表或索引中被删除数据所占用空间重新置为可用状态,那么在今后有新数据插入时,将优先使用该空间,直到所有被重用的空间用完时,再考虑使用新增的磁盘页面。 | 不论被删除的数据是否处于数据表的末端,这些数据所占用的空间都将被物理的释放并归还于操作系统。之后再有新数据插入时,将分配新的磁盘页面以供使用。 |
执行效率 | 由于只是状态置为操作,因此效率较高。 | 在当前版本的PostgreSQL(v9.1)中,该命令会为指定的表或索引重新生成一个数据文件,并将原有文件中可用的数据导入到新文件中,之后再删除原来的数据文件。因此在导入过程中,要求当前磁盘有更多的空间可用于此操作。由此可见,该命令的执行效率相对较低。 | |
被删除的数据所占用的物理空间是否被重新规划给操作系统。 | 不会 | 不会 | 会 |
在执行VACUUM命令时,是否可以并发执行针对该表的其他操作。 | 由于该操作是共享锁,因此可以与其他操作并行进行。 | 由于该操作需要在指定的表上应用排它锁,因此在执行该操作期间,任何基于该表的操作都将被挂起,知道该操作完成。 | |
推荐使用方式 | 在进行数据清空是,可以使用truncate操作,因为该操作将会物理的清空数据表,并将其所占用的空间直接归还于操作系统。 | 为了保证数据表的磁盘页面数量能够保持在一个相对稳定值,可以定期执行该操作,如每天或每周中数据操作相对较少的时段。 | 考虑到该操作的开销,以及对其他错误的排斥,推荐的方式是,定期监控数据量变化较大的表,只有确认其磁盘页面占有量接近临界值时,才考虑执行一次该操作。即便如此,也需要注意尽量选择数据操作较少的时段来完成该操作。 |
执行后其它操作的效率 | 对于查询而言,由于存在大量的磁盘页面碎片,因此效率会逐步降低。 | 相比于不执行任何VACUUM操作,其效率更高,但是插入的效率会有所降低。 | 在执行完该操作后,所有基于该表的操作效率都会得到极大的提升。 |
二、更新规划器统计:
PostgreSQL查询规划器在选择最优路径时,需要参照相关数据表的统计信息用以为查询生成最合理的规划。这些统计是通过ANALYZE命令获得的,你可以直接调用该命令,或者把它当做VACUUM命令里的一个可选步骤来调用,如VACUUM ANAYLYZE table_name,该命令将会先执行VACUUM再执行ANALYZE。与回收空间(VACUUM)一样,对数据更新频繁的表保持一定频度的ANALYZE,从而使该表的统计信息始终处于相对较新的状态,这样对于基于该表的查询优化将是极为有利的。然而对于更新并不频繁的数据表,则不需要执行该操作。
我们可以为特定的表,甚至是表中特定的字段运行ANALYZE命令,这样我们就可以根据实际情况,只对更新比较频繁的部分信息执行ANALYZE操作,这样不仅可以节省统计信息所占用的空间,也可以提高本次ANALYZE操作的执行效率。这里需要额外说明的是,ANALYZE是一项相当快的操作,即使是在数据量较大的表上也是如此,因为它使用了统计学上的随机采样的方法进行行采样,而不是把每一行数据都读取进来并进行分析。因此,可以考虑定期对整个数据库执行该命令。
事实上,我们甚至可以通过下面的命令来调整指定字段的抽样率,如:
ALTER TABLE testtable ALTER COLUMN test_col SET STATISTICS 200
注意:该值的取值范围是0--1000,其中值越低采样比例就越低,分析结果的准确性也就越低,但是ANALYZE命令执行的速度却更快。如果将该值设置为-1,那么该字段的采样比率将恢复到系统当前默认的采样值,我们可以通过下面的命令获取当前系统的缺省采样值。
postgres=# show default_statistics_target;
default_statistics_target
---------------------------
100
(1 row)
从上面的结果可以看出,该数据库的缺省采样值为100(10%)。
三、VACUUM和ANALYZE的示例:
#1. 创建测试数据表。
postgres=# CREATE TABLE testtable (i integer);
CREATE TABLE
#2. 为测试表创建索引。
postgres=# CREATE INDEX testtable_idx ON testtable(i);
CREATE INDEX
#3. 创建批量插入测试数据的函数。
postgres=# CREATE OR REPLACE FUNCTION test_insert() returns integer AS $$
DECLARE
min integer;
max integer;
BEGIN
SELECT COUNT(*) INTO min from testtable;
max := min + 10000;
FOR i IN min..max LOOP
INSERT INTO testtable VALUES(i);
END LOOP;
RETURN 0;
END;
$$ LANGUAGE plpgsql;
CREATE FUNCTION
#4. 批量插入数据到测试表(执行四次)
postgres=# SELECT test_insert();
test_insert
-------------
0
(1 row)
#5. 确认四次批量插入都成功。
postgres=# SELECT COUNT(*) FROM testtable;
count
-------
40004
(1 row)
#6. 分析测试表,以便有关该表的统计信息被更新到PostgreSQL的系统表。
postgres=# ANALYZE testtable;
ANALYZE
#7. 查看测试表和索引当前占用的页面数量(通常每个页面为8k)。
postgres=# SELECT relname,relfilenode, relpages FROM pg_class WHERE relname = 'testtable' or relname = 'testtable_idx';
relname | relfilenode | relpages
---------------+-------------+----------
testtable | 17601 | 157
testtable_idx | 17604 | 90
#8. 批量删除数据。
postgres=# DELETE FROM testtable WHERE i < 30000;
DELETE 30003
#9. 执行vacuum和analyze,以便更新系统表,同时为该表和索引记录高水标记。
#10. 这里需要额外说明的是,上面删除的数据均位于数据表的前部,如果删除的是末尾部分,
# 如where i > 10000,那么在执行VACUUM ANALYZE的时候,数据表将会被物理的缩小。
postgres=# VACUUM ANALYZE testtable;
ANALYZE
#11. 查看测试表和索引在删除后,再通过VACUUM ANALYZE更新系统统计信息后的结果(保持不变)。
postgres=# SELECT relname,relfilenode, relpages FROM pg_class WHERE relname = 'testtable' or relname = 'testtable_idx';
relname | relfilenode | relpages
---------------+-------------+----------
testtable | 17601 | 157
testtable_idx | 17604 | 90
(2 rows)
#12. 再重新批量插入两次,之后在分析该表以更新其统计信息。
postgres=# SELECT test_insert(); --执行两次。
test_insert
-------------
0
(1 row)
postgres=# ANALYZE testtable;
ANALYZE
#13. 此时可以看到数据表中的页面数量仍然为之前的高水标记数量,索引页面数量的增加
# 是和其内部实现方式有关,但是在后面的插入中,索引所占的页面数量就不会继续增加。
postgres=# SELECT relname,relfilenode, relpages FROM pg_class WHERE relname = 'testtable' or relname = 'testtable_idx';
relname | relfilenode | relpages
---------------+-------------+----------
testtable | 17601 | 157
testtable_idx | 17604 | 173
(2 rows)
postgres=# SELECT test_insert();
test_insert
-------------
0
(1 row)
postgres=# ANALYZE testtable;
ANALYZE
#14. 可以看到索引的页面数量确实没有继续增加。
postgres=# SELECT relname,relfilenode, relpages FROM pg_class WHERE relname = 'testtable' or relname = 'testtable_idx';
relname | relfilenode | relpages
---------------+-------------+----------
testtable | 17601 | 157
testtable_idx | 17604 | 173
(2 rows)
#15. 重新批量删除数据。
postgres=# DELETE FROM testtable WHERE i < 30000;
DELETE 19996
#16. 从后面的查询可以看出,在执行VACUUM FULL命令之后,测试表和索引所占用的页面数量
# 确实降低了,说明它们占用的物理空间已经缩小了。
postgres=# VACUUM FULL testtable;
VACUUM
postgres=# SELECT relname,relfilenode, relpages FROM pg_class WHERE relname = 'testtable' or relname = 'testtable_idx';
relname | relfilenode | relpages
---------------+-------------+----------
testtable | 17602 | 118
testtable_idx | 17605 | 68
(2 rows)
四、定期重建索引:
在PostgreSQL中,为数据更新频繁的数据表定期重建索引(REINDEX INDEX)是非常有必要的。对于B-Tree索引,只有那些已经完全清空的索引页才会得到重复使用,对于那些仅部分空间可用的索引页将不会得到重用,如果一个页面中大多数索引键值都被删除,只留下很少的一部分,那么该页将不会被释放并重用。在这种极端的情况下,由于每个索引页面的利用率极低,一旦数据量显著增加,将会导致索引文件变得极为庞大,不仅降低了查询效率,而且还存在整个磁盘空间被完全填满的危险。
对于重建后的索引还存在另外一个性能上的优势,因为在新建立的索引上,逻辑上相互连接的页面在物理上往往也是连在一起的,这样可以提高磁盘页面被连续读取的几率,从而提高整个操作的IO效率。见如下示例:
#1. 此时已经在该表中插入了大约6万条数据,下面的SQL语句将查询该索引所占用的磁盘空间。
postgres=# SELECT relname, pg_relation_size(oid)/1024 || 'K' AS size FROM pg_class WHERE relkind='i' AND relname = 'testtable_idx';
relname | size
----------------+------
testtable_idx | 1240K
(1 row)
#2. 删除数据表中大多数的数据。
postgres=# DELETE FROM testtable WHERE i > 20000;
DELETE 50006
#3. 分析一个该表,以便于后面的SQL语句继续查看该索引占用的空间。
postgres=# ANALYZE testtable;
ANALYZE
#4. 从该查询结果可以看出,该索引所占用的空间并未减少,而是和之前的完全一样。
postgres=# SELECT pg_relation_size('testtable_idx')/1024 || 'K' AS size;
size
------
1240K
(1 row)
#5. 重建索引。
postgres=# REINDEX INDEX testtable_idx;
REINDEX
#6. 查看重建后的索引实际占用的空间,从结果中可以看出索引的尺寸已经减少。
postgres=# SELECT pg_relation_size('testtable_idx')/1024 || 'K' AS size;
size
------
368K
(1 row)
#7. 最后一点需要记住的是,在索引重建后一定要分析数据表。
postgres=# ANALYZE testtable;
ANALYZE
五、观察磁盘使用情况:
1. 查看数据表所占用的磁盘页面数量。
#relpages只能被VACUUM、ANALYZE和几个DDL命令更新,如CREATE INDEX。通常一个页面的长度为8K字节。
postgres=# SELECT relfilenode, relpages FROM pg_class WHERE relname = 'testtable';
relfilenode | relpages
-------------+----------
16412 | 79
(1 row)
2. 查看指定数据表的索引名称和索引占用的磁盘页面数量。
postgres=# SELECT c2.relname, c2.relpages FROM pg_class c, pg_class c2, pg_index i
WHERE c.relname = 'testtable' AND c.oid = i.indrelid AND c2.oid = i.indexrelid
ORDER BY c2.relname;
relname | relpages
---------------+----------
testtable_idx | 46
(1 row)
一、pg_class:
该系统表记录了数据表、索引(仍然需要参阅pg_index)、序列、视图、复合类型和一些特殊关系类型的元数据。注意:不是所有字段对所有对象类型都有意义。
名字 | 类型 | 引用 | 描述 |
relname | name | 数据类型名字。 | |
relnamespace | oid | pg_namespace.oid | 包含这个对象的名字空间(模式)的OI。 |
reltype | oid | pg_type.oid | 对应这个表的行类型的数据类型。 |
relowner | oid | pg_authid.oid | 对象的所有者。 |
relam | oid | pg_am.oid | 对于索引对象,表示该索引的类型(B-tree,hash)。 |
relfilenode | oid | 对象存储在磁盘上的文件名,如果没有则为0。 | |
reltablespace | oid | pg_tablespace.oid | 对象所在的表空间。如果为零,则表示使用该数据库的缺省表空间。(如果对象在磁盘上没有文件,这个字段就没有什么意义) |
relpages | int4 | 该数据表或索引所占用的磁盘页面数量,查询规划器会借助该值选择最优路径。 | |
reltuples | float4 | 表中行的数量,该值只是被规划器使用的一个估计值。 | |
reltoastrelid | oid | pg_class.oid | 与此表关联的TOAST表的OID,如果没有为0。TOAST表在一个从属表里"离线"存储大字段。 |
reltoastidxid | oid | pg_class.oid | 如果是TOAST表,该字段为它索引的OID,如果不是TOAST表则为0。 |
relhasindex | bool | 如果这是一个数据表而且至少有(或者最近有过)一个索引,则为真。它是由CREATE INDEX设置的,但DROP INDEX不会立即将它清除。如果VACUUM发现一个表没有索引,那么它清理 relhasindex。 | |
relisshared | bool | 如果该表在整个集群中由所有数据库共享,则为真。 | |
relkind | char | r = 普通表,i = 索引,S = 序列,v = 视图, c = 复合类型,s = 特殊,t = TOAST表 | |
relnatts | int2 | 数据表中用户字段的数量(除了系统字段以外,如oid)。在pg_attribute里肯定有相同数目的数据行。见pg_attribute.attnum. | |
relchecks | int2 | 表中检查约束的数量,参阅pg_constraint表。 | |
reltriggers | int2 | 表中触发器的数量;参阅pg_trigger表。 | |
relhasoids | bool | 如果我们为对象中的每行都生成一个OID,则为真。 | |
relhaspkey | bool | 如果该表存在主键,则为真。 | |
relhasrules | bool | 如表有规则就为真;参阅pg_rewrite表。 | |
relhassubclass | bool | 如果该表有子表,则为真。 | |
relacl | aclitem[] | 访问权限。 |
见如下应用示例:
#查看指定表对象testtable的模式
postgres=# SELECT relname,relnamespace,nspname FROM pg_class c,pg_namespace n WHERE relname = 'testtable' AND relnamespace = n.oid;
relname | relnamespace | nspname
-------------+--------------+---------
testtable | 2200 | public
(1 row)
#查看指定表对象testtable的owner(即role)。
postgres=# select relname,rolname from pg_class c,pg_authid au where relname = 'testtable' and relowner = au.oid;
relname | rolname
-------------+----------
testtable | postgres
(1 row)
二、pg_attribute:
该系统表存储所有表(包括系统表,如pg_class)的字段信息。数据库中的每个表的每个字段在pg_attribute表中都有一行记录。
名字 | 类型 | 引用 | 描述 |
attrelid | oid | pg_class.oid | 此字段所属的表。 |
attname | name | 字段名。 | |
atttypid | oid | pg_type.oid | 字段的数据类型。 |
attstattarget | int4 | attstattarget控制ANALYZE为这个字段设置的统计细节的级别。零值表示不收集统计信息,负数表示使用系统缺省的统计对象。正数值的确切信息是和数据类型相关的。 | |
attlen | int2 | 该字段所属类型的长度。(pg_type.typlen的拷贝) | |
attnum | int2 | 字段的编号,普通字段是从1开始计数的。系统字段,如oid,是任意的负数。 | |
attndims | int4 | 如果该字段是数组,该值表示数组的维数,否则是0。 | |
attcacheoff | int4 | 在磁盘上总是-1,但是如果装载入内存中的行描述器中, 它可能会被更新为缓冲在行中字段的偏移量。 | |
atttypmod | int4 | 表示数据表在创建时提供的类型相关的数据(比如,varchar字段的最大长度)。其值对那些不需要atttypmod的类型而言通常为-1。 | |
attbyval | bool | pg_type.typbyval字段值的拷贝。 | |
attstorage | char | pg_type.typstorage字段值的拷贝。 | |
attalign | char | pg_type.typalign字段值的拷贝。 | |
attnotnull | bool | 如果该字段带有非空约束,则为真,否则为假。 | |
atthasdef | bool | 该字段是否存在缺省值,此时它对应pg_attrdef表里实际定义此值的记录。 | |
attisdropped | bool | 该字段是否已经被删除。如果被删除,该字段在物理上仍然存在表中,但会被分析器忽略,因此不能再通过SQL访问。 | |
attislocal | bool | 该字段是否局部定义在对象中的。 | |
attinhcount | int4 | 该字段所拥有的直接祖先的个数。如果一个字段的祖先个数非零,那么它就不能被删除或重命名。 |
见如下应用示例:
#查看指定表中包含的字段名和字段编号。
postgres=# SELECT relname, attname,attnum FROM pg_class c,pg_attribute attr WHERE relname = 'testtable' AND c.oid = attr.attrelid;
relname | attname | attnum
-------------+----------+--------
testtable | tableoid | -7
testtable | cmax | -6
testtable | xmax | -5
testtable | cmin | -4
testtable | xmin | -3
testtable | ctid | -1
testtable | i | 1
(7 rows)
#只查看用户自定义字段的类型
postgres=# SELECT relname,attname,typname FROM pg_class c,pg_attribute a,pg_type t WHERE c.relname = 'testtable' AND c.oid = attrelid AND atttypid = t.oid AND attnum > 0;
relname | attname | typname
-------------+----------+---------
testtable | i | int4
(7 rows)
三、pg_attrdef:
该系统表主要存储字段缺省值,字段中的主要信息存放在pg_attribute系统表中。注意:只有明确声明了缺省值的字段在该表中才会有记录。
名字 | 类型 | 引用 | 描述 |
adrelid | oid | pg_class.oid | 这个字段所属的表 |
adnum | int2 | pg_attribute.attnum | 字段编号,其规则等同于pg_attribute.attnum |
adbin | text | 字段缺省值的内部表现形式。 | |
adsrc | text | 缺省值的人可读的表现形式。 |
见如下应用示例:
#查看指定表有哪些字段存在缺省值,同时显示出字段名和缺省值的定义方式
postgres=# CREATE TABLE testtable2 (i integer DEFAULT 100);
CREATE TABLE
postgres=# SELECT c.relname, a.attname, ad.adnum, ad.adsrc FROM pg_class c, pg_attribute a, pg_attrdef ad WHERE relname = 'testtable2' AND ad.adrelid = c.oid AND adnum = a.attnum AND attrelid = c.oid;
relname | attname | adnum | adsrc
-------------+----------+---------+-------
testtable2 | i | 1 | 100
(1 row)
四、pg_authid:
该系统表存储有关数据库认证的角色信息,在PostgreSQL中角色可以表现为用户和组两种形式。对于用户而言只是设置了rolcanlogin标志的角色。由于该表包含口令数据,所以它不是公共可读的。PostgreSQL中提供了另外一个建立在该表之上的系统视图pg_roles,该视图将口令字段填成空白。
名字 | 类型 | 引用 | 描述 |
rolname | name | 角色名称。 | |
rolsuper | bool | 角色是否拥有超级用户权限。 | |
rolcreaterole | bool | 角色是否可以创建其它角色。 | |
rolcreatedb | bool | 角色是否可以创建数据库。 | |
rolcatupdate | bool | 角色是否可以直接更新系统表(如果该设置为假,即使超级用户也不能更新系统表)。 | |
rolcanlogin | bool | 角色是否可以登录,换句话说,这个角色是否可以给予会话认证标识符。 | |
rolpassword | text | 口令(可能是加密的);如果没有则为NULL。 | |
rolvaliduntil | timestamptz | 口令失效时间(只用于口令认证);如果没有失效期,则为NULL。 | |
rolconfig | text[] | 运行时配置变量的会话缺省。 |
见如下应用示例:
#从输出结果可以看出口令字段已经被加密。
postgres=# SELECT rolname,rolpassword FROM pg_authid;
rolname | rolpassword
-----------+-------------------------------------
postgres | md5a3556571e93b0d20722ba62be61e8c2d
五、pg_auth_members:
该系统表存储角色之间的成员关系。
名字 | 类型 | 引用 | 描述 |
roleid | oid | pg_authid.oid | 组角色的ID。 |
member | oid | pg_authid.oid | 属于组角色roleid的成员角色的ID。 |
grantor | oid | pg_authid.oid | 赋予此成员关系的角色的ID。 |
admin_option | bool | 如果具有把其它成员角色加入组角色的权限,则为真。 |
见如下应用示例:
#1. 先查看角色成员表中有哪些角色之间的隶属关系,在当前结果集中只有一个成员角色隶属于一个组角色,
# 如果有多个成员角色隶属于同一个组角色,这样将会有多条记录。
postgres=# SELECT * FROM pg_auth_members ;
roleid | member | grantor | admin_option
--------+--------+---------+--------------
16446 | 16445 | 10 | f
(1 row)
#2. 查看组角色的名字。
postgres=# SELECT rolname FROM pg_authid a,pg_auth_members am WHERE a.oid = am.roleid;
rolname
---------
mygroup
(1 row)
#3. 查看成员角色的名字。
#4. 如果需要用一个结果集获取角色之间的隶属关系,可以将这两个结果集作为子查询后再进行关联。
postgres=# SELECT rolname FROM pg_authid a,pg_auth_members am WHERE a.oid = am.member;
rolname
---------
myuser
(1 row)
六、pg_constraint:
该系统表存储PostgreSQL中表对象的检查约束、主键、唯一约束和外键约束。
名字 | 类型 | 引用 | 描述 |
conname | name | 约束名字(不一定是唯一的)。 | |
connamespace | oid | pg_namespace.oid | 包含这个约束的名字空间(模式)的OID。 |
contype | char | c = 检查约束, f = 外键约束, p = 主键约束, u = 唯一约束 | |
condeferrable | bool | 该约束是否可以推迟。 | |
condeferred | bool | 缺省时这个约束是否是推迟的? | |
conrelid | oid | pg_class.oid | 该约束所在的表,如果不是表约束则为0。 |
contypid | oid | pg_type.oid | 该约束所在的域,如果不是域约束则为0。 |
confrelid | oid | pg_class.oid | 如果为外键,则指向参照的表,否则为0。 |
confupdtype | char | 外键更新动作代码。 | |
confdeltype | char | 外键删除动作代码。 | |
confmatchtype | char | 外键匹配类型。 | |
conkey | int2[] | pg_attribute.attnum | 如果是表约束,则是约束控制的字段列表。 |
confkey | int2[] | pg_attribute.attnum | 如果是外键,则是参照字段的列表。 |
conbin | text | 如果是检查约束,则表示表达式的内部形式。 | |
consrc | text | 如果是检查约束,则是表达式的人可读的形式。 |
七、pg_tablespace:
该系统表存储表空间的信息。注意:表可以放在特定的表空间里,以帮助管理磁盘布局和解决IO瓶颈。
名字 | 类型 | 引用 | 描述 |
spcname | name | 表空间名称。 | |
spcowner | oid | pg_authid.oid | 表空间的所有者,通常是创建它的角色。 |
spclocation | text | 表空间的位置(目录路径)。 | |
spcacl | aclitem[] | 访问权限。 |
见如下应用示例:
#1. 创建表空间。
postgres=# CREATE TABLESPACE my_tablespace LOCATION '/opt/PostgreSQL/9.1/mydata';
CREATE TABLESPACE
#2. 将新建表空间的CREATE权限赋予public。
postgres=# GRANT CREATE ON TABLESPACE my_tablespace TO public;
GRANT
#3. 查看系统内用户自定义表空间的名字、文件位置和创建它的角色名称。
#4. 系统创建时自动创建的两个表空间(pg_default和pg_global)的文件位置为空(不是NULL)。
postgres=# SELECT spcname,rolname,spclocation FROM pg_tablespace ts,pg_authid a WHERE ts.spcowner = a.oid AND spclocation <> '';
spcname | rolname | spclocation
---------------+----------+----------------------------
my_tablespace | postgres | /opt/PostgreSQL/9.1/mydata
(1 row)
八、pg_namespace:
该系统表存储名字空间(模式)。
名字 | 类型 | 引用 | 描述 |
nspname | name | 名字空间(模式)的名称。 | |
nspowner | oid | pg_authid.oid | 名字空间(模式)的所有者 |
nspacl | aclitem[] | 访问权限。 |
见如下应用示例:
#查看当前数据库public模式的创建者的名称。
postgres=# SELECT nspname,rolname FROM pg_namespace n, pg_authid a WHERE nspname = 'public' AND nspowner = a.oid;
nspname | rolname
----------+----------
public | postgres
(1 row)
九、pg_database:
该系统表存储数据库的信息。和大多数系统表不同的是,在一个集群里该表是所有数据库共享的,即每个集群只有一份pg_database拷贝,而不是每个数据库一份。
名字 | 类型 | 引用 | 描述 |
datname | name | 数据库名称。 | |
datdba | oid | pg_authid.oid | 数据库所有者,通常为创建该数据库的角色。 |
encoding | int4 | 数据库的字符编码方式。 | |
datistemplate | bool | 如果为真,此数据库可以用于CREATE DATABASE TEMPLATE子句,把新数据库创建为此数据库的克隆。 | |
datallowconn | bool | 如果为假,则没有人可以联接到这个数据库。 | |
datlastsysoid | oid | 数据库里最后一个系统OID,此值对pg_dump特别有用。 | |
datvacuumxid | xid | ||
datfrozenxid | xid | ||
dattablespace | text | pg_tablespace.oid | 该数据库的缺省表空间。在这个数据库里,所有pg_class.reltablespace为零的表都将保存在这个表空间里,特别要指出的是,所有非共享的系统表也都存放在这里。 |
datconfig | text[] | 运行时配置变量的会话缺省值。 | |
datacl | aclitem[] | 访问权限。 |
十、pg_index:
该系统表存储关于索引的一部分信息。其它的信息大多数存储在pg_class。
名字 | 类型 | 引用 | 描述 |
indexrelid | oid | pg_class.oid | 该索引在pg_class里的记录的OID。 |
indrelid | oid | pg_class.oid | 索引所在表在pg_class里的记录的OID。 |
indnatts | int2 | 索引中的字段数量(拷贝的pg_class.relnatts)。 | |
indisunique | bool | 如果为真,该索引是唯一索引。 | |
indisprimary | bool | 如果为真,该索引为该表的主键。 | |
indisclustered | bool | 如果为真,那么该表在这个索引上建了簇。 | |
indkey | int2vector | pg_attribute.attnum | 该数组的元素数量为indnatts,数组元素值表示建立这个索引时所依赖的字段编号,如1 3,表示第一个字段和第三个字段构成这个索引的键值。如果为0,则表示是表达式索引,而不是基于简单字段的索引。 |
indclass | oidvector | pg_opclass.oid | 对于构成索引键值的每个字段,这个字段都包含一个指向所使用的操作符表的OID。 |
indexprs | text | 表达式树用于那些非简单字段引用的索引属性。它是一个列表,在indkey里面的每个零条目一个元素。如果所有索引属性都是简单的引用,则为空。 | |
indpred | text | 部分索引断言的表达式树。如果不是部分索引, 则是空字串。 |
见如下应用示例:
#查看该索引所在表的名称,以及构成该索引的键值数量和具体键值的字段编号。
postgres=# SELECT indnatts,indkey,relname FROM pg_index i, pg_class c WHERE c.relname = 'testtable2' AND indrelid = c.oid;
indnatts | indkey | relname
----------+--------+------------
2 | 1 3 | testtable2
(1 row)
#查看指定表包含的索引,同时列出索引的名称。
postgres=# SELECT t.relname AS table_name, c.relname AS index_name FROM (SELECT relname,indexrelid FROM pg_index i, pg_class c WHERE c.relname = 'testtable2' AND indrelid = c.oid) t, pg_index i,pg_class c WHERE t.indexrelid = i.indexrelid AND i.indexrelid = c.oid;
table_name | index_name
------------+----------------
testtable2 | testtable2_idx
(1 row)
一、pg_tables:
该视图提供了对有关数据库中每个表的有用信息地访问。
名字 | 类型 | 引用 | 描述 |
schemaname | name | pg_namespace.nspname | 包含表的模式名字。 |
tablename | name | pg_class.relname | 表的名字。 |
tableowner | name | pg_authid.rolname | 表的所有者的名字。 |
tablespace | name | pg_tablespace.spcname | 包含表的表空间名字(如果是数据库缺省,则为 NULL)。 |
hasindexes | bool | pg_class.relhasindex | 如果表拥有(或者最近拥有)任何索引,则为真。 |
hasrules | bool | pg_class.relhasrules | 如果表存在规则,则为真。 |
hastriggers | bool | pg_class.reltriggers | 如果表有触发器,则为真。 |
二、pg_indexes:
该视图提供对数据库中每个索引的有用信息的访问。
名字 | 类型 | 引用 | 描述 |
schemaname | name | pg_namespace.nspname | 包含表和索引的模式的名字。 |
tablename | name | pg_class.relname | 索引所在表的名字。 |
indexname | name | pg_class.relname | 索引的名字。 |
tablespace | name | pg_tablespace.spcname | 包含索引的表空间名字(如果是数据库缺省,则为NULL)。 |
indexdef | text | 索引定义(一个重建的创建命令)。 |
三、pg_views:
该视图提供了对数据库里每个视图的有用信息的访问途径。
名字 | 类型 | 引用 | 描述 |
schemaname | name | pg_namespace.nspname | 包含此视图的模式名字。 |
viewname | name | pg_class.relname | 视图的名字。 |
viewowner | name | pg_authid.rolname | 视图的所有者的名字。 |
definition | text | 视图定义(一个重建的SELECT查询)。 |
四、pg_user:
该视图提供了对数据库用户的相关信息的访问。 这个视图只是pg_shadow表的公众可读的部分的视图化,但是不包含口令字段。
名字 | 类型 | 引用 | 描述 |
usename | name | 用户名。 | |
usesysid | int4 | 用户ID(用于引用这个用户的任意数字)。 | |
usecreatedb | bool | 用户是否可以创建数据库。 | |
usesuper | bool | 用户是否是一个超级用户。 | |
usecatupd | bool | 用户是否可以更新系统表。(即使超级用户也不能这么干,除非这个字段为真。) | |
passwd | text | 口令(可能加密了)。 | |
valuntil | abstime | 口令失效的时间(只用于口令认证)。 | |
useconfig | text[] | 运行时配置参数的会话缺省。 |
五、pg_roles:
该视图提供访问数据库角色有关信息的接口。这个视图只是pg_authid表的公开可读部分的视图化,同时把口令字段用空白填充。
名字 | 类型 | 引用 | 描述 |
rolname | name | 角色名。 | |
rolsuper | bool | 是否有超级用户权限的角色。 | |
rolcreaterole | bool | 是否可以创建更多角色的角色。 | |
rolcreatedb | bool | 是否可以创建数据库的角色。 | |
rolcatupdate | bool | 是否可以直接更新系统表的角色。 | |
rolcanlogin | bool | 如果为真,表示是可以登录的角色。 | |
rolpassword | text | 不是口令(总是 ********)。 | |
rolvaliduntil | timestamptz | 口令失效日期(只用于口令认证);如果没有失效期,为NULL。 | |
rolconfig | text[] | 运行时配置变量的会话缺省。 |
六、pg_rules:
该视图提供对查询重写规则的有用信息访问的接口。
名字 | 类型 | 引用 | 描述 |
schemaname | name | pg_namespace.nspname | 包含表的模式的名字。 |
tablename | name | pg_class.relname | 规则施加影响的表的名字。 |
rulename | name | pg_rewrite.rulename | 规则的名字。 |
definition | text | 规则定义(一个重新构造的创建命令)。 |
七、pg_settings:
该视图提供了对服务器运行时参数的访问。它实际上是SHOW和SET命令的另外一种方式。它还提供一些用SHOW不能直接获取的参数的访问,比如最大和最小值。
名字 | 类型 | 引用 | 描述 |
name | text | 运行时配置参数名。 | |
setting | text | 参数的当前值。 | |
category | text | 参数的逻辑组。 | |
short_desc | text | 参数的一个简短的描述。 | |
extra_desc | text | 有关参数的额外的、更详细的信息。 | |
context | text | 设置这个参数的值要求的环境。 | |
vartype | text | 参数类型(bool、integer、real和string)。 | |
source | text | 当前参数值的来源。 | |
min_val | text | 该参数允许的最小值(非数字值为NULL)。 | |
max_val | text | 该参数允许的最大值(非数字值为NULL)。 |
我们不能对pg_settings视图进行插入或者删除, 只能更新。对pg_settings中的一行进行UPDATE等效于在该命名参数上执行SET命令。这个修改值影响当前会话使用的数值。如果在一个最后退出的事务中发出了UPDATE命令,那么UPDATE命令的效果将在事务回滚之后消失。一旦包围它的事务提交,这个效果将固化,直到会话结束。
零、口令文件:
在给出其它PostgreSQL客户端命令之前,我们需要先介绍一下PostgreSQL中的口令文件。之所以在这里提前说明该文件,是因为我们在后面的示例代码中会大量应用该文件,从而保证我们的脚本能够自动化完成。换句话说,如果在客户端命令执行时没有提供该文件,PostgreSQL的所有客户端命令均会被口令输入提示中断。
在当前用户的HOME目录下,我们需要手工创建文件名为 .pgpass的口令文件,这样就可以在我们连接PostgreSQL服务器时,客户端命令自动读取该文件已获得登录时所需要的口令信息。该文件的格式如下:
hostname:port:database:username:password
以上数据是用冒号作为分隔符,总共分为五个字段,分别表示服务器主机名(IP)、服务器监听的端口号、登录访问的数据库名、登录用户名和密码,其中前四个字段都可以使用星号(*)来表示匹配任意值。见如下示例:
/> cat > .pgpass
*:5432:postgres:postgres:123456
CTRL+D
#.pgpass文件的权限必须为0600,从而防止任何全局或者同组的用户访问,否则这个文件将被忽略。
/> chmod 0600 .pgpass
在学习后面的客户端命令之前,我们需要根据自己的应用环境手工创建该文件,以便后面所有的示例代码都会用到该口令文件,这样它们就都可以以批处理的方式自动完成。
一、createdb:
创建一个新的PostgreSQL数据库。该命令的使用方式如下:
createdb [option...] [dbname] [description]
1. 命令行选项列表:
选项 | 说明 |
-D(--tablespace=tablespace) | 指定数据库的缺省表空间。 |
-e(--echo) | 回显createdb生成的命令并且把它发送到服务器。 |
-E(--encoding=encoding) | 指定用于此数据库的字符编码方式。 |
-l(--locale=locale) | 指定用于此数据库的本地化设置。 |
-O(--owner=owner) | 指定新建数据库的拥有者,如果未指定此选项,该值为当前登录的用户。 |
-T(--template=template) | 指定创建此数据库的模板数据库。 |
-h(--host=host) | 指定PostgreSQL服务器的主机名。 |
-p(--port=port) | 指定服务器的侦听端口,如不指定,则为缺省的5432。 |
-U(--username=username) | 本次操作的登录用户名,如果-O选项没有指定,此数据库的Owner将为该登录用户。 |
-w(--no-password) | 如果当前登录用户没有密码,可以指定该选项直接登录。 |
2. 应用示例:
#1. 以postgres的身份登录。(详情参照上面口令文件的内容)
/> psql
#2. 创建表空间。
postgres=# CREATE TABLESPACE my_tablespace LOCATION '/opt/PostgreSQL/9.1/mydata';
CREATE TABLESPACE
#3. 创建新数据库的owner。
postgres=# CREATE ROLE myuser LOGIN PASSWORD '123456';
CREATE ROLE
postgres=# \q
#4. 创建新数据库,其中本次连接的登录用户为postgres,新数据库的owner为myuser,表空间为my_tablespace,新数据库名为mydatabase。
/> createdb -U postgres -O myuser -D my_tablespace -e mydatabase
CREATE DATABASE mydatabase OWNER myuser TABLESPACE my_tablespace;
#5. 重新登录,通过查询系统表查看该数据库是否创建成功,以及表空间和所有者是否一致。
/> psql
postgres=# SELECT datname,rolname,spcname FROM pg_database db, pg_authid au, pg_tablespace ts WHERE datname = 'mydatabase' AND datdba = au.oid AND dattablespace = ts.oid;
datname | rolname | spcname
------------+---------+---------------
mydatabase | myuser | my_tablespace
(1 row)
二、dropdb:
删除一个现有PostgreSQL数据库。
dropdb [option...] dbname
1. 命令行选项列表:
选项 | 说明 |
-e(--echo) | 回显dropdb生成的命令并且把它发送到服务器。 |
-i(--interactive) | 在做任何破坏性动作前提示。 |
-q(--quiet) | 不显示响应。 |
-h(--host=host) | 指定PostgreSQL服务器的主机名。 |
-p(--port=port) | 指定服务器的监听端口,如不指定,则为缺省的5432。 |
-U(--username=username) | 本次操作的登录用户名。 |
-w(--no-password) | 如果当前登录用户没有密码,可以指定该选项直接登录。 |
2. 应用示例:
#以postgres的身份连接服务器,删除mydatabase数据库。
/> dropdb -U postgres -e mydatabase
DROP DATABASE mydatabase;
#通过查看系统表验证该数据库是否已经被删除。
/> psql
postgres=# SELECT count(*) FROM pg_database WHERE datname = 'mydatabase';
count
-------
0
(1 row)
三、reindexdb:
为一个指定的PostgreSQL数据库重建索引。
reindexdb [connection-option...] [--table | -t table ] [--index | -i index ] [dbname]
reindexdb [connection-option...] [--all | -a]
reindexdb [connection-option...] [--system | -s] [dbname]
1. 命令行选项列表:
选项 | 说明 |
-a(-all) | 重建整个数据库的索引。 |
-e(--echo) | 回显reindexdb生成的命令并且把它发送到服务器。 |
-i(--index=index) | 仅重建指定的索引。 |
-q(--quiet) | 不显示响应。 |
-s(--system) | 重建数据库系统表的索引。 |
-t(--table=table) | 仅重建指定数据表的索引。 |
-h(--host=host) | 指定PostgreSQL服务器的主机名。 |
-p(--port=port) | 指定服务器的监听端口,如不指定,则为缺省的5432。 |
-U(--username=username) | 本次操作的登录用户名。 |
-w(--no-password) | 如果当前登录用户没有密码,可以指定该选项直接登录。 |
2. 应用示例:
#仅重建数据表testtable上的全部索引。
/> reindexdb -t testtable -e -U postgres postgres
REINDEX TABLE testtable;
#仅重建指定索引testtable_idx
/> reindexdb -i testtable_idx -e -U postgres postgres
REINDEX INDEX testtable_idx;
#重建指定数据库mydatabase的全部索引。
/> reindexdb mydatabase
四、vacuumdb:
收集垃圾并且分析一个PostgreSQL数据库。
vacuumdb [-options] [--full | -f] [--verbose | -v] [--analyze | -z] [-t table [(column [,...])]] [dbname]
vacuumdb [-options] [--all | -a] [--full | -f] [--verbose | -v] [--analyze | -z]
1. 命令行选项列表:
选项 | 说明 |
-a(--all) | 清理所有数据库。 |
-e(--echo) | 回显vacuumdb生成的命令并且把它发送到服务器。 |
-f(--full) | 执行完全清理。 |
-q(--quiet) | 不显示响应。 |
-t table [(column[,...])] | 仅仅清理或分析指定的数据表,字段名只是在与--analyze选项联合使用时才需要声明。 |
-v(--verbose) | 在处理过程中打印详细信息。 |
-z(--analyze) | 计算用于规划器的统计值。 |
-h(--host=host) | 指定PostgreSQL服务器的主机名。 |
-p(--port=port) | 指定服务器的监听端口,如不指定,则为缺省的5432。 |
-U(--username=username) | 本次操作的登录用户名。 |
-w(--no-password) | 如果当前登录用户没有密码,可以指定该选项直接登录。 |
2. 应用示例:
#清理整个数据库mydatabase。
/> vacuumdb -e mydatabase
VACUUM;
#清理并分析postgres数据库中的testtable表。
/> vacuumdb -e --analyze --table 'testtable' postgres
VACUUM ANALYZE testtable;
#清理并分析postgres数据库中的testtable表的i字段。
/> vacuumdb -e --analyze -t 'testtable(i)' postgres
VACUUM ANALYZE testtable(i);
五、createuser:
定义一个新的PostgreSQL用户帐户,需要说明的是只有超级用户或者是带有CREATEROLE权限的用户才可以执行该命令。如果希望创建的是超级用户,那么只能以超级用户的身份执行该命令,换句话说,带有CREATEROLE权限的普通用户无法创建超级用户。该命令的使用方式如下:
createuser [option...] [username]
1. 命令行选项列表:
选项 | 说明 |
-c number | 设置新创建用户的最大连接数,缺省为没有限制。 |
-d(--createdb) | 允许该新建用户创建数据库。 |
-D(--no-createdb) | 禁止该新建用户创建数据库。 |
-e(--echo) | 回显createuser生成的命令并且把它发送到服务器。 |
-E(--encrypted) | 对保存在数据库里的用户口令加密。如果没有声明, 则使用缺省值。 |
-i(--inherit) | 新创建的角色将自动继承它的组角色的权限。 |
-I(--no-inherit) | 新创建的角色不会自动继承它的组角色的权限。 |
-l(--login) | 新角色将被授予登录权限,该选项为缺省选项。 |
-L(--no-login) | 新角色没有被授予登录权限。 |
-N(--unencrypted) | 不对保存在数据库里的用户口令加密。如果没有声明, 则使用缺省值。 |
-P(--pwprompt) | 如果给出该选项,在创建用户时将提示设置口令。 |
-r(--createrole) | 新角色被授予创建数据库的权限。 |
-R(--no-createrole) | 新角色没有被授予创建数据库的权限。 |
-s(--superuser) | 新角色为超级用户。 |
-S(--no-superuser) | 新角色不是超级用户。 |
-h(--host=host) | 指定PostgreSQL服务器的主机名。 |
-p(--port=port) | 指定服务器的监听端口,如不指定,则为缺省的5432。 |
-U(--username=username) | 本次操作的登录用户名。 |
-w(--no-password) | 如果当前登录用户没有密码,可以指定该选项直接登录。 |
2. 应用示例:
# 对于有些没有缺省设置的选项,如-(d/D)、-(s/S)和-(r/R),如果在命令行中没有直接指定,那么在执行该命令是将会给出提示信息。
# 需要注意的是该提示将会挂起自动化脚本,直到输入后命令才会继续执行。
/> createuser -U postgres myuser
Shall the new role be a superuser? (y/n) n
Shall the new role be allowed to create databases? (y/n) y
Shall the new role be allowed to create more new roles? (y/n) n
CREATE ROLE myuser NOSUPERUSER CREATEDB NOCREATEROLE INHERIT LOGIN;
# 通过psql登录后查看系统视图,以验证该用户是否成功创建,以及新角色的权限是否正确。
/> psql
postgres=# SELECT rolname,rolsuper,rolinherit,rolcreaterole,rolcreatedb,rolcanlogin FROM pg_roles WHERE rolname = 'myuser';
rolname | rolsuper | rolinherit | rolcreaterole | rolcreatedb | rolcanlogin
---------+----------+------------+---------------+-------------+-------------
myuser | f | t | f | t | t
(1 row)
# 为了保证自动化脚本不会被该命令的提示挂起,我们需要在执行该命令时指定所有没有缺省值的选项。
/> createuser -U postgres -e -S -D -R myuser2
CREATE ROLE myuser2 NOSUPERUSER NOCREATEDB NOCREATEROLE INHERIT LOGIN;
# 我们可以在创建用户时即刻指定该用户的密码,该操作由-P选项完成,然而这样的用法一定会挂起自动化脚本,
# 因此我们可以采用一种折中的办法,即在创建用户时不指定密码,在自动化脚本执行成功后再手工该用户的密码。
/> createuser -P -s -e myuser3
Enter password for new role:
Enter it again:
CREATE ROLE myuser3 PASSWORD 'md5fe54c4f3129f2a766f53e4f4c9d2a698' SUPERUSER CREATEDB CREATEROLE INHERIT LOGIN;
六、dropuser:
删除一个PostgreSQL用户帐户,需要说明的是只有超级用户或带有CREATEROLE权限的用户可以执行该命令,如果要删除超级用户,只能通过超级用户的身份执行该命令。该命令的使用方式如下:
dropuser [option...] [username]
1. 命令行选项列表:
选项 | 说明 |
-e(--echo) | 回显dropuser生成的命令并且把它发送到服务器。 |
-i(--interactive) | 在做任何破坏性动作前提示。 |
-h(--host=host) | 指定PostgreSQL服务器的主机名。 |
-p(--port=port) | 指定服务器的监听端口,如不指定,则为缺省的5432。 |
-U(--username=username) | 本次操作的登录用户名。 |
-w(--no-password) | 如果当前登录用户没有密码,可以指定该选项直接登录。 |
2. 应用示例:
# 直接删除指定用户。
/> dropuser -e myuser3
DROP ROLE myuser3;
# 在删除指定用户时,该命令会给出提示信息,以免误操作。
/> dropuser -e -i myuser2
Role "myuser2" will be permanently removed.
Are you sure? (y/n) y
DROP ROLE myuser2;
七、pg_dump:
pg_dump是一个用于备份PostgreSQL数据库的工具。它甚至可以在数据库正在并发使用时进行完整一致的备份,而不会阻塞其它用户对数据库的访问。该工具生成的转储格式可以分为两种,脚本和归档文件。其中脚本格式是包含许多SQL命令的纯文本格式,这些SQL命令可以用于重建该数据库并将之恢复到生成此脚本时的状态,该操作需要使用psql来完成。至于归档格式,如果需要重建数据库就必须和pg_restore工具一起使用。在重建过程中,可以对恢复的对象进行选择,甚至可以在恢复之前对需要恢复的条目进行重新排序。该命令的使用方式如下:
pg_dump [option...] [dbname]
1. 命令行选项列表:
选项 | 说明 |
-a(--data-only) | 只输出数据,不输出模式(数据对象的定义)。这个选项只是对纯文本格式有意义。对于归档格式,你可以在调用pg_restore时指定选项。 |
-b(--blobs) | 在dump中包含大对象。 |
-c(--clean) | 在输出创建数据库对象的SQL命令之前,先输出删除该数据库对象的SQL命令。这个选项只是对纯文本格式有意义。对于归档格式,你可以在调用 pg_restore时指定选项。 |
-C(--create) | 先输出创建数据库的命令,之后再重新连接新创建的数据库。对于此种格式的脚本,在运行之前是和哪个数据库进行连接就不这么重要了。这个选项只是对纯文本格式有意义。对于归档格式,你可以在调用pg_restore时指定选项。 |
-Eencoding | 以指定的字符集创建该dump文件。 |
-ffile | 输出到指定文件,如果没有该选项,则输出到标准输出。 |
-Fformat | p(plain): 纯文本格式的SQL脚本文件(缺省)。c(custom): 输出适合于pg_restore的自定义归档格式。 这是最灵活的格式,它允许对装载的数据和对象定义进行重新排列。这个格式缺省的时候是压缩的。t(tar): 输出适合于pg_restore的tar归档文件。使用这个归档允许在恢复数据库时重新排序和/或把数据库对象排除在外。同i时也可能可以在恢复的时候限制对哪些数据进行恢复。 |
-n schema | 只转储schema的内容。如果没有声明该选项,目标数据库中的所有非系统模式都会被转储。该选项也可以被多次指定,以指定不同pattern的模式。 |
-Nschema | 不转储匹配schema的内容,其他规则和-n一致。 |
-o(--oids) | 作为数据的一部分,为每个表都输出对象标识(OID)。 |
-O(--no-owner) | 不输出设置对象所有权的SQL命令。 |
-s(--schema-only) | 只输出对象定义(模式),不输出数据。 |
-Susername | 指定关闭触发器时需要用到的超级用户名。它只有在使用--disable-triggers的时候才有关系。 |
-ttable | 只输出表的数据。很可能在不同模式里面有多个同名表,如果这样,那么所有匹配的表都将被转储。通过多次指定该参数,可以一次转储多张表。这里还可以指定和psql一样的pattern,以便匹配更多的表。(关于pattern,基本的使用方式是可以将它视为unix的通配符,即*表示任意字符,?表示任意单个字符,.(dot)表示schema和object之间的分隔符,如a*.b*,表示以a开头的schema和以b开头的数据库对象。如果没有.(dot),将只是表示数据库对象。这里也可以使用基本的正则表达式,如[0-9]表示数字。) |
-Ttable | 排除指定的表,其他规则和-t选项一致。 |
-x(--no-privileges) | 不导出访问权限信息(grant/revoke命令)。 |
-Z0..9 | 声明在那些支持压缩的格式中使用的压缩级别。 (目前只有自定义格式支持压缩) |
--column-inserts | 导出数据用insert into table_name(columns_list) values(values_list)命令表示,这样的操作相对其它操作而言是比较慢的,但是在特殊情况下,如数据表字段的位置有可能发生变化或有新的字段插入到原有字段列表的中间等。由于columns_list被明确指定,因此在导入时不会出现数据被导入到错误字段的问题。 |
--inserts | 导出的数据用insert命令表示,而不是copy命令。即便使用insert要比copy慢一些,但是对于今后导入到其他非PostgreSQL的数据库是比较有意义的。 |
--no-tablespaces | 不输出设置表空间的命令,如果带有这个选项,所有的对象都将恢复到执行pg_restore时的缺省表空间中。 |
--no-unlogged-table-data | 对于不计入日志(unlogged)的数据表,不会导出它的数据,至于是否导出其Schema信息,需要依赖其他的选项而定。 |
-h(--host=host) | 指定PostgreSQL服务器的主机名。 |
-p(--port=port) | 指定服务器的侦听端口,如不指定,则为缺省的5432。 |
-U(--username=username) | 本次操作的登录用户名,如果-O选项没有指定,此数据库的Owner将为该登录用户。 |
-w(--no-password) | 如果当前登录用户没有密码,可以指定该选项直接登录。 |
2. 应用示例:
# -h: PostgreSQL服务器的主机为192.168.149.137。
# -U: 登录用户为postgres。
# -t: 导出表名以test开头的数据表,如testtable。
# -a: 仅仅导出数据,不导出对象的schema信息。
# -f: 输出文件是当前目录下的my_dump.sql
# mydatabase是此次操作的目标数据库。
/> pg_dump -h 192.168.149.137 -U postgres -t test* -a -f ./my_dump.sql mydatabase
#-c: 先输出删除数据库对象的SQL命令,在输出创建数据库对象的SQL命令,这对于部署干净的初始系统或是搭建测试环境都非常方便。
/> pg_dump -h 192.168.220.136 -U postgres -c -f ./my_dump.sql mydatabase
#导出mydatabase数据库的信息。在通过psql命令导入时可以重新指定数据库,如:/> psql -d newdb -f my_dump.sql
/> pg_dump -h 192.168.220.136 -U postgres -f ./my_dump.sql mydatabase
#导出模式为my_schema和以test开头的数据库对象名,但是不包括my_schema.employee_log对象。
/> pg_dump -t 'my_schema.test*' -T my_schema.employee_log mydatabase > my_dump.sql
#导出east和west模式下的所有数据库对象。下面两个命令是等同的,只是后者使用了正则。
/> pg_dump -n 'east' -n 'west' mydatabase -f my_dump.sql
/> pg_dump -n '(east|west)' mydatabase -f my_dump.sql
八、pg_restore:
pg_restore用于恢复pg_dump导出的任何非纯文本格式的文件,它将数据库重建成保存它时的状态。对于归档格式的文件,pg_restore可以进行有选择的恢复,甚至也可以在恢复前重新排列数据的顺序。
pg_restore可以在两种模式下操作。如果指定数据库,归档将直接恢复到该数据库。否则,必须先手工创建数据库,之后再通过pg_restore恢复数据到该新建的数据库中。该命令的使用方式如下:
pg_restore [option...] [filename]
1. 命令行选项列表:
选项 | 说明 |
filename | 指定要恢复的备份文件,如果没有声明,则使用标准输入。 |
-a(--data-only) | 只恢复数据,而不恢复表模式(数据对象定义)。 |
-c(--clean) | 创建数据库对象前先清理(删除)它们。 |
-C(--create) | 在恢复数据库之前先创建它。(在使用该选项时,数据库名需要由-d选项指定,该选项只是执行最基本的CREATE DATABASE命令。需要说明的是,归档文件中所有的数据都将恢复到归档文件里指定的数据库中)。 |
-ddbname | 与数据库dbname建立连接并且直接恢复数据到该数据库中。 |
-e(--exit-on-error) | 如果在向数据库发送SQL命令的时候遇到错误,则退出。缺省是继续执行并且在恢复结束时显示一个错误计数。 |
-Fformat | 指定备份文件的格式。由于pg_restore会自动判断格式,因此指定格式并不是必须的。如果指定,它可以是以下格式之一:t(tar): 使用该格式允许在恢复数据库时重新排序和/或把表模式信息排除出去,同时还可能在恢复时限制装载的数据。 c(custom):该格式是来自pg_dump的自定义格式。这是最灵活的格式,因为它允许重新对数据排序,也允许重载表模式信息,缺省情况下这个格式是压缩的。 |
-I index | 只恢复指定的索引。 |
-l(--list) | 列出备份中的内容,这个操作的输出可以作为-L选项的输入。注意,如果过滤选项-n或-t连同-l选项一起使用的话,他们也将限制列出的条目。 |
-L list-file | 仅恢复在list-file中列出的条目,恢复的顺序为各个条目在该文件中出现的顺序,你也可以手工编辑该文件,并重新排列这些条目的位置,之后再进行恢复操作,其中以分号(;)开头的行为注释行,注释行不会被导入。 |
-n namespace | 仅恢复指定模式(Schema)的数据库对象。该选项可以和-t选项联合使用,以恢复指定的数据对象。 |
-O(--no-owner) | 不输出设置对象所有权的SQL命令。 |
-Pfunction-name(argtype [, ...]) | 只恢复指定的命名函数。该名称应该和转储的内容列表中的完全一致。 |
-s(--schema-only) | 只恢复表结构(数据定义)。不恢复数据,序列值将重置。 |
-Susername | 指定关闭触发器时需要用到的超级用户名。它只有在使用--disable-triggers的时候才有关系。 |
-t table | 只恢复指定表的Schema和/或数据,该选项也可以连同-n选项指定模式。 |
-x(--no-privileges) | 不恢复访问权限信息(grant/revoke命令)。 |
-1(--single-transaction) | 在一个单一事物中执行恢复命令。这个选项隐含包括了--exit-on-error选项。 |
--no-tablespaces | 不输出设置表空间的命令,如果带有这个选项,所有的对象都将恢复到执行pg_restore时的缺省表空间中。 |
--no-data-for-failed-tables | 缺省情况下,即使创建表失败了,如该表已经存在,数据加载的操作也不会停止,这样的结果就是很容易导致大量的重复数据被插入到该表中。如果带有该选项,那么一旦出现针对该表的任何错误,对该数据表的加载将被忽略。 |
--role=rolename | 以指定的角色名执行restore的操作。通常而言,如果连接角色没有足够的权限用于本次恢复操作,那么就可以利用该选项在建立连接之后再切换到有足够权限的角色。 |
-h(--host=host) | 指定PostgreSQL服务器的主机名。 |
-p(--port=port) | 指定服务器的侦听端口,如不指定,则为缺省的5432。 |
-U(--username=username) | 本次操作的登录用户名,如果-O选项没有指定,此数据库的Owner将为该登录用户。 |
-w(--no-password) | 如果当前登录用户没有密码,可以指定该选项直接登录。 |
2. 应用示例:
#先通过createdb命令,以myuser用户的身份登录,创建带恢复的数据newdb
/> createdb -U myuser newdb
#用pg_restore命令的-l选项导出my_dump.dat备份文件中导出数据库对象的明细列表。
/> pg_restore -l my_dump.dat > db.list
/> cat db.list
2; 145344 TABLE species postgres
4; 145359 TABLE nt_header postgres
6; 145402 TABLE species_records postgres
8; 145416 TABLE ss_old postgres
10; 145433 TABLE map_resolutions postgres
#将以上列表文件中的内容修改为以下形式。
#主要的修改是注释掉编号为2、4和8的三个数据库对象,同时编号10的对象放到该文件的头部,这样在基于该列表
#文件导入时,2、4和8等三个对象将不会被导入,在恢复的过程中将先导入编号为10的对象的数据,再导入对象6的数据。
/> cat new_db.list
10; 145433 TABLE map_resolutions postgres
;2; 145344 TABLE species postgres
;4; 145359 TABLE nt_header postgres
6; 145402 TABLE species_records postgres
;8; 145416 TABLE ss_old postgres
#恢复时指定的数据库是newdb,导入哪些数据库对象和导入顺序将会按照new_db.list文件中提示的规则导入。
/> pg_restore -d newdb -L new_db.list my_dump.dat
九、psql:
PostgreSQL的交互终端,等同于Oracle中的sqlplus。
1. 常用命令行选项列表:
选项 | 说明 |
-c command | 指定psql执行一条SQL命令command(用双引号括起),执行后退出。 |
-d dbname | 待连接的数据库名称。 |
-E | 回显由\d和其他反斜杠命令生成的实际查询。 |
-f filename | 使用filename文件中的数据作为命令输入源,而不是交互式读入查询。在处理完文件后,psql结束并退出。 |
-h hostname | 声明正在运行服务器的主机名 |
-l | 列出所有可用的数据库,然后退出。 |
-L filename | 除了正常的输出源之外,把所有查询记录输出到文件filename。 |
-o filename | 将所有查询重定向输出到文件filename。 |
-p port | 指定PostgreSQL服务器的监听端口。 |
-q --quiet | 让psql安静地执行所处理的任务。缺省时psql将输出打印欢迎和许多其他信息。 |
-t --tuples-only | 关闭打印列名称和结果行计数脚注等信息。 |
-U username | 以用户username代替缺省用户与数据库建立连接。 |
2. 命令行选项应用示例:
#-d: 指定连接的数据库。
#-U: 指定连接的用户。
#-c: 后面的SQL语句是本次操作需要执行的命令。
/> psql -d posgres -U postgres -c "select * from testtable"
i
---
1
2
3
5
(4 rows)
#-t: 没有输出上面输出结果中的字段标题信息和行数统计信息。
#-q:该选项和-t选项联合使用,非常有利于自动化脚本。如:
# select 'copy ' || tablename || ' to ' || tablename || '.sql' from pg_tables
#由以上sql语句生成的结果集,在重定向到输出文件后,可以作为下一次psql的输入执行。
/> psql -t -q -c "select * from testtable"
1
2
3
5
#-l: 列出当前系统中可用的数据库。
/> psql -l
List of databases
Name | Owner | Encoding | Collation | Ctype | Access privileges
------------+----------+----------+-------------+-------------+-----------------------
mydatabase | myuser | UTF8 | zh_CN.UTF-8 | zh_CN.UTF-8 |
postgres | postgres | UTF8 | zh_CN.UTF-8 | zh_CN.UTF-8 |
... ...
(4 rows)
#-o: 将查询语句的数据结果输出到指定文件。
/> psql -c "select * from testtable" -o out
/> cat out
i
---
1
2
3
5
(4 rows)
3. 内置命令列表:
psql内置命令的格式为反斜杠后面紧跟一个命令动词,之后是任意参数。参数与命令动词以及其他参数之间可以用空白符隔开,如果参数里面包含空白符,该参数必须用单引号括起,如果参数内包含单引号,则需要用反斜杠进行转义,此外单引号内的参数还支持类似C语言printf函数所支持的转义关键字,如\t、\n等。
命令 | 说明 |
\a | 如果目前的表输出格式是不对齐的,切换成对齐的。如果是对齐的,则切换成不对齐。 |
\cd [directory] | 把当前工作目录切换到directory。没有参数则切换到当前用户的主目录。 |
\C [title] | 为查询结果添加表头(title),如果没有参数则取消当前的表头。 |
\c[dbname[username] ] | 连接新的数据库,同时断开当前连接。如果dbname参数为-,表示仍然连接当前数据库。如果忽略username,则表示继续使用当前的用户名。 |
\copy | 其参数类似于SQL copy,功能则几乎等同于SQL copy,一个重要的差别是该内置命令可以将表的内容导出到本地,或者是从本地导入到数据库指定的表,而SQL copy则是将表中的数据导出到服务器的某个文件,或者是从服务器的文件导入到数据表。由此可见,SQL copy的效率要优于该内置命令。 |
\d [pattern] | 显示和pattern匹配的数据库对象,如表、视图、索引或者序列。显示所有列,它们的类型,表空间(如果不是缺省的)和任何特殊属性。 |
\db [pattern] | 列出所有可用的表空间。如果声明了pattern, 那么只显示那些匹配模式的表空间。 |
\db+ [pattern] | 和上一个命令相比,还会新增显示每个表空间的权限信息。 |
\df [pattern] | 列出所有可用函数,以及它们的参数和返回的数据类型。如果声明了pattern,那么只显示匹配(正则表达式)的函数。 |
\df+ [pattern] | 和上一个命令相比,还会新增显示每个函数的附加信息,包括语言和描述。 |
\distvS [pattern] | 这不是一个单独命令名称:字母 i、s、t、v、S 分别代表索引(index)、序列(sequence)、表(table)、视图(view)和系统表(system table)。你可以以任意顺序声明部分或者所有这些字母获得这些对象的一个列表。 |
\dn [pattern] | 列出所有可用模式。如果声明了pattern,那么只列出匹配模式的模式名。 |
\dn+ [pattern] | 和上一个命令相比,还会新增显示每个对象的权限和注释。 |
\dp [pattern] | 生成一列可用的表和它们相关的权限。如果声明了pattern, 那么只列出名字可以匹配模式的表。 |
\dT [pattern] | 列出所有数据类型或只显示那些匹配pattern的。 |
\du [pattern] | 列出所有已配置用户或者只列出那些匹配pattern的用户。 |
\echotext [ ... ] | 向标准输出打印参数,用一个空格分隔并且最后跟着一个新行。如:\echo `date` |
\g[{filename ||command}] | 把当前的查询结果缓冲区的内容发送给服务器并且把查询的输出存储到可选的filename或者把输出定向到一个独立的在执行 command的Unix shell。 |
\ifilename | 从文件filename中读取并把其内容当作从键盘输入的那样执行查询。 |
\l | 列出服务器上所有数据库的名字和它们的所有者以及字符集编码。 |
\o[{filename ||command}] | 把后面的查询结果保存到文件filename里或者把后面的查询结果定向到一个独立的shell command。 |
\p | 打印当前查询缓冲区到标准输出。 |
\q | 退出psql程序。 |
\r | 重置(清空)查询缓冲区。 |
\s [filename] | 将命令行历史打印出或是存放到filename。如果省略filename,历史将输出到标准输出。 |
\t | 切换是否输出列/字段名的信息头和行记数脚注。 |
\w{filename ||command} | 将当前查询缓冲区输出到文件filename或者定向到Unix命令command。 |
\z [pattern] | 生成一个带有访问权限列表的数据库中所有表,视图和序列的列表。如果给出任何pattern,则被当成一个规则表达式,只显示匹配的表,视图和序列。 |
\! [command] | 返回到一个独立的Unix shell或者执行Unix命令command。参数不会被进一步解释,shell将看到全部参数。 |
4. 内置命令应用示例:
在psql中,大部分的内置命令都比较易于理解,因此这里只是给出几个我个人认为相对容易混淆的命令。
# \c: 其中横线(-)表示仍然连接当前数据库,myuser是新的用户名。
postgres=# \c - myuser
Password for user myuser:
postgres=> SELECT user;
current_user
--------------
myuser
(1 row)
# 执行任意SQL语句。
postgres=# SELECT * FROM testtable WHERE i = 2;
i
---
2
(1 row)
# \g 命令会将上一个SQL命令的结果输出到指定文件。
postgres=# \g my_file_for_command_g
postgres=# \! cat my_file_for_command_g
i
---
2
(1 row)
# \g 命令会将上一个SQL命令的结果从管道输出到指定的Shell命令,如cat。
postgres=# \g | cat
i
---
2
(1 row)
# \p 打印上一个SQL命令。
postgres=# \p
SELECT * FROM testtable WHERE i = 2;
# \w 将上一个SQL命令输出到指定的文件。
postgres=# \w my_file_for_option_w
postgres=# \! cat my_file_for_option_w
SELECT * FROM testtable WHERE i = 2;
# \o 和\g相反,该命令会将后面psql命令的输出结果输出到指定的文件,直到遇到下一个独立的\o,
# 此后的命令结果将不再输出到该文件。
postgres=# \o my_file_for_option_o
postgres=# SELECT * FROM testtable WHERE i = 1;
# 终止后面的命令结果也输出到my_file_for_option_o文件中。
postgres=# \o
postgres=# \! cat my_file_for_option_o
i
---
1
(1 row)
事实上之前有很长一段时间都在纠结是否有必要好好学习它,但是始终都没有一个很好的理由说服自己。甚至是直到这个项目最终决定选用PostgreSQL时,我都没有真正意识到学习它的价值,当时只是想反正和其它数据库差不多,能用就行了。然而有一天晚上,自己也不知道为什么,躺在床上开始回想整个项目的实施过程,想着想着就想到了数据库选型这一问题上了。事情是这样的,最初客户将他们的产品目标定位为主打中型规模,同时也要在一定程度上支持小型规模。鉴于此,我们为他们提供的方案是中型规模的数据库选用Oracle,小型规模的选定MySQL,在经过多轮商谈之后这个方案通过了。然而随着项目的深入,客户突然有一天提出,由于成本和市场推广等问题,该产品的数据库部分需要进行一定的调整,调整的结果是中型规模可以同时支持Oracle和MySQL,而小型规模则要同时支持MySQL和PostgreSQL,原因非常简单,PostgreSQL是纯免费的数据库产品。听到这个消息之后,我当时就非常恼火,因为当初为了保证运行时效率(国标),我们的数据库访问层是完全基于数据库供应商提供的原始C接口开发的,甚至都没有考虑ODBC提供的原始C接口,以防在转换中失去效率,或是ODBC本身为了强调通用性而不得不牺牲某些数据库的优化特征,如批量插入、批量读取等。最后的结果显而易见,客户就是上帝,上帝的意见就是真理,这样我们就不得不基于现有的访问层接口又重新开发了一套支持PostgreSQL原生C接口的驱动。然而随着对PostgreSQL的不断学习,对它的了解也在逐步加深,后来发现它的功能还是非常强大的,特别是对GIS空间数据的支持就更加的吸引我了。于是就在脑子里为MySQL和PostgreSQL做了一个简单的对比和分析,最后得出一个结论,相比MySQL,PostgreSQL并没有什么刚性的缺点,但是它的纯免费特征确实是MySQL无法比拟的。它完全可以成为软件产品解决方案中非常重要的一环,特别是针对很多中低端用户,不仅在使用中不会感到任何差异,而且还能节省一部分额外的费用,这对于产品的市场推广是非常有帮助的。想到这里便决定起而行之,花上一段时间好好的学习和研究它,于是就有了这个系列的博客。
好了,闲淡扯的差不多了,让我们还是言归正传吧!
PostgreSQL学习手册(数据表)
http://www.cnblogs.com/stephen-liu74/archive/2011/12/16/2290803.html
一、表的定义
二、系统字段
三、表的修改
四、权限
PostgreSQL学习手册(模式Schema)
http://www.cnblogs.com/stephen-liu74/archive/2011/12/18/2291526.html
PostgreSQL学习手册(表的继承和分区)
http://www.cnblogs.com/stephen-liu74/archive/2011/12/18/2291814.html
一、表的继承
二、分区表
PostgreSQL学习手册(常用数据类型)
http://www.cnblogs.com/stephen-liu74/archive/2011/12/19/2293602.html
一、数值类型
二、字符类型
三、日期/时间类型
四、布尔类型
五、位串类型
六、数组
七、复合类型
PostgreSQL学习手册(函数和操作符<一>)
http://www.cnblogs.com/stephen-liu74/archive/2011/12/19/2294071.html
一、逻辑操作符
二、比较操作符
三、数学函数和操作符
四、字符串函数和操作符
五、位串函数和操作符
PostgreSQL学习手册(函数和操作符<二>)
http://www.cnblogs.com/stephen-liu74/archive/2011/12/20/2294643.html
六、模式匹配
七、数据类型格式化函数
八、时间/日期函数和操作符
PostgreSQL学习手册(函数和操作符<三>)
http://www.cnblogs.com/stephen-liu74/archive/2011/12/20/2295273.html
九、序列操作函数
十、条件表达式
十一、数组函数和操作符
十二、系统信息函数
十三、系统管理函数
PostgreSQL学习手册(索引)
http://www.cnblogs.com/stephen-liu74/archive/2011/12/22/2298182.html
一、索引的类型
二、复合索引
三、组合多个索引
四、唯一索引
五、表达式索引
六、部分索引
七、检查索引的使用
PostgreSQL学习手册(事物隔离)
http://www.cnblogs.com/stephen-liu74/archive/2011/12/25/2300863.html
PostgreSQL学习手册(性能提升技巧)
http://www.cnblogs.com/stephen-liu74/archive/2011/12/25/2301064.html
一、使用EXPLAIN
二、批量数据插入
PostgreSQL学习手册(服务器配置)
http://www.cnblogs.com/stephen-liu74/archive/2011/12/26/2302241.html
一、服务器进程的启动和关闭
二、服务器配置
三、内存相关的参数配置
PostgreSQL学习手册(角色和权限)
http://www.cnblogs.com/stephen-liu74/archive/2011/12/26/2302639.html
一、数据库角色
二、角色属性
三、权限
四、角色成员
PostgreSQL学习手册(数据库管理)
http://www.cnblogs.com/stephen-liu74/archive/2011/12/27/2303510.html
一、概述
二、创建数据库
三、修改数据库配置
四、删除数据库
五、表空间
PostgreSQL学习手册(数据库维护)
http://www.cnblogs.com/stephen-liu74/archive/2011/12/27/2304155.html
一、恢复磁盘空间
二、更新规划器统计
三、VACUUM和ANALYZE的示例
四、定期重建索引
五、观察磁盘使用情况
PostgreSQL学习手册(系统表)
http://www.cnblogs.com/stephen-liu74/archive/2011/12/28/2305415.html
一、pg_class
二、pg_attribute
三、pg_attrdef
四、pg_authid
五、pg_auth_members
六、pg_constraint
七、pg_tablespace
八、pg_namespace
九、pg_database
十、pg_index
PostgreSQL学习手册(系统视图)
http://www.cnblogs.com/stephen-liu74/archive/2011/12/29/2306115.html
一、pg_tables
二、pg_indexes
三、pg_views
四、pg_user
五、pg_roles
六、pg_rules
七、pg_settings
PostgreSQL学习手册(客户端命令<一>)
http://www.cnblogs.com/stephen-liu74/archive/2011/12/29/2306493.html
零、口令文件
一、createdb
二、dropdb
三、reindexdb
四、vacuumdb
五、createuser
六、dropuser
PostgreSQL学习手册(客户端命令<二>)
http://www.cnblogs.com/stephen-liu74/archive/2011/12/30/2307813.html
七、pg_dump
八、pg_restore
九、psql
PostgreSQL学习手册(SQL语言函数)
http://www.cnblogs.com/stephen-liu74/archive/2012/01/04/2312194.html
一、基本概念
二、基本类型
三、复合类型
四、带输出参数的函数
五、返回结果作为表数据源
六、返回集合的SQL函数
七、多态的SQL函数
八、函数重载
PostgreSQL学习手册(PL/pgSQL过程语言)
http://www.cnblogs.com/stephen-liu74/archive/2012/01/05/2312759.html
一、概述
二、PL/pgSQL的结构
三、声明
四、基本语句
五、控制结构
六、游标
七、错误和消息
如果您觉得这个系列的博客可以让您有所收获,请保持持续的关注。
如果您发现博客中有明显的纰漏,欢迎指正。
如果您有意进行技术上的交流,可以通过邮件共同探讨([email protected])。
如果您有更好的建议或更好的实现方式,敬请赐教。
如果您已经是我的关注者,希望随后发布的Lua系列不会让您失望。
I am Stephen Liu.