参考文档: PostgreSQL 9 Administration Cookbook (第二版)中文版 第七章 数据库管理

-- 事务,要么全部成功,要么全部失败

BEGIN;
command 1;
command 2;
command 3;
COMMIT;

-- 在psql客户端,可以使用-1 ,--single-transaction 来表示使用事务

bash $ psql -1 -f myscript.sql
bash $ psql --single-transaction -f myscript.sql

-- psql的帮助,提示是-1(数字1)

General options:
  -c, --command=COMMAND    run only single command (SQL or internal) and exit
  -d, --dbname=DBNAME      database name to connect to (default: "postgres")
  -f, --file=FILENAME      execute commands from file, then exit
  -l, --list               list available databases, then exit
  -v, --set=, --variable=NAME=VALUE
                           set psql variable NAME to VALUE
                           (e.g., -v ON_ERROR_STOP=1)
  -V, --version            output version information, then exit
  -X, --no-psqlrc          do not read startup file (~/.psqlrc)
  -1 ("one"), --single-transaction
                           execute as a single transaction (if non-interactive)
  -?, --help[=options]     show this help, then exit
      --help=commands      list backslash commands, then exit
      --help=variables     list special variables, then exit

-- if exists 和 or replace

DROP VIEW IF EXISTS cust_view;
CREATE OR REPLACE VIEW cust_view AS SELECT * FROM cust; 
drop index if exists  idx_1;

-- 如果改变函数,或者视图输出的定义,则即使有or replace也不行。需要先drop,再create

CREATE OR REPLACE VIEW emp_view AS SELECT empname as title1 FROM emp;
CREATE OR REPLACE VIEW emp_view AS SELECT empname as title2 FROM emp;

mydb=# CREATE OR REPLACE VIEW emp_view AS SELECT empname as title1 FROM emp;
CREATE VIEW
mydb=# CREATE OR REPLACE VIEW emp_view AS SELECT empname as title2 FROM emp;
ERROR:  cannot change name of view column "title1" to "title2"
mydb=#

-- postgres不支持嵌套事务

mydb=# begin;
BEGIN
mydb=# create table a(x int);
CREATE TABLE
mydb=# begin;    -- 这个语句,其实被pg忽略掉了 
WARNING:  there is already a transaction in progress
BEGIN
mydb=# create table b(x int);
CREATE TABLE
mydb=# commit;
COMMIT
mydb=# rollback;
WARNING:  there is no transaction in progress
ROLLBACK
mydb=#

-- 不能包含在事务中的语句

CREATE DATABASE / DROP DATABASE
CREATE TABLESPACE / DROP TABLESPACE
CREATE INDEX CONCURRENTLY
VACUUM
REINDEX DATABASE / REINDEX  SYSTEM 
CLUSTER

-- psql脚本工具的默认模式是当遇到错误时继续处理,这样的目的是为了历史兼容性。

-- 编辑一个脚本,然后执行,会发现,即使错误了,也一直执行下去

-bash-4.2$ more /tmp/test.sql 
mistake1;
mistake2;
mistake3;
-bash-4.2$ 

-bash-4.2$ psql -f '/tmp/test.sql'
psql.bin:/tmp/test.sql:1: ERROR:  syntax error at or near "mistake1"
LINE 1: mistake1;
        ^
psql.bin:/tmp/test.sql:2: ERROR:  syntax error at or near "mistake2"
LINE 1: mistake2;
        ^
psql.bin:/tmp/test.sql:3: ERROR:  syntax error at or near "mistake3"
LINE 1: mistake3;
        ^
-bash-4.2$

-- 让脚本,遇到错误后,立刻退出,可以在执行的时候,加上ON_ERROR_STOP=on,或者在脚本中加上。ON_ERROR_STOP是psql的一个特殊命令,控制脚本执行psql的行为模式。
-- help中的 -v参数

-v, --set=, --variable=NAME=VALUE
                           set psql variable NAME to VALUE
                           (e.g., -v ON_ERROR_STOP=1)
						   
-bash-4.2$ psql -f '/tmp/test.sql' -v ON_ERROR_STOP=1
psql.bin:/tmp/test.sql:1: ERROR:  syntax error at or near "mistake1"
LINE 1: mistake1;
        ^
-bash-4.2$ psql -f '/tmp/test.sql' -v ON_ERROR_STOP=ON
psql.bin:/tmp/test.sql:1: ERROR:  syntax error at or near "mistake1"
LINE 1: mistake1;
        ^
-bash-4.2$ 

-bash-4.2$ more /tmp/test.sql 
\set ON_ERROR_STOP
mistake1;
mistake2;
mistake3;
-bash-4.2$ psql -f '/tmp/test.sql' 
psql.bin:/tmp/test.sql:2: ERROR:  syntax error at or near "mistake1"
LINE 1: mistake1;
        ^
-bash-4.2$					   

-bash-4.2$ psql -f '/tmp/test.sql' -v ON_ERROR_STOP     -- 这个遇到错误不会退出,因为缺少参数ON,或者1  。
psql.bin:/tmp/test.sql:1: ERROR:  syntax error at or near "mistake1"
LINE 1: mistake1;
        ^
psql.bin:/tmp/test.sql:2: ERROR:  syntax error at or near "mistake2"
LINE 1: mistake2;
        ^
psql.bin:/tmp/test.sql:3: ERROR:  syntax error at or near "mistake3"
LINE 1: mistake3;
        ^
-bash-4.2$

-- 可以将ON_ERROR_STOP添加到配置文件中 

-bash-4.2$ psql -f '/tmp/test.sql'
psql.bin:/tmp/test.sql:1: ERROR:  syntax error at or near "mistake1"
LINE 1: mistake1;
        ^
-bash-4.2$ more .psqlrc   -- 添加以后,遇到错误退出。
\set ON_ERROR_STOP
-bash-4.2$ psql -X -f '/tmp/test.sql'   -- 如果想覆盖掉配置文件中的内容,可以使用-X参数 。
psql.bin:/tmp/test.sql:1: ERROR:  syntax error at or near "mistake1"
LINE 1: mistake1;
        ^
psql.bin:/tmp/test.sql:2: ERROR:  syntax error at or near "mistake2"
LINE 1: mistake2;
        ^
psql.bin:/tmp/test.sql:3: ERROR:  syntax error at or near "mistake3"
LINE 1: mistake3;
        ^
-bash-4.2$ 

-X, --no-psqlrc          do not read startup file (~/.psqlrc)   -- help中 -X参数的说明

-- 使用脚本生成脚本 (类似oracle中的|| 来生成执行脚本)。 创建三个表,使用脚本生成修改三个表的语句 。

create schema test;
create table test.a (col1 INTEGER);
create table test.b (col1 INTEGER);
create table test.c (col1 INTEGER);

SELECT relname FROM pg_class c JOIN pg_namespace n ON c.relnamespace = n.oid WHERE n.nspname = 'test';
mydb=# SELECT relname FROM pg_class c JOIN pg_namespace n ON c.relnamespace = n.oid WHERE n.nspname = 'test';
 relname 
---------
 a
 b
 c
(3 rows)

mydb=# 

\t on   -- \t [on|off]            show only rows (currently off)
\o '/tmp/multi.sql'  -- \o [FILE]              send all query results to file or |pipe

SELECT 'ALTER TABLE '|| n.nspname || '.' || c.relname || ' ADD COLUMN last_update_timestamp TIMESTAMP WITH TIME ZONE;'
FROM pg_class c JOIN pg_namespace n ON c.relnamespace = n.oid WHERE n.nspname = 'test';

mydb=# \o '/tmp/multi.sql'
mydb=# SELECT 'ALTER TABLE '|| n.nspname || '.' || c.relname || ' ADD COLUMN last_update_timestamp TIMESTAMP WITH TIME ZONE;'
mydb-# FROM pg_class c JOIN pg_namespace n ON c.relnamespace = n.oid WHERE n.nspname = 'test';
mydb=# \! '/tmp/multi.sql'
sh: /tmp/multi.sql: Permission denied
mydb=# \! more '/tmp/multi.sql'
 ALTER TABLE test.a ADD COLUMN last_update_timestamp TIMESTAMP WITH TIME ZONE;
 ALTER TABLE test.b ADD COLUMN last_update_timestamp TIMESTAMP WITH TIME ZONE;
 ALTER TABLE test.c ADD COLUMN last_update_timestamp TIMESTAMP WITH TIME ZONE;

mydb=# 

\i multi.sql  -- 批量执行sql

-- 查看修改后的表结构

mydb=# \d test.a
                                  Table "test.a"
        Column         |           Type           | Collation | Nullable | Default 
-----------------------+--------------------------+-----------+----------+---------
 col1                  | integer                  |           |          | 
 last_update_timestamp | timestamp with time zone |           |          | 

mydb=# \d test.b
                                  Table "test.b"
        Column         |           Type           | Collation | Nullable | Default 
-----------------------+--------------------------+-----------+----------+---------
 col1                  | integer                  |           |          | 
 last_update_timestamp | timestamp with time zone |           |          | 

mydb=# \d test.c
                                  Table "test.c"
        Column         |           Type           | Collation | Nullable | Default 
-----------------------+--------------------------+-----------+----------+---------
 col1                  | integer                  |           |          | 
 last_update_timestamp | timestamp with time zone |           |          | 

mydb=#

-- 或者另一种方法,这种方法的缺点是直接执行了,没有生成对应的脚本 。

DO $$
DECLARE c record;
BEGIN
FOR c IN SELECT t.*, n.nspname
FROM pg_class c JOIN pg_namespace n
ON c.relnamespace = n.oid
WHERE n.nspname = 'test' /* ; not needed */
LOOP
EXECUTE 'ALTER TABLE '|| quote_ident(n.nspname) ||
'.' || quote_ident(c.relname) ||
' ADD COLUMN last_update_timestamp ' ||
'TIMESTAMP WITH TIME ZONE;'
END LOOP;
END $$;

-- 并行运行,也就是把一个脚本分割成多个脚本 (这个没有执行成功)

\t on  
\o script-:i.sql
SELECT sql FROM (
SELECT 'ALTER TABLE '|| n.nspname || '.' || c.relname ||
' ADD COLUMN last_update_timestamp TIMESTAMP WITH TIME ZONE DEFAULT
now();' as sql
,row_number() OVER (ORDER BY pg_relation_size(c.oid))
FROM pg_class c
JOIN pg_namespace n
ON c.relnamespace = n.oid
WHERE n.nspname = 'test'
ORDER BY 2 DESC) as s
WHERE row_number % 2 = :i;
\o

-- 然后生成两个脚本

psql -v i=0 -f make-script.sql 
psql -v i=1 -f make-script.sql

-- 然后执行

psql -f script-0.sql &
psql -f script-1.sql &

-- 生成两个文件,分别执行 (这个执行成功了 )

\t on
\o script0.sql
SELECT sql FROM (
SELECT 'ALTER TABLE '|| n.nspname || '.' || c.relname ||
' ADD COLUMN last_update_timestamp TIMESTAMP WITH TIME ZONE DEFAULT
now();' as sql
,row_number() OVER (ORDER BY pg_relation_size(c.oid))
FROM pg_class c
JOIN pg_namespace n
ON c.relnamespace = n.oid
WHERE n.nspname = 'test'
ORDER BY 2 DESC) as s
WHERE row_number % 2 = 0;
\o script1.sql
SELECT sql FROM (
SELECT 'ALTER TABLE '|| n.nspname || '.' || c.relname ||
' ADD COLUMN last_update_timestamp TIMESTAMP WITH TIME ZONE DEFAULT
now();' as sql
,row_number() OVER (ORDER BY pg_relation_size(c.oid))
FROM pg_class c
JOIN pg_namespace n
ON c.relnamespace = n.oid
WHERE n.nspname = 'test'
ORDER BY 2 DESC) as s
WHERE row_number % 2 = 1;

pg_batch, 一个工具, 还是调用上面的脚本 。

-- 添加删除表上的列 。添加或者删除表上的列,会持有全表所,有可能会运行很长时间 。

ALTER TABLE mytable ADD COLUMN last_update_timestamp TIMESTAMP WITHOUT TIME ZONE;
ALTER TABLE mytable DROP COLUMN last_update_timestamp;

ALTER TABLE mytable DROP COLUMN IF EXISTS last_update_timestamp, ADD COLUMN last_update_timestamp TIMESTAMP WITHOUT TIME ZONE;  -- 使用alter合并多个操作
UPDATE mytable SET last_update_timestamp = NULL;  -- 等同于上面的语句

drop column实际上并没有从表中的每一行删除列,而是将这个列标记为已删除,所以drop column会感觉很快。
如果增加了一个允许为空并且默认值是空的列,则add column会很快。如果使用了非空约束,或者明确指定了默认值,则需要写入表中的每一行,会导致很慢。
alter table允许一次执行多个列的操作。alter table命令被优化过,所以所有的列操作都将在对表进行一次扫描侯就可以完成。
ALTER TABLE mytable ADD COLUMN last_update_userid INTEGER, ADD COLUMN last_update_comment TEXT;  -- 一次性操作完毕

列被删除时候,其上的索引也开会被自动删除。当列上的其他对象(如外键)会导致alter table命令被拒绝,则可以使用cascade 。
ALTER TABLE x DROP COLUMN last_update_timestamp CASCADE;

-- 修改列

CREATE TABLE birthday( name TEXT, dob INTEGER);
insert into birthday values('simon',690926);

ALTER TABLE birthday ALTER COLUMN dob SET DATA TYPE text;

mydb=# \d birthday
            Table "public.birthday"
 Column | Type | Collation | Nullable | Default 
--------+------+-----------+----------+---------
 name   | text |           |          | 
 dob    | text |           |          | 

mydb=# 

ALTER TABLE birthday ALTER COLUMN dob SET DATA TYPE integer;

mydb=# ALTER TABLE birthday ALTER COLUMN dob SET DATA TYPE integer;
ERROR:  column "dob" cannot be cast automatically to type integer
HINT:  You might need to specify "USING dob::integer".
mydb=# 

ALTER TABLE birthday ALTER COLUMN dob SET DATA TYPE integer USING dob::integer;  -- 使用using从句进行显示转换
mydb=# ALTER TABLE birthday ALTER COLUMN dob SET DATA TYPE integer USING dob::integer;
ALTER TABLE
mydb=# \dS birthday
              Table "public.birthday"
 Column |  Type   | Collation | Nullable | Default 
--------+---------+-----------+----------+---------
 name   | text    |           |          | 
 dob    | integer |           |          | 

mydb=#

-- dob::text的意思,是转换成文本 ,(无法将整型integer转换成时间类型date,需要先转换成text,再转换成date日期类型,一旦有了文本类型,就可以使用to_date函数了)
开始的数据是690926,认为格式是YYMMDD。PG转换这个日期的时候,认为69属于当前世纪,会被转换为2069而不是1969 。使用case语句显式地剪掉100,将年份再16和69之间的日志前移一个世纪。
ALTER TABLE birthday ALTER COLUMN dob SET DATA TYPE date USING date(to_date(dob::text, 'YYMMDD') - (case when dob/10000 < 15 then interval '0' else interval '100 years' end));

mydb=# ALTER TABLE birthday ALTER COLUMN dob SET DATA TYPE date USING date(to_date(dob::text, 'YYMMDD') - (case when dob/10000 < 15 then interval '0' else interval '100 years' end));
ALTER TABLE
mydb=# \dS birthday
            Table "public.birthday"
 Column | Type | Collation | Nullable | Default 
--------+------+-----------+----------+---------
 name   | text |           |          | 
 dob    | date |           |          | 

mydb=# select * from birthday;
 name  |    dob     
-------+------------
 simon | 1969-09-26
(1 row)

mydb=#

-- 在PG中,无论是否有not null约束,都可以设置或者删除字段的默认表达式

ALTER TABLE foo ALTER COLUMN col DROP DEFAULT expression;
ALTER TABLE foo ALTER COLUMN col SET DEFAULT 'expression';
ALTER TABLE foo ALTER COLUMN col SET NOT NULL;
ALTER TABLE foo ALTER COLUMN col DROP NOT NULL;

-- 添加或删除模式schema
-- 添加一个新的模式

CREATE SCHEMA sharedschema;

-- 添加一个新的模式,该模式被指定的用户拥有

CREATE SCHEMA sharedschema AUTHORIZATION scarlett;

-- 创建一个schema,和当前已存在的用户名相同

CREATE SCHEMA AUTHORIZATION scarlett;

-- 删除一个schema

DROP SCHEMA str;

-- 在任何情况下,都确保模式创建成功

create schema if not exists str;

-- 在不同的schema之间移动对象

alter table cust set schema anotherschema;
alter schema existingschema rename to anotherschema;  -- 移动所有对象

-- 添加、删除表空间

CREATE TABLESPACE new_tablespace LOCATION '/usr/local/pgsql/new_tablespace';
DROP TABLESPACE new_tablespace;  -- 表空间只有为空时,才能被删除

每个表空间都有一个对应的位置。例外的情况,用于共享系统编目的对象和其他对象的pg_global、pg_default表空间,不需要设置位置,直接存放在data目录下的子目录中。

-- 查看每个用户对象的表空间

SELECT spcname
,relname
--,CASE WHEN relistemp THEN 'temp ' ELSE '' END ||           -- pg 9
,CASE WHEN relpersistence='t' THEN 'temp ' ELSE '' END ||    -- PG10
CASE
WHEN relkind = 'r' THEN 'table'
WHEN relkind = 'v' THEN 'view'
WHEN relkind = 'S' THEN 'sequence'
WHEN relkind = 'c' THEN 'type'
ELSE 'index' END as objtype
FROM pg_class c join pg_tablespace ts
ON (CASE WHEN c.reltablespace = 0 THEN
(SELECT dattablespace FROM pg_database
WHERE datname = current_database())
ELSE c.reltablespace END) = ts.oid
WHERE relname NOT LIKE 'pg_toast%'
AND relnamespace NOT IN (SELECT oid FROM pg_namespace WHERE
nspname IN ('pg_catalog', 'information_schema'));

mydb=# SELECT spcname
mydb-# ,relname
mydb-# --,CASE WHEN relistemp THEN 'temp ' ELSE '' END ||           -- pg 9
mydb-# ,CASE WHEN relpersistence='t' THEN 'temp ' ELSE '' END ||    -- PG10
mydb-# CASE
mydb-# WHEN relkind = 'r' THEN 'table'
mydb-# WHEN relkind = 'v' THEN 'view'
mydb-# WHEN relkind = 'S' THEN 'sequence'
mydb-# WHEN relkind = 'c' THEN 'type'
mydb-# ELSE 'index' END as objtype
mydb-# FROM pg_class c join pg_tablespace ts
mydb-# ON (CASE WHEN c.reltablespace = 0 THEN
mydb(# (SELECT dattablespace FROM pg_database
mydb(# WHERE datname = current_database())
mydb(# ELSE c.reltablespace END) = ts.oid
mydb-# WHERE relname NOT LIKE 'pg_toast%'
mydb-# AND relnamespace NOT IN (SELECT oid FROM pg_namespace WHERE
mydb(# nspname IN ('pg_catalog', 'information_schema'));
  spcname   |             relname              | objtype  
------------+----------------------------------+----------
 pg_default | weather                          | table
 pg_default | cities                           | table
 pg_default | shoe                             | view
 pg_default | shoelace                         | view
 pg_default | shoe_ready                       | view
 pg_default | sales_summary_seller             | index
 pg_default | words                            | index
 pg_default | wrd                              | index
 pg_default | wrd_word                         | index
 pg_default | wrd_trgm                         | index
 pg_default | shoelace_log                     | table
 pg_default | t                                | table
 pg_default | orders_pkey                      | index
 pg_default | orderlines_pkey                  | index
 pg_default | x                                | table
 pg_default | orders                           | table
 pg_default | orderlines                       | table
 pg_default | t1                               | table
 pg_default | Myemp                            | table
 pg_default | x                                | table
 pg_default | boxes                            | table
 pg_default | boxes_position_excl              | index
 pg_default | foo_fooid_excl                   | index
 pg_default | t2_id_seq                        | sequence
 pg_default | t2                               | table
 pg_default | iprange                          | table
 pg_default | abc                              | table
 pg_default | myview                           | view
 pg_default | my_first_table                   | table
 pg_default | products                         | table
 pg_default | company                          | table
 pg_default | company_pkey                     | index
 pg_default | capitals                         | table
 pg_default | measurement                      | index
 pg_default | measurement_y2006m02             | table
 pg_default | measurement_y2006m03             | table
 pg_default | measurement_y2007m11             | table
 pg_default | measurement_y2006m02_logdate_idx | index
 pg_default | measurement_y2006m03_logdate_idx | index
 pg_default | measurement_y2007m11_logdate_idx | index
 pg_default | users_id_seq                     | sequence
 pg_default | users                            | table
 pg_default | users_pkey                       | index
 pg_default | invoice                          | table
 pg_default | vw_getfoo                        | view
 pg_default | foo_1                            | table
 pg_default | invoice_pkey                     | index
 pg_default | test1                            | table
 pg_default | test1_id_index                   | index
 pg_default | test2                            | table
 pg_default | test2_mm_idx                     | index
 pg_default | tab_big                          | table
 pg_default | image                            | table
 pg_default | sum_prod                         | type
 pg_default | foo                              | table
 pg_default | tab                              | table
 pg_default | emp1                             | table
 pg_default | sales_summary                    | index
 pg_default | shoe_data                        | table
 pg_default | unit                             | table
 pg_default | shoelace_data                    | table
 pg_default | lines                            | table
 pg_default | b                                | table
 pg_default | emp_audit                        | table
 pg_default | c                                | table
 pg_default | emp                              | table
 pg_default | people_id_seq                    | sequence
 pg_default | people_pkey                      | index
 pg_default | hats_id_seq                      | sequence
 pg_default | hats_pkey                        | index
 pg_default | audit_log                        | table
 pg_default | hats                             | table
 pg_default | people                           | table
 pg_default | emp_view                         | view
 pg_default | a                                | table
 pg_default | b                                | table
 pg_default | a                                | table
 pg_default | birthday                         | table
(78 rows)

mydb=#

-- 为每个表空间设置一个特定的所有者

ALTER TABLESPACE new_tablespace OWNER TO eliza;

-- 为用户设置默认的表空间

ALTER USER eliza SET default_tablespace = 'new_tablespace';

-- 将pg_xlog存放到单独的设备上 (PG10 ,是pg_wal)

停止数据库
pg_ctl  stop  
将pg_xlog移动到另一个不同的磁盘设备上
mv $PGDATA/pg_xlog  /mnt/newdisk/
建立一个从旧位置到新位置的软连接
ln -s /mnt/newdisk/pg_xlog $PGDATA/pg_xlog  
重启数据库
pg_ctl start
提交个事务,验证
psql -c 'create table pgxlogtext(x int);

-- 表空间级调优(这个设置大致适用于SSD驱动器,假设驱动器随机读方面比HDD快40倍,顺序读快20倍)

ALTER TABLESPACE new_tablespace SET (seq_page_cost = 0.05, random_page_cost = 0.1);

-- 在不同的表空间之间移动对象(不需要考虑toast对象,当移动表的时候,toast对应也会被移动。但是索引不会被移动,要手动移动)

ALTER TABLE mytable SET TABLESPACE new_tablespace;
ALTER INDEX mytable_val_idx SET TABLESPACE new_tablespace;

-- 一次性移动一个表和该表的所有索引,可以在一个事务中执行(表越大,耗时可能会越长,该过程会产生WAL日志,还会锁表)

BEGIN;
ALTER TABLE mytable SET TABLESPACE new_tablespace;
ALTER INDEX mytable_val1_idx SET TABLESPACE new_tablespace;
ALTER INDEX mytable_val2_idx SET TABLESPACE new_tablespace;
COMMIT;

-- 可以设置缺省的表空间,后续创建的对象,会创建在这个默认的表空间中

set default_tablespace ='new_tablespace';

-- 也可以设置,让所有的用户连接到数据库的时候,自动执行表空间设置

ALTER DATABASE mydb SET default_tablespace = 'new_tablespace';
ALTER DATABASE mydb SET tablespace new_tablespace;  -- 不要执行这个命令,这个命令相当于把不在表空间的对象,移动到这个表空间 。

-- 检查索引是否和表位于同一个表空间(比如,移动表后,发现索引并没有被移动),也可以通过\d t 来查看表和索引是否在同一个表空间(会显示索引所在的表空间)

SELECT i.relname as indexname
,tsi.spcname
,t.relname as tablename
,tst.spcname
FROM (((pg_class t /* tables */
JOIN pg_tablespace tst
ON t.reltablespace = tst.oid)
JOIN pg_index pgi
ON pgi.indrelid = t.oid)
JOIN pg_class i /* indexes */
ON pgi.indexrelid = i.oid)
JOIN pg_tablespace tsi
ON i.reltablespace = tsi.oid
WHERE i.relname NOT LIKE 'pg_toast%'
AND i.reltablespace != t.reltablespace
;

mydb=# SELECT i.relname as indexname
mydb-# ,tsi.spcname
mydb-# ,t.relname as tablename
mydb-# ,tst.spcname
mydb-# FROM (((pg_class t /* tables */
mydb(# JOIN pg_tablespace tst
mydb(# ON t.reltablespace = tst.oid)
mydb(# JOIN pg_index pgi
mydb(# ON pgi.indrelid = t.oid)
mydb(# JOIN pg_class i /* indexes */
mydb(# ON pgi.indexrelid = i.oid)
mydb-# JOIN pg_tablespace tsi
mydb-# ON i.reltablespace = tsi.oid
mydb-# WHERE i.relname NOT LIKE 'pg_toast%'
mydb-# AND i.reltablespace != t.reltablespace
mydb-# ;
 indexname | spcname | tablename | spcname 
-----------+---------+-----------+---------
(0 rows)

mydb=#

FDW外部表的访问: 可以参考  

外部表可以收集统计信息,postgres_fdw透明推送where从句到远程服务中。dblink模块无法发送本地的where从句到远程数据库。
另一个访问其他数据库的方法是使用PL/Proxy的工具。PL/Proxy仅以函数方式运行。PL/Proxy是专门设计应用于分片数据和负载均衡的较为复杂的架构。(例子略)

pg访问oracle数据库的扩展oracle_fdw。 (测试略,有空再研究,好像要安装oracle  客户端之类的)

-- 对视图的修改,比如对视图插入数据等(未做测试)
一个简单的视图

CREATE VIEW cust_view AS SELECT customerid ,firstname ,lastname ,age FROM cust;

尝试对视图执行插入动作,报错

postgres=# INSERT INTO cust_view
postgres-# VALUES (5, 'simon', 'riggs', 133);
ERROR: cannot insert into a view
HINT: You need an unconditional ON INSERT DO INSTEAD rule.
尝试以下语句
CREATE RULE cust_view_insert AS
ON insert TO cust_view
DO INSTEAD
INSERT INTO cust
VALUES (new.customerid, new.firstname, new.lastname, new.age);
重新插入数据
postgres=# INSERT INTO cust_view
postgres-# VALUES (5, 'simon', 'riggs', 133);
INSERT 0 1
插入数据成功,继续添加针对update和delete的规则
CREATE RULE cust_view_update AS
ON update TO cust_view
DO INSTEAD
UPDATE cust SET
firstname = new.firstname
,lastname = new.lastname
,age = new.age
WHERE customerid = old.customerid;
CREATE RULE cust_view_delete AS
ON delete TO cust_view
DO INSTEAD
DELETE FROM cust
WHERE customerid = old.customerid;

-- 使用物化视图
创建两个随机填充内容的表,其中一个很大

create table dish(dish_id serial primary key,dish_description text);
create table eater(eater_id serial,eating_date date,dish_id int references dish(dish_id));

insert into dish(dish_description) values('Lentils'),('Mango'),('Plantain'),('Rice'),('Tea');
insert into eater(eating_date,dish_id) 
select floor(abs(sin(n)) * 365) :: int + date '2014-01-01',
			 ceil(abs(sin(n :: float * n))*5) :: int 
			 from generate_series(1,500000) as rand(n);

-- 创建视图

create view v_dish as select dish_description,count(*) from dish join eater using(dish_id) group by dish_description order by 1;

-- 创建物化视图

create materialized view mv_dish as select dish_description,count(*) from dish join eater using(dish_id) group by dish_description order by 1;

-- 视图和物化视图产生相同的结果,但是查询的耗时有较大差异。物化视图比非物化视图快很多。当建立物化视图的时候,默认行为是马上执行查询并保存结果集,就像保存在表中一样。简单来说,创建物化视图慢,但使用起来很快。刚好和普通视图相反。普通视图是立即被创建,但是在每次使用的时候都要重新计算。

\timing
select * from v_dish;
select * from mv_dish;

mydb=# \timing
Timing is on.
mydb=# select * from v_dish;  -- 视图查询,消耗124ms多
 dish_description | count  
------------------+--------
 Lentils          |  64236
 Mango            |  66512
 Plantain         |  74058
 Rice             |  90222
 Tea              | 204972
(5 rows)

Time: 124.876 ms
mydb=# select * from mv_dish;  -- 物化视图查询,消耗0.6ms多 ,时间上差异很大
 dish_description | count  
------------------+--------
 Lentils          |  64236
 Mango            |  66512
 Plantain         |  74058
 Rice             |  90222
 Tea              | 204972
(5 rows)

Time: 0.606 ms
mydb=#

-- 视图的刷新。当构成表发生变化的时候,物化视图不会自动变更,需要刷新。

refresh materialized view mv_dish;
mydb=# refresh materialized view mv_dish;
REFRESH MATERIALIZED VIEW
Time: 214.795 ms
mydb=#

END