pgsql官方文档学习笔记

从头开始

安装

本次是使用docker 拉取的pgsql的镜像来学习pgsql的

拉取镜像

docker pull postgres

查看镜像:

docker images

启动容器

docker run --name pg -e POSTGRES_PASSWORD=123456 -e POSTGRES_USER=postgres -p 5432:5432 -d postgres:latest

进入容器

docker exec -it 5ad99bf0c1d5 /bin/bash

连接postgresSql 命令

psql -h localhost -p 5432 -U postgres

创建角色赋予权限

一个数据库角色可以有一系列属性,这些属性定义他的权限,以及与客户认证系统的交互。

登陆权限

CREATE ROLE root LOGIN;
CREATE USER name;   # 默认就有登录权限

超级用户权限

默认的超级用户是 postgres ,密码是 123456 ,创建与初始超级用户权限一样的角色

CREATE ROLE root SUPERUSER;

创建数据库的权限

用户创建数据库也是需要权限的,拥有该权限才能创建数据库(对于超级用户是例外,因为他们超越所有权限检查)

CREATE ROLE name CREATEDB ;

创建角色权限

创建角色的权限也需要赋予 ,拥有该权限的角色,拥有更改和删除其他角色,以及给其他成员赋予或者撤销成员关系,不过,要创建、更改、删除一个超级用户角色的成员关系, 需要具有超级用户属性;只有CREATEROLE还不够。

CREATE ROLE name CREATEROLE

启动复制权限

角色要想启动流复制,必须明确给出权限

CREATE ROLE name REPLICATION LOGIN

创建删除数据库

shell命令

创建数据库

$ createdb mydb    -- 创建数据库

删除数据库

$ dropdb mydb  -- 删除数据库

可能的报错

报错一

createdb: command not found

原因:那么就是PostgreSQL没有安装好:要么是就根本没装上、要么是搜索路径没有包含它。 尝试用绝对路径调用该命令试试:

$ /usr/local/pgsql/bin/createdb mydb

报错二

createdb: could not connect to database postgres: could not connect to server: No such file or directory
        Is the server running locally and accepting
        connections on Unix domain socket "/tmp/.s.PGSQL.5432"?

原因:这意味着服务器没有启动,或者没有在createdb预期的地方启动。同样, 你也要检查安装指导或者找管理员。

报错三

createdb: could not connect to database postgres: FATAL:  role "joe" does not exist

原因:

在这里提到了你自己的登陆名。如果管理员没有为你创建PostgreSQL 用户帐号,就会发生这些现像。

本次使用中,我出现了这种情况,但是我本身是有 postgres 角色的,使用命令更换用户就可以了

命令:

su postgres

备注

:PostgreSQL用户名 是和操作系统用户账号分开的。如果你与一个数据库连接,你可以指定以哪个 PostgreSQL用户名进行连接;如果你不指定,那么缺省 就是你当前的操作系统账号。如果这样,那么总有一个与操作系统用户同名的 PostgreSQL用户账号用于启动服务器,并且通常这个用 户都有创建数据库的权限。如果你不想以该用户身份登陆,那么你也可以在任何地方 声明一个-U选项来选择一个连接时使用的PostgreSQL用户名。

访问数据库

进入数据库的交互界面

$ psql mydb

返回

psql (14beta2 (Debian 14~beta2-1.pgdg100+1))
Type "help" for help.

mydb=# 

尝试使用sql命令进行交互

mydb=# select version()
mydb-# ;          -- 使用分号结束
mydb=# SELECT current_date;

 current_date 
--------------
 2021-07-26
(1 row)

select 2+2;

 ?column? 
----------
        4
(1 row)

sql语言

创建新表以及删除表

你可以通过声明表的名字和所有字段的名字及其类型来创建表:

CREATE TABLE weather (
    city            varchar(80),
    temp_lo         int,           -- low temperature
    temp_hi         int,           -- high temperature
    prcp            real,          -- precipitation
    date            date
);

你可以在psql里连换行符一起键入这些东西。psql 可以识别该命令直到分号才结束。

你可以在 SQL 命令中自由使用空白(空格,tab,换行符)。这意味着你可以用 和上面不同的对齐方式(甚至在同一行中)键入命令。双划线("--") 引入注释,任何跟在它后面的东西直到该行的结尾都被忽略。

SQL 是对关键字 和标识符大小写不敏感的语言,只有在标识符用双引号包围时才能保留它们的 大小写属性(上面没有这么干)。

varchar(80)声明一个可以存储最长 80 个字符的任意字符串的数据类型。 int是普通的整数类型。real是一种用于存储单精度浮点数 的类型。date类型应该可以自解释。没错,类型为date的字 段名字也是date。这么做可能比较方便,也可能容易让人 混淆,你自己看啦。

PostgreSQL支持标准的SQL类型: int, smallint, real, double precision, char(*N*), varchar(*N*), date, time, timestamp,和 interval,还支持其它的通用类型和丰富的几何类型。PostgreSQL 允许你自定义任意数量的数据类型。因而类型名并不是语法关键字,除了SQL 标准要求支持的特例外。

第二个例子将保存城市和它们相关的地理位置:

CREATE TABLE cities (
    name            varchar(80),
    location        point
);

point类型就是一个PostgreSQL特有的数据类型的例子。

最后,我们还要提到如果你不再需要某个表,或者你想创建一个不同的表,那么你可以用下面的命令删除它:

DROP TABLE tablename;

向表中添加行

  • 方式一

    INSERT语句用于向表中添加行:

    INSERT INTO weather VALUES ('San Francisco', 46, 50, 0.25, '1994-11-27');
    

    请注意所有数据类型都使用了相当明了的输入格式。那些不是简单数字值的常量必 需用单引号(')包围,就像在例子里一样。date类型实际上 对可接收的格式相当灵活,不过在本教程里,我们应该坚持使用这里显示的格式。

  • 方式二

    到目前为止使用的语法要求你记住字段的顺序。一个可选的语法允许你明确地列出字段:

    INSERT INTO weather (city, temp_lo, temp_hi, prcp, date)
        VALUES ('San Francisco', 43, 57, 0.0, '1994-11-29');
    

    如果需要,你可以用另外一个顺序列出字段或者是忽略某些字段,比如说,我们不知道降水量:

    INSERT INTO weather (date, city, temp_hi, temp_lo)    VALUES ('1994-11-29', 'Hayward', 54, 37);
    
  • 方式三

    你还可以使用COPY从文本文件中装载大量数据。这么干通常更快, 因为COPY命令就是为这类应用优化的,只是比INSERT 少一些灵活性。比如:

    COPY weather FROM '/home/user/weather.txt';
    

查询一个表

不知道表字段,查询表中所有行

SELECT * FROM weather;

知道表结构字段

SELECT city, temp_lo, temp_hi, prcp, date 
FROM weather;

而输出应该是:

    city      | temp_lo | temp_hi | prcp |    date
---------------+---------+---------+------+------------
 San Francisco |      46 |      50 | 0.25 | 1994-11-27
 San Francisco |      43 |      57 |    0 | 1994-11-29
 Hayward       |      37 |      54 |      | 1994-11-29
(3 rows)

带算数表达式的查询

SELECT city, (temp_hi+temp_lo)/2 AS temp_avg, date 
FROM weather; -- as重命名字段

返回

     city      | temp_avg |    date---------------+----------+------------ San Francisco |       48 | 1994-11-27 San Francisco |       50 | 1994-11-29 Hayward       |       45 | 1994-11-29(3 rows)

带查询条件的查询

一个查询可以使用WHERE子句进行"修饰",声明需要哪些行。 WHERE子句包含一个布尔表达式(值为真),只有那些布尔表达式为 真的行才会被返回。允许你在条件中使用常用的布尔操作符(AND, OR,NOT)。比如,下面的查询检索旧金山的下 雨天的天气:

SELECT * 
FROM weather
WHERE city = 'San Francisco' AND prcp > 0.0;

排序操作(order by)

SELECT * FROM weather
ORDER BY city, temp_lo;

消除重复数据(distinct)

SELECT DISTINCT city
FROM weather;

备注一

虽然SELECT *对于即兴的查询是有用的,但我们普遍认为在生产代码中 这是很糟糕的风格,因为给表增加一个字段就改变了结果。

备注二

在一些数据库系统里,包括老版本的PostgreSQL, DISTINCT自动对行进行排序,因此ORDER BY 是多余的。但是这一点并不是 SQL 标准的要求,并且目前的PostgreSQL 并不保证DISTINCT导致数据行被排序。

在表间连接

到目前为止,我们的查询一次只访问了一个表。查询可以一次访问多个表, 或者用某种方式访问一个表,而同时处理该表的多个行。一个同时访问同一个或 者不同表的多个行的查询叫连接查询。

举例来说,比如你 想列出所有天气记录以及这些记录相关的城市。要实现这个目标,我们需要拿 weather表每行的city字段和cities 表所有行的name字段进行比较,并选取那些这些数值相匹配的行。

连接查询操作

SELECT *
FROM weather, cities
WHERE city = name;

返回

    city      | temp_lo | temp_hi | prcp |    date    |     name      | location
---------------+---------+---------+------+------------+---------------+-----------
 San Francisco |      46 |      50 | 0.25 | 1994-11-27 | San Francisco | (-194,53)
 San Francisco |      43 |      57 |    0 | 1994-11-29 | San Francisco | (-194,53)
(2 rows)

观察结果集的两个方面:

  • 没有城市 Hayward 的结果行。这是因为在cities表里面没有与 Hayward 匹配的行,所以连接忽略了weather表里的不匹配行。我们可以使用左连接来带上 Hayward 。
  • 有两个字段包含城市名。这是正确的,因为weathercities表的字段是接在一起的。不过,实际上我们不想要这些, 因此你将可能希望明确列出输出字段而不是使用*

weather 表

select * from weather;

返回

     city      | temp_lo | temp_hi | prcp |    date    
---------------+---------+---------+------+------------
 San Francisco |      46 |      50 | 0.25 | 1994-11-27
 San Francisco |      43 |      57 |    0 | 1994-11-29
 Hayward       |      37 |      54 |      | 1994-11-29
(3 rows)

city表

select * from cities;

返回

     name      | location  
---------------+-----------
 San Francisco | (-194,53)
(1 row)

使用左连接

mydb=# SELECT *                                          
    FROM weather left join cities
    on city = name;

返回

     city      | temp_lo | temp_hi | prcp |    date    |     name      | location  
---------------+---------+---------+------+------------+---------------+-----------
 San Francisco |      46 |      50 | 0.25 | 1994-11-27 | San Francisco | (-194,53)
 San Francisco |      43 |      57 |    0 | 1994-11-29 | San Francisco | (-194,53)
 Hayward       |      37 |      54 |      | 1994-11-29 |               | 
(3 rows)

练习: 看看省略WHERE子句的含义是什么。

SELECT *                                              FROM weather from cities

返回

     city      | temp_lo | temp_hi | prcp |    date    | location  
---------------+---------+---------+------+------------+-----------
 San Francisco |      46 |      50 | 0.25 | 1994-11-27 | (-194,53)
 San Francisco |      43 |      57 |    0 | 1994-11-29 | (-194,53)
 Hayward       |      37 |      54 |      | 1994-11-29 | (-194,53)
(3 rows)

因为这些字段的名字都不一样,所以分析器自动找出它们属于哪个表,但是如果两个 表中有重复的字段名,你就必须使用字段全称限定你想要的字段:

SELECT weather.city, weather.temp_lo, weather.temp_hi,
       weather.prcp, weather.date, cities.location
    FROM weather, cities
    WHERE cities.name = weather.city;

一般认为在连接查询里使用字段全称是很好的风格,这样,即使在将来向其中一个 表里添加了同名字段也不会引起混淆。

where子句更类似一种内连接

SELECT city, temp_lo, temp_hi, prcp, date, location
    FROM weather, cities
    WHERE city = name;
    
 SELECT *
    FROM weather INNER JOIN cities ON (weather.city = cities.name);  

这两者的更为相似

这个语法并非像上面那个那么常用,我们在这里写出来是为了让你更容易了解后面 的主题。

左外连接

SELECT *
    FROM weather LEFT OUTER JOIN cities ON (weather.city = cities.name);

     city      | temp_lo | temp_hi | prcp |    date    |     name      | location
---------------+---------+---------+------+------------+---------------+-----------
 Hayward       |      37 |      54 |      | 1994-11-29 |               |
 San Francisco |      46 |      50 | 0.25 | 1994-11-27 | San Francisco | (-194,53)
 San Francisco |      43 |      57 |    0 | 1994-11-29 | San Francisco | (-194,53)
(3 rows)

这个查询是一个左外连接,因为连接操作符(LEFT OUTER JOIN) 左边的表中的行在输出中至少出现一次,而右边的表只输出那些与左边的表中的某些行匹 配的行。如果输出的左表中的行没有右表中的行与其对应,那么右表中的字段将填充为 NULL 。

练习: 还有右连接和全连接。试着找出来它们能干什么。

右连接

SELECT *                                           
    FROM weather right join cities
    on city = name;
    
         city      | temp_lo | temp_hi | prcp |    date    |     name      | location  
---------------+---------+---------+------+------------+---------------+-----------
 San Francisco |      43 |      57 |    0 | 1994-11-29 | San Francisco | (-194,53)
 San Francisco |      46 |      50 | 0.25 | 1994-11-27 | San Francisco | (-194,53)
(2 rows)

全连接

SELECT *
    FROM weather FULL OUTER join cities
    on city = name;
    
         city      | temp_lo | temp_hi | prcp |    date    |     name      | location  
---------------+---------+---------+------+------------+---------------+-----------
 San Francisco |      46 |      50 | 0.25 | 1994-11-27 | San Francisco | (-194,53)
 San Francisco |      43 |      57 |    0 | 1994-11-29 | San Francisco | (-194,53)
 Hayward       |      37 |      54 |      | 1994-11-29 |               | 
(3 rows)

我们也可以把一个表和它自己连接起来。这叫自连接。 比如,假设我们想找出那些在其它天气记录的温度范围之外的天气记录。 这样我们就需要拿weather表里每行的temp_lotemp_hi 字段与weather表里其它行的temp_lotemp_hi字段进行比较。我们可以用下面的查询实现这个目标:

SELECT W1.city, W1.temp_lo AS low, W1.temp_hi AS high,
    W2.city, W2.temp_lo AS low, W2.temp_hi AS high
    FROM weather W1, weather W2
    WHERE W1.temp_lo < W2.temp_lo
    AND W1.temp_hi > W2.temp_hi;

     city      | low | high |     city      | low | high
---------------+-----+------+---------------+-----+------
 San Francisco |  43 |   57 | San Francisco |  46 |   50
 Hayward       |  37 |   54 | San Francisco |  46 |   50
(2 rows)

在这里我们把 weather 表重新标记为W1W2以区分连接的左边和右边。 你还可以用这样的别名在其它查询里节约一些敲键,比如

SELECT *
    FROM weather w, cities c
    WHERE w.city = c.name;

聚合函数

和大多数其它关系数据库产品一样,PostgreSQL支持聚合函数。 一个聚合函数从多个输入行中计算出一个结果。比如,我们有在一个行集合上计算count(数目), sum(总和),avg(均值),max(最大值), min(最小值)的函数。

比如,我们可以用下面的语句找出所有低温中的最高温度:

SELECT max(temp_lo) FROM weather;
 max 
-----
  46
(1 row)

如果我们想知道该读数发生在哪个城市,可能会用:

SELECT city FROM weather WHERE temp_lo = max(temp_lo);     错误

不过这个方法不能运转,因为聚合函数max不能用于WHERE 子句中。存在这个限制是因为WHERE子句决定哪些行可以进入聚合阶段; 因此它必需在聚合函数之前计算。不过,我们可以用其它方法实现这个目的; 这里我们使用子查询

SELECT city FROM weather
    WHERE temp_lo = (SELECT max(temp_lo) FROM weather);

这样做是可以的,因为子查询是一次独立的计算,它独立于外层查询计算自己的聚合。

聚合同样也常用于 GROUP BY子句。比如,我们可以获取每个城市低温的最高值:

SELECT city, max(temp_lo)
    FROM weather
    GROUP BY city;
    
         city      | max 
---------------+-----
 Hayward       |  37
 San Francisco |  46
(2 rows)

这样每个城市一个输出。每个聚合结果都是在匹配该城市的行上面计算的。 我们可以用HAVING过滤这些分组:

SELECT city, max(temp_lo)
    FROM weather
    GROUP BY city
    HAVING max(temp_lo) < 40;
    
  city   | max
---------+-----
 Hayward |  37
(1 row)

这样就只给出那些temp_lo值曾经有低于 40 度的城市。最后, 如果我们只关心那些名字以"S"开头的城市,我们可以用:

SELECT city, max(temp_lo)
    FROM weather
    WHERE city LIKE 'S%'(1)
    GROUP BY city
    HAVING max(temp_lo) < 40;

备注一

理解聚合和SQL的WHEREHAVING 子句之间的关系非常重要。WHEREHAVING的基本区别如下: WHERE在分组和聚合计算之前选取输入行(它控制哪些行进入聚合计算), 而HAVING在分组和聚合之后选取输出行。因此,WHERE 子句不能包含聚合函数;因为试图用聚合函数判断那些行将要输入给聚合运算是没有意义的。 相反,HAVING子句总是包含聚合函数。当然,你可以写不使用聚合的HAVING 子句,但这样做没什么好处,因为同样的条件用在WHERE阶段会更有效。

备注二

在前面的例子里,我们可以在WHERE里应用城市名称限制,因为它不需要聚合。 这样比在HAVING里增加限制更加高效,因为我们避免了为那些未通过 WHERE检查的行进行分组和聚合计算。

更新

你可以用UPDATE命令更新现有的行。假设你发现所有 11 月 28 日的温度计数都低了两度,那么你就可以用下面的方式更新数据:

UPDATE weather
    SET temp_hi = temp_hi - 2,  temp_lo = temp_lo - 2
    WHERE date > '1994-11-28';

看看数据的新状态:

SELECT * FROM weather;

     city      | temp_lo | temp_hi | prcp |    date
---------------+---------+---------+------+------------
 San Francisco |      46 |      50 | 0.25 | 1994-11-27
 San Francisco |      41 |      55 |    0 | 1994-11-29
 Hayward       |      35 |      52 |      | 1994-11-29
(3 rows)

删除

数据行可以用DELETE命令从表中删除。假设你对 Hayward 的天气不再感兴趣, 那么你可以用下面的命令把那些行从表中删除:

DELETE FROM weather WHERE city = 'Hayward';

所有属于 Hayward 的天气记录都将被删除。

SELECT * FROM weather;
     city      | temp_lo | temp_hi | prcp |    date
---------------+---------+---------+------+------------
 San Francisco |      46 |      50 | 0.25 | 1994-11-27
 San Francisco |      41 |      55 |    0 | 1994-11-29
(2 rows)

使用下面形式的语句时一定要小心:

DELETE FROM tablename;

如果没有指定条件,DELETE将从指定表中删除所有行。 做这些之前系统不会请求你确认!

高级特性

视图

假设你的应用对天气记录和城市位置的 组合列表特别感兴趣,而你又不想每次键入这些查询。那么你可以在这个查询上创建一个视图, 它给这个查询一个名字,你可以像普通表那样引用它。

CREATE VIEW myview AS
    SELECT city, temp_lo, temp_hi, prcp, date, location
        FROM weather, cities
        WHERE city = name;
        
SELECT * FROM myview;

自由地运用视图是好的 SQL 数据库设计的一个关键要素。视图允许我们把表结构的细节封装起来, 这些表可能随你的应用进化而变化,但这些变化却可以隐藏在一个一致的接口后面。

视图几乎可以在一个真正的表可以使用的任何地方使用。在其它视图上面再创建视图也并非罕见。

外键

你想确保没有人可以在weather表里插入一条在cities 表里没有匹配记录的数据行。这就叫维护表的参照完整性。在简单的数据库系统里, 实现(如果也叫实现)这个特性的方法通常是先看看cities表里是否有匹配的记录, 然后插入或者拒绝新的weather记录。这个方法有许多问题,而且非常不便, 因此PostgreSQL可以为你做这些。

新的表声明看起来会像下面这样:

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
);

然后我们试图插入一条非法的记录:

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".

事务

事务是所有数据库系统的一个基本概念。一次事务的要点就是把多个步骤捆绑成一个单一的、 不成功则成仁的操作。其它并发的事务是看不到在这些步骤之间的中间状态的,并且如果发生了一些问题, 导致该事务无法完成,那么所有这些步骤都完全不会影响数据库。

比如,假设一个银行的数据库包含各种客户帐户的余额,以及每个分行的总余额。 假设我们要记录一次从 Alice 的帐户到 Bob 的帐户的金额为 $100.00 的支付动作。 那么,完成这个任务的简单到极点的 SQL 命令像下面这样:

UPDATE accounts SET balance = balance - 100.00
    WHERE name = 'Alice';
UPDATE branches SET balance = balance - 100.00
    WHERE name = (SELECT branch_name FROM accounts WHERE name = 'Alice');
UPDATE accounts SET balance = balance + 100.00
    WHERE name = 'Bob';
UPDATE branches SET balance = balance + 100.00
    WHERE name = (SELECT branch_name FROM accounts WHERE name = 'Bob');

这些命令的细节在这儿并不重要;重要的是这里牵涉到了好几个独立的更新来完成这个相当简单的操作。 银行官员会希望要么所有这些更新全部生效,要么全部不起作用。我们当然不希望一次系统崩溃就导致 Bob 收到 100 块不是 Alice 支付的钱,也不希望 Alice 老是花钱却不能从 Bob 那里拿到物品。 我们需要保证:如果在操作的过程中出了差错,那么所有这些步骤都不会发生效果。 把这些更新组合成一个事务就给予我们这样的保证。事务被认为是原子的: 从其它事务的角度来看,它要么是全部发生,要么完全不发生。

我们还需要保证:一旦一个事务完成并且得到数据库系统的认可,那么它必须被真正永久地存储, 并且不会在随后的崩溃中消失。比如,如果我们记录到了一个 Bob 撤单的动作, 那么我们不希望仅仅在他走出银行大门之后的一次崩溃就会导致对他的帐户的扣减动作消失。 一个事务型数据库保证一个事务所做的所有更新在事务发出完成响应之前都记录到永久的存储中(也就是磁盘)

事务型数据库的另外一个重要的性质和原子更新的概念关系密切:当多个事务并发地运行的时候, 每个事务都不应看到其它事务所做的未完成的变化。比如,如果一个事务正忙着计算所有分行的余额总和, 那么它不应该包括来自 Alice 的分行的扣帐和来自 Bob 分行的入帐,反之亦然。所以事务必须是黑白分明的, 不仅仅体现在它们在数据库上产生的永久影响上,而且体现在它们运转时的自身的可视性上。 一个打开的事务所做的更新在它完成之前是无法被其它事务看到的,而到提交的时候所有更新同时可见。

在PostgreSQL里,一个事务是通过把 SQL 命令用BEGINCOMMIT 命令包围实现的。因此我们的银行事务实际上看起来像下面这样:

BEGIN;
UPDATE accounts SET balance = balance - 100.00
    WHERE name = 'Alice';
-- 等等
COMMIT;

如果在该事务的过程中,我们决定不做提交(可能是我们刚发现 Alice 的余额是负数), 那么我们可以发出ROLLBACK而不是COMMIT命令, 那么到目前为止我们的所有更新都会被取消。

PostgreSQL 实际上把每个 SQL 语句当做在一个事务中执行来看待。 如果你没有发出BEGIN命令,那么每个独立的语句都被一个隐含的BEGIN 和(如果成功的话)COMMIT包围。一组包围在BEGINCOMMIT 之间的语句有时候被称做事务块

我们可以通过使用保存点的方法,在一个事务里更加精细地控制其中的语句。 保存点允许你选择性地抛弃事务中的某些部分,而提交剩下的部分。在用SAVEPOINT 定义了一个保存点后,如果需要,你可以使用ROLLBACK TO回滚到该保存点。 则该事务在定义保存点到 ROLLBACK TO 之间的所有数据库更改都被抛弃, 但是在保存点之前的修改将被保留。

在回滚到一个保存点之后,这个保存点仍然保存着其定义,所以你可以回滚到这个位置好几次。 当然,如果你确信你不需要再次回滚到一个保存点,那么你可以释放它,这样系统可以释放一些资源。 要记住:释放或者回滚到一个保存点都会自动释放在其后定义的所有保存点。

所有这些都发生在一个事务块内部,所以所有这些都不可能被其它事务会话看到。 当且仅当你提交了这个事务块,这些提交了的动作才能以一个单元的方式被其它会话看到, 而回滚的动作完全不会被看到。

记得我们的银行数据库吗? 假设我们从 Alice 的帐户上消费 $100.00 ,然后给 Bob 的帐户进行加款, 稍后我们发现我们应该给 Wally 的账号加款。那么我们可以像下面这样使用保存点:

BEGIN;
UPDATE accounts SET balance = balance - 100.00
    WHERE name = 'Alice';
SAVEPOINT my_savepoint;
UPDATE accounts SET balance = balance + 100.00
    WHERE name = 'Bob';
-- 呀!加错钱了,应该用 Wally 的账号
ROLLBACK TO my_savepoint;
UPDATE accounts SET balance = balance + 100.00
    WHERE name = 'Wally';
COMMIT;

这个例子当然是实在太简单了,但是通过使用保存点,我们可以对事务块有大量的控制。 并且,ROLLBACK TO是除了事务全部回滚,重新来过之外, 唯一可以用于重新控制一个因错误而被系统置于退出状态事务的方法。

窗口函数

继承

继承是面向对象的数据库的概念。它开启了数据库设计的有趣的新的可能性。

一般数据库中,我们使用视图来创建一个符合要求的表

CREATE TABLE capitals (
  name       text,
  population real,
  altitude   int,    -- (单位是英尺)
  state      char(2)
);

CREATE TABLE non_capitals (
  name       text,
  population real,
  altitude   int     -- (单位是英尺)
);

CREATE VIEW cities AS
  SELECT name, population, altitude FROM capitals
    UNION
  SELECT name, population, altitude FROM non_capitals;

​ 如果只是查询,那么这个方法运转得很好,但是如果你需要更新某几行,那这个方法就很难看了。

​ 一种更好的方法是:

CREATE TABLE cities (
  name       text,
  population real,
  altitude   int     -- (单位是英尺)
);

CREATE TABLE capitals (
  state      char(2)
) INHERITS (cities);

在这个例子里,capitals继承了其父表 cities的所有字段(name,populationaltitude)。字段name的类型text是 PostgreSQL用于变长字符串的固有类型。州首府有一个额外的字段 state显示其所处的州。在PostgreSQL里, 一个表可以从零个或者更多其它表中继承过来。

比如,下面的查询找出所有海拔超过 500 英尺的城市的名字,包括州首府:

SELECT name, altitude
  FROM cities
  WHERE altitude > 500;

它返回:

   name    | altitude
-----------+----------
 Las Vegas |     2174
 Mariposa  |     1953
 Madison   |      845
(3 rows)

另一方面,下面的查询找出所有不是州首府并且位于海拔大于或等于 500 英尺的城市:

SELECT name, altitude
    FROM ONLY cities
    WHERE altitude > 500;
   name    | altitude
-----------+----------
 Las Vegas |     2174
 Mariposa  |     1953
(2 rows)
posted @ 2021-07-26 15:25  香草味的薄荷糖  阅读(516)  评论(0)    收藏  举报