postgresql学习笔记(一)---持续更新中

随着大数据越炒越热,postgresql作为开源数据库代表之一扮演着越来越重要的角色,个人感觉必须要自学一把,今天开始持续更新,一边学习一边记录。
一.首先什么是postgresql
PostgreSQL is an object-relational database management system (ORDBMS) based on  POSTGRES, Version 4.2 , developed at the University of California at Berkeley Computer Science Department。
先不管伯克利什么的,postgresql是属于ORDBMS,即面向对象数据库管理系统,区别于oracle、db2等关系型数据库(RDBMS),ORDBMS面向的是对象,但本质上也是属于关系型数据库,其广泛应用于地理环境相关数据库业务中。

PostgreSQL is an open-source descendant of this original Berkeley code. It supports a large part of the SQL standard and offers many modern features:
  • complex queries
  • foreign keys
  • triggers
  • views
  • transactional integrity
  • multiversion concurrency control
Also, PostgreSQL can be extended by the user in many ways, for example by adding new
  • data types
  • functions
  • operators
  • aggregate functions
  • index methods
  • procedural languages
看起来和大多数数据库一样,可以复杂查询、外键、触发器、视图、事务完整性、多版本并发控制、可以创建数据类型、函数、操作符、聚合函数、索引、结构语言等。

二.postgresql基本功能

2.1 创建数据库
首先了解下postgresql的结构,和绝大多数数据库一样,是server/client结构
我在官网上下载了postgresql的rpm包可以直接安装,os是centos6.5

安装结束后初始化数据库、启动数据库
#service postgresql-9.4 initdb
#/etc/init.d/postgresql-9.4 start
此时我们能看到默认端口5432的监听起来了

安装好rpm包后会自动创建postgres超级用户,切换到postgres账户上创建一个数据库wyzpostgre
createdb wyzpostgre

当然我们作为练习可以创建个用户练习
登录数据库(必须要安装client,这一步我们再之前就做过了)
psql wyzpostgres

登录数据库后大家已经迫不及待的敲出命令了,好的,我们先查一下版本号吧
wyzpostgres=# SELECT version();
                                                    version
---------------------------------------------------------------------------------------------------------------
 PostgreSQL 9.4.5 on x86_64-unknown-linux-gnu, compiled by gcc (GCC) 4.4.7 20120313 (Red Hat 4.4.7-16), 64-bit
(1 row)


wyzpostgres=# SELECT current_date;
    date
------------
 2015-12-28
(1 row)
和其他sql语言不同,如果要查询帮助或者退出之类的,是以反斜杠开头

\q 则是退出

2.2 创建表
wyzpostgres=# CREATE TABLE weather (
wyzpostgres(#     city            varchar(80),
wyzpostgres(#     temp_lo         int,           -- low temperature
wyzpostgres(#     temp_hi         int,           -- high temperature
wyzpostgres(#     prcp            real,          -- precipitation
wyzpostgres(#     date            date
wyzpostgres(# );
--表示注释的内容可以作为说明,已分号结束。
好的我们已经有了weather这个表了,再来一个city
wyzpostgres=# CREATE TABLE cities (
wyzpostgres(#     name            varchar(80),
wyzpostgres(#     location        point
wyzpostgres(# );
注意,location这个变量的类型是point,这是postgres特有的类型,用于地理位置。
(删除表的命令很简单,drop table xxx)
2.3 向表中添加行
wyzpostgres=# INSERT INTO weather VALUES ('NanJing', 46, 50, 0.25, '2016-12-28');
wyzpostgres=# INSERT INTO cities VALUES ('NanJing', '(-194.0, 53.0)');
2.4 查询表
wyzpostgres=# SELECT * FROM weather;

查询的命令就是sql语言,可以使用as、order by等等方面查看结果,当然也包含内连接、外连接、全连接 等等
聚合函数

如果想查询某个城市最高温度,也许我会
wyzpostgres=# SELECT city FROM weather WHERE temp_lo = max(temp_lo);
ERROR:  aggregate functions are not allowed in WHERE
LINE 1: SELECT city FROM weather WHERE temp_lo = max(temp_lo);
看到提示了,聚合函数不能在where的子句中
那么只能这样通过子链接查询

2.5 更新
wyzpostgres=# update weather set temp_hi=9 where temp_lo=-1;
2.6 删除
delete from weather where city='GuangZhou';

三. postgresql高级功能

3.1 视图
视图可以方便查询,避免每次查询键入过多的选项


3.2 外键
外键的主要作用就是维护表的完整性,比如在weather中插入一条city中没有匹配的记录行,外键可以帮助我们来作限制使得这条插入命令无法成功。
The new declaration of the tables would look like this:
CREATE TABLE cities ( city varchar(80) primary key, location point);CREATE TABLE weather ( city varchar(80) references cities(city), temp_lo int, temp_hi int, prcp real, date date);
Now try inserting an invalid record:
INSERT INTO weather VALUES ('Berkeley', 45, 53, 0.0, '1994-11-28');
ERROR: insert or update on table "weather" violates foreign key constraint "weather_city_fkey"DETAIL: Key (city)=(Berkeley) is not present in table "cities".
3.3 事务
事务是sql中非常重要的途径,是把多个步骤捆绑成一个单一的不成功则成仁的,其他并发的事务是不会影响该事务操作,中间过程如果出现问题,也不会影响整个数据库性能
举个例子,一个银行系统,wyz转账100元给wrr,需要以下几步骤
update account set balance = balance -100 where name ='WYZ';
updata branches set balance=balance-100 where name=(select branch_name from account where name='WYZ');
updata account set balance=balance+100 where name='WRR';
updata branches set balance=balance+100 where name=(select branch_name from account where name='WRR');
所以我们可不希望因为系统崩溃而wyz的钱无缘无故少了100而没有到wrr的账上,也不希望wrr无缘无故的白白得到100,所以这个时候创建一个事务很重要,要么整个过程全部执行成功且存储后才算完成,否则则不生效任何一个步骤
事务型数据库的重要特点就是多个事务之间是独立的,多个并发事务时间是不会影响的。
在postgresql中事务都是begin开始 commit结束
其实换个角度理解,在postgresql中每个语句都有一个隐含的begin和commit包围,而实际含有begin和commit的命令则称为事务块
而且可以通过保存点来回滚事务中某个阶段前的信息。
begin;
update account set balance = balance -100 where name ='WYZ';

savepoint my_savepoint
updata account set balance=balacne+100 where name='WRZ';
吊,钱转错了,装给wrz了,应该给wrr,没问题
rollback to my_savepoint;
updata account set balance=balance+100 where name='WRR';
commit;



注:与oracle不同,pg在事务这里暂时无法做到单节点的多事务并发(指的把一个事务拆分成n份充分利用cpu核数以增快查询速度,而oracle可以,看postgresql官网说9.6版本以后可以具备这个功能,但是只能基于全表搜索,说不定性能还不佳)

3.4 窗口函数
不同于常规的聚合函数, 使用的窗口函数不会导致行被分组到一个单一的输出行;行保留其独立的身份。 在后台,窗口函数能够访问的不止查询结果的当前行。
例子
SELECT depname, empno, salary, rank() OVER (PARTITION BY depname ORDER BY salary DESC) FROM empsalary;
over(partition by xxx)是窗口函数的格式,可以通过order by 排序

3.5 继承
继承是面向对象数据库的新感念
例:
CREATE TABLE cities ( name text, population real, altitude int );CREATE TABLE capitals ( state char(2)) INHERITS (cities);
在这个例子里,capitals继承了其父表 cities的所有字段(name,population 和altitude)。字段name的类型text是 PostgreSQL用于变长字符串的固有类型。州首府有一个额外的字段 state显示其所处的州。在PostgreSQL里, 一个表可以从零个或者更多其它表中继承过来。
SELECT name, altitude FROM ONLY cities WHERE altitude > 500;
cities前面的ONLY指示系统只对cities 表运行查询,而不包括继承级别中低于cities的表。 许多我们已经讨论过的命令—SELECT, UPDATE和 DELETE—都支持这个ONLY表示法。

四.sql语法

4.1 语法结构
和oracle、mysql一样
建议保持好的写作习惯,没有大小写区别,建议将关键字写成大写,内容写成小写
UPDATE "my_table" SET "a" = 5;

...

4.2 值表达式

4.3 调用函数
CREATE FUNCTION concat_lower_or_upper(a text, b text, uppercase boolean DEFAULT false)RETURNS textAS$$ SELECT CASE WHEN $3 THEN UPPER($1 || ' ' || $2) ELSE LOWER($1 || ' ' || $2) END;$$LANGUAGE SQL IMMUTABLE STRICT;
函数concat_lower_or_upper有两个强制的参数,a和b。 此外第三个参数是一个可选参数uppercase,默认为false。 a和b输入将被串联,并且将根据uppercase 参数强制为大写或者小写

4.3.1 使用位置表示方法
SELECT concat_lower_or_upper('Hello', 'World', true); concat_lower_or_upper ----------------------- HELLO WORLD(1 row)
或者不指定uppercase 默认是flase小写
SELECT concat_lower_or_upper('Hello', 'World'); concat_lower_or_upper ----------------------- hello world(1 row)

4.3.2 使用名称表示方法
SELECT concat_lower_or_upper(a := 'Hello', b := 'World', uppercase := true); concat_lower_or_upper ----------------------- HELLO WORLD(1 row)SELECT concat_lower_or_upper(a := 'Hello', uppercase := true, b := 'World'); concat_lower_or_upper ----------------------- HELLO WORLD(1 row)
4.3.3 使用混合表示方法
即函数里变量可以根据位置也可以根据名称混合起来表达
SELECT concat_lower_or_upper('Hello', 'World', uppercase := true); concat_lower_or_upper ----------------------- HELLO WORLD(1 row)

五. 数据定义

5.1 表的基本概念

5.2 缺省值

在一个表定义里,缺省值是在字段数据类型后面列出的。比如:
CREATE TABLE products ( product_no integer, name text, price numeric DEFAULT 9.99);
缺省值可以是一个表达式,它会在插入缺省值的时候计算(不是在创建表的时候)。 一个常见的例子是一个timestamp字段可能有缺省值CURRENT_TIMESTAMP, 它表示插入行的时刻。另外一个常见的例子是为每一行生成一个"序列号"。 在PostgreSQL里,通常是用类似下面这样的方法生成的:
CREATE TABLE products ( product_no integer DEFAULT nextval('products_product_no_seq'), ...);

5.3 约束
5.3.1 检查约束
检查约束是最常见的约束类型。它允许你声明在某个字段里的数值必须使一个布尔表达式为真。 比如,要强制一个正数的产品价格,你可以用:
CREATE TABLE products ( product_no integer, name text, price numeric CHECK (price > 0));

增加了约束的表可以试验,结果如下
wyzpostgres=# insert into products (product_no ) values (-1);
INSERT 0 1
wyzpostgres=# insert into products (price) values (-1);
ERROR: new row for relation "products" violates check constraint "products_price_check"
DETAIL: Failing row contains (null, null, -1).
wyzpostgres=#


你还可以给这个约束取一个独立的名字。这样就可以令错误消息更清晰, 并且在你需要修改它的时候引用这个名字。语法是:
CREATE TABLE products ( product_no integer, name text, price numeric CONSTRAINT positive_price CHECK (price > 0));
一个检查约束也可以引用多个字段。假设你存储一个正常价格和一个折扣价,并且你想保证折扣价比正常价低:
CREATE TABLE products ( product_no integer, name text, price numeric CHECK (price > 0), discounted_price numeric CHECK (discounted_price > 0), CHECK (price > discounted_price));
头两个约束看上去很面熟。第三个使用了一个新的语法。它没有附着在某个字段上, 而是在逗号分隔的字段列表中以一个独立行的形式出现。字段定义和约束定义可以按照任意顺序列出。

5.3.2 非空约束
非空约束只是简单地声明一个字段必须不能是 NULL。下面是一个例子:
CREATE TABLE products ( product_no integer NOT NULL, name text NOT NULL, price numeric);

当然,一个字段可以有多个约束。只要一个接着一个写就可以了:
CREATE TABLE products ( product_no integer NOT NULL, name text NOT NULL, price numeric NOT NULL CHECK (price > 0));
上面的例子既有非空约束也有检查约束

5.3.3 唯一约束
唯一约束保证在一个字段或者一组字段里的数据与表中其它行的数据相比是唯一的。它的语法是:
CREATE TABLE products ( product_no integer UNIQUE, name text, price numeric);
上面是写成字段约束,下面这个则写成表约束:
CREATE TABLE products ( product_no integer, name text, price numeric, UNIQUE (product_no));
如果一个唯一约束引用一组字段,那么这些字段用逗号分隔列出:
CREATE TABLE example ( a integer, b integer, c integer, UNIQUE (a, c));
这样就声明了特定字段值的组合在整个表范围内是唯一的。 但是这些字段中的某个单独值可以不必是(并且通常也确实不是)唯一的。
你也可以给唯一约束赋予一个自己定义的名字,方法与前面相同:
CREATE TABLE products ( product_no integer CONSTRAINT must_be_different UNIQUE, name text, price numeric);

5.3.4 主键
从技术上讲,主键约束只是唯一约束和非空约束的组合。所以,下面两个表定义是等价的:
CREATE TABLE products ( product_no integer UNIQUE NOT NULL, name text, price numeric);
CREATE TABLE products ( product_no integer PRIMARY KEY, name text, price numeric);
主键也可以约束多于一个字段;其语法类似于唯一约束:
CREATE TABLE example ( a integer, b integer, c integer, PRIMARY KEY (a, c));
一个表最多可以有一个主键(但是它可以有多个唯一和非空约束)。关系型数据库理论告诉我们, 每个表都必须有一个主键。PostgreSQL并不强制这个规则, 但我们最好还是遵循它。

5.3.5 外键

外键约束声明一个字段(或者一组字段)的数值必须匹配另外一个表中出现的数值。 我们把这个行为称为两个相关表之间的参照完整性。
假设你有个产品表,我们可能使用了好几次:
CREATE TABLE products ( product_no integer PRIMARY KEY, name text, price numeric);
假设你有一个存储这些产品的订单的表。我们想保证订单表只包含实际存在的产品。 因此我们在订单表中定义一个外键约束引用产品表:
CREATE TABLE orders ( order_id integer PRIMARY KEY, product_no integer REFERENCES products (product_no), quantity integer);
现在,我们不能创建任何其非空product_no记录没有在产品表中出现的订单。
在这种情况下我们把订单表叫做引用表, 而产品表叫做被引用表。同样,也有引用字段和被引用字段。
你也可以把上面的命令简写成:
CREATE TABLE orders ( order_id integer PRIMARY KEY, product_no integer REFERENCES products, quantity integer);
因为如果缺少字段列表的话,就会引用被引用表的主键。
一个外键也可以约束和引用一组字段。同样,也需要写成表约束的形式。 下面是一个捏造出来的语法例子:
CREATE TABLE t1 ( a integer PRIMARY KEY, b integer, c integer, FOREIGN KEY (b, c) REFERENCES other_table (c1, c2));

5.4 系统字段

oid
行对象标识符(对象ID)。这个字段只有在创建表的时候使用了WITH OIDS 或者是配置参数 default_with_oids 的值为真时出现。 这个字段的类型是oid(和字段同名);
tableoid
包含本行的表的OID。这个字段对那些从继承层次中选取的查询特别有用因为如果没有它的话,我们就很难说明一行来自哪个独立的表。tableoid 可以和pg_class的oid字段连接起来获取表名字。
xmin
插入该行版本的事务标识(事务ID)。注意:在这个环境里,一个行版本是一行的一个状态; 一行的每次更新都为同一个逻辑行创建一个新的行版本。
cmin
在插入事务内部的命令标识(从零开始)。
xmax
删除事务的标识(事务ID),如果不是被删除的行版本,那么是零。在一个可见行版本里, 这个字段有可能是非零。这通常意味着删除事务还没有提交,或者是一个删除的企图被回滚掉了。
cmax
删除事务内部的命令标识符,或者是零。
ctid
一个行版本在它所处的表内的物理位置。请注意,尽管ctid 可以用于非常快速地定位行版本,但每次VACUUM FULL之后, 一个行的ctid都会被更新或者移动。因此ctid 是不能作为长期的行标识符的。应该使用 OID ,或者更好是用户定义的序列号,来标识一个逻辑行。

5.5 修改表
5.5.1 增加、删除字段
wyzpostgres=# ALTER TABLE products ADD COLUMN description text;
ALTER TABLE
wyzpostgres=#
wyzpostgres=#
wyzpostgres=# \d products
Table "public.products"
Column | Type | Modifiers
-------------+---------+-----------
product_no | integer |
name | text |
price | numeric |
description | text |
Check constraints:
"products_price_check" CHECK (price > 0::numeric)

wyzpostgres=# ALTER TABLE products DROP COLUMN description CASCADE;
ALTER TABLE
wyzpostgres=# \d products
Table "public.products"
Column | Type | Modifiers
------------+---------+-----------
product_no | integer |
name | text |
price | numeric |
Check constraints:
"products_price_check" CHECK (price > 0::numeric)
上图结果演示了如何增加、删除字段方法,其中删除的时候增加了cascade可以彻底删除字段相关约束等任何依赖信息。

5.5.2 增加、删除约束
要增加一个约束,必须使用表约束语法。比如:
ALTER TABLE products ADD CHECK (name <> '');ALTER TABLE products ADD CONSTRAINT some_name UNIQUE (product_no);ALTER TABLE products ADD FOREIGN KEY (product_group_id) REFERENCES product_groups;
要增加一个不能写成表约束的非空约束,使用下面的语法:
ALTER TABLE products ALTER COLUMN product_no SET NOT NULL;
这个约束将立即进行检查,所以表在添加约束之前必须符合约束条件。

要删除一个约束,你需要知道它的名字。如果你曾经给了它取了名字, 那么事情就很简单。否则你就需要找出系统自动分配的名字。psql 的命令\d tablename可以帮这个忙; 其它接口可能也提供了检查表的细节的方法。然后就是这条命令:
ALTER TABLE products DROP CONSTRAINT some_name;
如果你在处理一个生成的约束名,比如$2,别忘了你需要给它添加双引号, 让它成为一个有效的标识符。
和删除字段一样,如果你想删除被依赖的约束,你需要用CASCADE。 一个例子是某个外键约束依赖被引用字段上的唯一约束或者主键约束。
除了非空约束外,所有约束类型都这么用。要删除非空约束,可以这样:
ALTER TABLE products ALTER COLUMN product_no DROP NOT NULL;
要记得非空约束没有名字。

5.5.3 改变字段的缺省值
要给一个字段设置缺省值,可以使用一个像下面这样的命令:
ALTER TABLE products ALTER COLUMN price SET DEFAULT 7.77;
请注意这么做不会影响任何表中现有的数据行,它只是为将来的INSERT命令改变缺省值。
要删除缺省值,可以用:
ALTER TABLE products ALTER COLUMN price DROP DEFAULT;

5.5.4 修改字段的数据类型
把一个字段转换成另外一种数据类型,使用下面的命令:
ALTER TABLE products ALTER COLUMN price TYPE numeric(10,2);

5.5.5 重命名字段
重命名一个字段:
ALTER TABLE products RENAME COLUMN product_no TO product_number;

5.5.6 重命名表
重命名一个表:
ALTER TABLE products RENAME TO items;

5.6 权限
postgres有好多种不同的权限:SELECT, INSERT, UPDATE, DELETE, TRUNCATE, REFERENCES, TRIGGER, CREATE, CONNECT, TEMPORARY, EXECUTE, 和 USAGE
使用GRANT命令赋予权限。例如,如果joe是一个已经存在的用户, 而accounts是一个已经存在的表,更新表的权限可以用下面的命令赋予:
GRANT UPDATE ON accounts TO joe;
可以使用REVOKE命令撤销权限:
REVOKE ALL ON accounts FROM PUBLIC;

5.7 模式

5.7.1创建、删除模式
一个数据库包含一个或多个已命名的模式,模式又包含表。模式还可以包含其它对象, 包括数据类型、函数、操作符等。同一个对象名可以在不同的模式里使用而不会导致冲突
要创建一个模式,使用 CREATE SCHEMA 命令。给出你选择的模式名字。比如:
CREATE SCHEMA myschema;
要删除一个模式及其包含的所有对象,可以使用:
DROP SCHEMA myschema CASCADE;

,以pg_开头的模式名是保留给系统使用的,用户不能创建这样的名字。

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

5.7.3 模式搜索路径

要显示当前搜索路径,使用下面的命令:
SHOW search_path;
在缺省的设置中,返回下面的东西:
search_path-------------- "$user",public

要设置模式的搜索路径,可以用(省略了$user是因为并不立即需要它)
SET search_path TO myschema,public;
同样,因为myschema是路径中的第一个元素,新对象缺省时将创建在这里。

5.7.4 模式和权限
缺省时每个人都在public模式上有CREATE和USAGE权限。
缺省时,用户无法访问模式中不属于他们所有的对象。为了让他们能够访问, 模式的所有者需要在模式上赋予他们USAGE权限。 为了让用户使用模式中的对象,我们可能需要赋予适合该对象的额外权限。

5.7.5 系统表模式

除了public和用户创建的模式之外,每个数据库都包含一个pg_catalog模式, 它包含系统表和所有内置数据类型、函数、操作符。pg_catalog总是搜索路径中的一部分。 如果它没有明确出现在路径中,那么它隐含地在所有路径之前搜索。 这样就保证了内置名字总是可以被搜索。不过,你可以明确地把pg_catalog放在搜索路径之后, 如果你想使用用户自定义的名字覆盖内置的名字的话。

5.7.7 移植性
在 SQL 标准里,在同一个模式里的对象被不同的用户所有的概念是不存在的。而且, 有些实现不允许你创建和它们的所有者不同名的模式。实际上, 模式的概念和用户在那些只实现了标准中规定的基本模式支持的数据库系统里几乎是一样的。 因此,许多用户考虑对名字加以修饰,使它们真正由username.tablename 组成。如果你为每个用户都创建了一个模式,这实际上就是PostgreSQL的行为。
同样,在 SQL 标准里也没有public模式的概念。为了最大限度地遵循标准, 你不应该使用(可能甚至是应该删除)public模式。
当然,有些数据库系统可能根本没有模式,或者是通过允许跨数据库访问来提供模式的功能。 如果你需要在这些系统上干活,那么为了最大限度的移植性,应该根本不使用模式。

5.8 继承
见第三章之前写过

5.9 分区

5.9.1 概述
分区的意思是把逻辑上的一个大表分割成物理上的几块。分区可以提供若干好处:
  • 某些类型的查询性能可以得到极大提升。 特别是表中访问率较高的行位于一个单独分区或少数几个分区上的情况下。 分区可以减少索引体积从而可以将高使用率部分的索引存放在内存中。 如果索引不能全部放在内存中,那么在索引上的读和写都会产生更多的磁盘访问。
  • 当查询或更新一个分区的大部分记录时, 连续扫描那个分区而不是使用索引离散的访问整个表可以获得巨大的性能提升。
  • 如果需要大量加载或者删除的记录位于单独的分区上, 那么可以通过直接读取或删除那个分区以获得巨大的性能提升, 因为ALTER TABLE NO INHERIT和DROP TABLE 比操作大量的数据要快的多。这些命令同时还可以避免由于大量DELETE 导致的VACUUM超载。
  • 很少用的数据可以移动到便宜一些的慢速存储介质上。
目前,PostgreSQL支持通过表继承进行分区。 每个分区必须做为单独一个父表的子表进行创建。父表自身通常是空的, 它的存在只是为了代表整个数据集。
PostgreSQL可以实现下面形式的分区:
范围分区
表被一个或者多个关键字段分区成"范围",这些范围在不同的分区里没有重叠。 比如,我们可以通过时间范围分区,或者根据特定业务对象的标识符范围分区。
列表分区
表通过明确地列出每个分区里应该出现哪些关键字值实现。


5.9.2 实现分区
要设置一个分区的表,做下面的步骤:
  1. 创建"主表",所有分区都从它继承。
这个表中没有数据,不要在这个表上定义任何检查约束,除非你希望约束同样也适用于所有分区。 同样,在其上定义任何索引或者唯一约束也没有意义。
  1. 创建几个"子表",每个都从主表上继承。通常,这些表不会增加任何字段。
我们将把子表称作分区,尽管它们就是普通的PostgreSQL表。
  1. 给分区表增加约束,定义每个分区允许的健值。
典型的例子是:
CHECK ( x = 1 )CHECK ( county IN ( 'Oxfordshire', 'Buckinghamshire', 'Warwickshire' ))CHECK ( outletID >= 100 AND outletID < 200 )
确保这些约束能够保证在不同的分区里不会有重叠的键值。一个常见的错误是设置下面这样的范围:
CHECK ( outletID BETWEEN 100 AND 200 )CHECK ( outletID BETWEEN 200 AND 300 )
这样做是错误的,因为它没说清楚健值 200 属于那个范围。
请注意在范围和列表分区的语法方面没有什么区别;这些术语只是用于描述的。
  1. 对于每个分区,在关键字字段上创建一个索引,以及其它你想创建的索引。 关键字字段索引并非严格必需的,但是在大多数情况下它是很有帮助的。 如果你希望关键字值是唯一的,那么你应该总是给每个分区创建一个唯一或者主键约束。
  2. 另外,定义一个规则或者触发器,来重定向数据插入主表到适当的分区。
  3. 确保postgresql.conf里的配置参数constraint_exclusion是打开的。 没有这个参数,查询不会按照需要进行优化。

例子:
比如,假设我们为一个巨大的冰激凌公司构造数据库。该公司每天都测量最高温度, 以及每个地区的冰激凌销售。概念上,我们需要一个这样的表:
CREATE TABLE measurement ( city_id int not null, logdate date not null, peaktemp int, unitsales int);
所以每次查询的话都会把所有时间都查一次,我们就可以做分区表,把查询时间细化到最近一周、一个月、一季度等

以下是用分区表实现的步骤:
  1. 主表是measurement表,就像上面那样声明。
  2. 然后我们为每个月创建一个分区:
CREATE TABLE measurement_y2006m02 ( ) INHERITS (measurement);CREATE TABLE measurement_y2006m03 ( ) INHERITS (measurement);...CREATE TABLE measurement_y2007m11 ( ) INHERITS (measurement);CREATE TABLE measurement_y2007m12 ( ) INHERITS (measurement);CREATE TABLE measurement_y2008m01 ( ) INHERITS (measurement);
每个分区都是拥有自己内容的完整的表,只是它们从measurement表继承定义。
这样就解决了我们的一个问题:删除旧数据。每个月, 我们需要做的只是在最旧的子表上执行一个DROP TABLE, 然后为新月份创建一个新的子表。

(注:此时要删除这几个分区表必须先把继承关系解除,
先要alter table measurement_y2007m11 no inherit measurement;
才能drop table measurement_y2007m11 cascade ;
或者drop命令不加cascade
直接 drop table measurement_y2007m11;)
  1. 我们必须提供非重叠的表约束。而不是只像上面那样创建分区表,所以我们的建表脚本就变成:
CREATE TABLE measurement_y2006m02 ( CHECK ( logdate >= DATE '2006-02-01' AND logdate < DATE '2006-03-01' )) INHERITS (measurement);CREATE TABLE measurement_y2006m03 ( CHECK ( logdate >= DATE '2006-03-01' AND logdate < DATE '2006-04-01' )) INHERITS (measurement);...CREATE TABLE measurement_y2007m11 ( CHECK ( logdate >= DATE '2007-11-01' AND logdate < DATE '2007-12-01' )) INHERITS (measurement);CREATE TABLE measurement_y2007m12 ( CHECK ( logdate >= DATE '2007-12-01' AND logdate < DATE '2008-01-01' )) INHERITS (measurement);CREATE TABLE measurement_y2008m01 ( CHECK ( logdate >= DATE '2008-01-01' AND logdate < DATE '2008-02-01' )) INHERITS (measurement);
  1. 我们可能还需要在关键字字段上有索引:
CREATE INDEX measurement_y2006m02_logdate ON measurement_y2006m02 (logdate);CREATE INDEX measurement_y2006m03_logdate ON measurement_y2006m03 (logdate);...CREATE INDEX measurement_y2007m11_logdate ON measurement_y2007m11 (logdate);CREATE INDEX measurement_y2007m12_logdate ON measurement_y2007m12 (logdate);CREATE INDEX measurement_y2008m01_logdate ON measurement_y2008m01 (logdate);
我们选择先不建立更多的索引。
  1. 我们想让我们的应用可以说INSERT INTO measurement ... 并且数据被重定向到相应的分区表。我们可以安排给主表附上一个合适的触发器。 如果数据只进入最新的分区,我们可以使用一个非常简单的触发器:
CREATE OR REPLACE FUNCTION measurement_insert_trigger()RETURNS TRIGGER AS $$BEGIN INSERT INTO measurement_y2008m01 VALUES (NEW.*); RETURN NULL;END;$$LANGUAGE plpgsql;
创建完函数后,我们将创建一个调用触发器函数的触发器:
CREATE TRIGGER insert_measurement_trigger BEFORE INSERT ON measurement FOR EACH ROW EXECUTE PROCEDURE measurement_insert_trigger();
我们必须每月重新定义触发器,以便它总是指向当前分区。然而,触发定义不需要更新。
我们可能想插入数据并且想让服务器自动定位应该向哪个分区插入数据。 我们可以用下面这个复杂的触发器来实现这个目标,比如:
CREATE OR REPLACE FUNCTION measurement_insert_trigger()RETURNS TRIGGER AS $$BEGIN IF ( NEW.logdate >= DATE '2006-02-01' AND NEW.logdate < DATE '2006-03-01' ) THEN INSERT INTO measurement_y2006m02 VALUES (NEW.*); ELSIF ( NEW.logdate >= DATE '2006-03-01' AND NEW.logdate < DATE '2006-04-01' ) THEN INSERT INTO measurement_y2006m03 VALUES (NEW.*); ... ELSIF ( NEW.logdate >= DATE '2008-01-01' AND NEW.logdate < DATE '2008-02-01' ) THEN INSERT INTO measurement_y2008m01 VALUES (NEW.*); ELSE RAISE EXCEPTION 'Date out of range. Fix the measurement_insert_trigger() function!'; END IF; RETURN NULL;END;$$LANGUAGE plpgsql;
每一个触发器跟以前一样。注意,每一个IF测试必须匹配其分区的 CHECK约束。
当这个函数比单月的情况更复杂时,它不需要经常的更新,因为分支可以在需要之前被添加

5.9.3 分区和约束排除
约束排除是一种 查询优化技巧 ,它改进了用上述方法定义的表分区的性能。比如:
SET constraint_exclusion = on;SELECT count(*) FROM measurement WHERE logdate >= DATE '2008-01-01';

如果没有约束排除,上面的查询会扫描measurement表中的每一个分区。 打开了约束排除之后,规划器将检查每个分区的约束然后试图证明该分区不需要被扫描 (因为它不能包含任何符合WHERE子句条件的数据行)。如果规划器可以证明这个, 它就把该分区从查询规划里排除出去。
你可以使用EXPLAIN命令显示一个规划在constraint_exclusion 打开和关闭情况下的不同。一个为这种类型的表设置的典型的非最佳的规划是:
SET constraint_exclusion = off;EXPLAIN SELECT count(*) FROM measurement WHERE logdate >= DATE '2008-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 >= '2008-01-01'::date) -> Seq Scan on measurement_y2006m02 measurement (cost=0.00..30.38 rows=543 width=0) Filter: (logdate >= '2008-01-01'::date) -> Seq Scan on measurement_y2006m03 measurement (cost=0.00..30.38 rows=543 width=0) Filter: (logdate >= '2008-01-01'::date)... -> Seq Scan on measurement_y2007m12 measurement (cost=0.00..30.38 rows=543 width=0) Filter: (logdate >= '2008-01-01'::date) -> Seq Scan on measurement_y2008m01 measurement (cost=0.00..30.38 rows=543 width=0) Filter: (logdate >= '2008-01-01'::date)


5.10 外部数据
PostgreSQL实现了SQL/MED规范的一部分, 允许使用规则的SQL查询访问驻留在PostgreSQL外部的数据。 这样的数据被称为外部数据。 (请注意这种使用不能同外键混淆,外键是数据库的一种约束类型。)
外部数据是通过外部数据封装器的帮助来访问的。 一个外部数据封装器是一个可以与外部数据源沟通的库,隐藏与外部数据源连接的细节并且从外部数据源获得数据。 这里有几个作为贡献模板的可用外部数据封装器比如如下:

等,具体可以上网参考pg的外部数据

5.11 其他数据对象
在关系结构里,表是核心的对象,因为它们保存你的数据。但是它们并非存在于数据库中的唯一对象。 我们可以创建许多其它类型的对象来让我们对数据的使用和管理变得更方便。 我们没有在这一章里讨论这些对象,但是我们在这里会给你一个列表,这样你就知道什么是可能的。
  • 视图
  • 函数和操作符
  • 数据类型和域
  • 触发器和重写规则


六 数据操作
6.1 插入数据
INSERT INTO products VALUES (1, 'Cheese', 9.99);
数据值是按照这些字段在表中出现的顺序列出的,并且用逗号分隔。通常, 数据值是文本(常量),但也允许使用标量表达式。
上述语法的缺点是你必须知道表中字段的顺序。 你也可以明确地列出字段以避免这个问题。比如, 下面的两条命令都和上面的那条命令效果相同:
INSERT INTO products (product_no, name, price) VALUES (1, 'Cheese', 9.99);INSERT INTO products (name, price, product_no) VALUES ('Cheese', 9.99, 1);

当然你也可以在一条命令中插入多行:
INSERT INTO products (product_no, name, price) VALUES (1, 'Cheese', 9.99), (2, 'Bread', 1.99), (3, 'Milk', 2.99);

6.2 更新数据
UPDATE products SET price = 10 WHERE price = 5;
或者比如, 如果你想把所有产品的价格提高 10% ,可以用:
UPDATE products SET price = price * 1.10;

你还可以在一个UPDATE命令中更新更多的字段, 方法是在SET子句中列出更多赋值语句。比如:
UPDATE mytable SET a = 5, b = 3, c = 1 WHERE a > 0;

6.3 删除数据
DELETE FROM products WHERE price = 10;
如果你只是写:
DELETE FROM products;
那么表中所有行都会被删除!程序员一定要注意。












你可能感兴趣的:(postgresql)