1,PostgreSQL 约束
PostgreSQL 约束用于规定表中的数据规则。
如果存在违反约束的数据行为,行为会被约束终止。
约束可以在创建表时规定(通过 CREATE TABLE 语句),或者在表创建之后规定(通过 ALTER TABLE 语句)。
约束确保了数据库中数据的准确性和可靠性。
约束可以是列级或表级。列级约束仅适用于列,表级约束被应用到整个表。
以下是在 PostgreSQL 中常用的约束。
默认情况下,列可以保存为 NULL 值。如果您不想某列有 NULL 值,那么需要在该列上定义此约束,指定在该列上不允许 NULL 值。
NULL 与没有数据是不一样的,它代表着未知的数据。
实例
下面实例创建了一张新表叫 employee,添加了 5 个字段,其中三个 ID,NAME,AGE 设置不接受空置:
runoobdb=# create table employee(id int primary key not null, name text not null, age int not null, address char(50), salary real);
CREATE TABLE
runoobdb=# select * from employee;
id | name | age | address | salary
----+------+-----+---------+--------
(0 rows)
runoobdb=#
UNIQUE 约束可以设置列是唯一的,避免同一列出现重复值。
实例
下面实例创建了一张新表叫 employee1,添加了 5 个字段,其中 AGE 设置为 UNIQUE,因此你不能添加两条有相同年龄的记录:
runoobdb=# create table employee1(id int primary key not null, name text not null, age int not null unique, address char(50), salary real default 50000.00);
CREATE TABLE
runoobdb=# select * from employee1; id | name | age | address | salary
----+------+-----+---------+--------
(0 rows)
在设计数据库时,PRIMARY KEY 非常重要。
PRIMARY KEY 称为主键,是数据表中每一条记录的唯一标识。
设置 UNIQUE 的列可能有多个,但是一张表只有一列可以设置 PRIMARY KEY。
我们可以使用主键来引用表中的行,也可以通过把主键设置为其他表的外键,来创建表之间的关系。
主键是非空约束和唯一约束的组合。
一个表只能有一个主键,它可以由一个或多个字段组成,当多个字段作为主键,它们被称为复合键。
如果一个表在任何字段上定义了一个主键,那么在这些字段上不能有两个记录具有相同的值。
实例
下面我们创建 employee2 表,其中 ID 作为主键:
runoobdb=# create table employee2(id int primary key not null, name text not null,
CREATE TABLE
runoobdb=# select * from employee2;
id | name | age | address | salary
----+------+-----+---------+--------
(0 rows)
FOREIGN KEY 即外键约束,指定列(或一组列)中的值必须匹配另一个表的某一行中出现的值。
通常一个表中的 FOREIGN KEY 指向另一个表中的 UNIQUE KEY(唯一约束的键),即维护了两个相关表之间的引用完整性。
实例
下面实例创建了一张 employee3表,并添加了5个字段:
runoobdb=# create table employee3(id int primary key not null, name text not null,
CREATE TABLE
runoobdb=# select * from employee3;
id | name | age | address | salary
----+------+-----+---------+--------
(0 rows)
下面实例创建一张 department表,并添加 3 个字段,EMP_ID 就是外键,参照 employee3 的 ID:
runoobdb=# create table department(id int primary key not null, dept char(50) not null, emp_id int references employee3(id));
CREATE TABLE
runoobdb=# select * from department;
id | dept | emp_id
----+------+--------
(0 rows)
CHECK 约束保证列中的所有值满足某一条件,即对输入一条记录要进行检查。如果条件值为 false,则记录违反了约束,且不能输入到表。
实例
例如,下面实例建一个新的表 employee4,增加了五列。在这里,我们为 SALARY 列添加 CHECK,所以工资不能为零:
runoobdb=# create table employee4(id int primary key not null, name text not null, age int not null, address char(50), salary real check(salary > 0));
CREATE TABLE
runoobdb=# select * from employee4;
id | name | age | address | salary
----+------+-----+---------+--------
(0 rows)
EXCLUSION 约束确保如果使用指定的运算符在指定列或表达式上比较任意两行,至少其中一个运算符比较将返回 false 或 null。
实例
下面实例创建了一张 employee5 表,添加 5 个字段,并且使用了 EXCLUDE 约束。
runoobdb=# create extension btree_gist;
CREATE EXTENSION
runoobdb=# create table employee5(id int primary key not null, name text, age int, address char(50), salary real, EXCLUDE USING gist (name with =, age with <>));
CREATE TABLE
runoobdb=# \d
List of relations
Schema | Name | Type | Owner
--------+------------+-------+----------
public | company | table | postgres
public | company1 | table | postgres
public | department | table | postgres
public | employee | table | postgres
public | employee1 | table | postgres
public | employee2 | table | postgres
public | employee3 | table | postgres
public | employee4 | table | postgres
public | employee5 | table | postgres
(9 rows)
这里,USING gist 是用于构建和执行的索引一种类型。
您需要为每个数据库执行一次 CREATE EXTENSION btree_gist 命令,
这将安装 btree_gist 扩展,它定义了对纯标量数据类型的 EXCLUDE 约束。
由于我们已经强制执行了年龄必须相同,让我们通过向表插入记录来查看这一点:
runoobdb=# insert into employee5 values(1, 'Paul', 32, 'California', 20000.00);
INSERT 0 1
runoobdb=# insert into employee5 values(2, 'Paul', 32, 'Texas', 20000.00);
INSERT 0 1
runoobdb=# insert into employee5 values(3, 'Allen', 42, 'California', 20000.00);
INSERT 0 1
runoobdb=# insert into employee5 values(4, 'Allen', 52, 'California', 20000.00);
ERROR: conflicting key value violates exclusion constraint "employee5_name_age_excl"
DETAIL: Key (name, age)=(Allen, 52) conflicts with existing key (name, age)=(Allen, 42).
前面三条顺利添加的 employee5 表中,但是第四条则会报错。
ERROR: conflicting key value violates exclusion constraint "employee6_name_age_excl"
DETAIL: Key (name, age)=(Allen, 52) conflicts with existing key (name, age)=(Allen, 42).
删除约束必须知道约束名称,已经知道名称来删除约束很简单,如果不知道名称,则需要找到系统生成的名称,使用 \d 表名 可以找到这些信息。
通用语法如下:
ALTER TABLE table_name DROP CONSTRAINT some_name;
2,PostgreSQL 连接(JOIN)
PostgreSQL JOIN 子句用于把来自两个或多个表的行结合起来,基于这些表之间的共同字段。
在 PostgreSQL 中,JOIN 有五种连接类型:
接下来让我们创建两张表 COMPANY 和 DEPARTMENT。
创建 COMPANY 表,数据内容如下:
runoobdb=# select * from company;
id | name | age | address | salary | join_date
----+-------+-----+----------------------------------------------------+--------+------------
1 | Paul | 32 | California | 20000 | 2014-01-01
2 | Allen | 25 | Texas | 27500 | 2015-02-18
3 | Teddy | 23 | Norway | 18000 | 2016-03-28
4 | Mark | 25 | Rich-Mond | 6500 | 2018-05-20
5 | David | 27 | Texas | 80500 | 2018-07-20
6 | Kim | 22 | South-Hall | 40500 | 2019-07-10
7 | James | 24 | Houston | 10000 | 2017-03-02
8 | Paul | 24 | Houston | 20000 | 2018-09-02
9 | James | 44 | Norway | 5000 | 2017-09-20
10 | James | 45 | Texas | 5000 | 2015-10-02
(10 rows)
创建一张 DEPARTMENT 表,添加三个字段:
runoobdb=# create table departments(id int primary key not null,dept char(50) not null, emp_id int not null);
CREATE TABLE
向 departments 表插入三条记录:
runoobdb=# insert into departments (id, dept, emp_id) values(1, 'IT Billing', 1);
INSERT 0 1
runoobdb=# insert into departments (id, dept, emp_id) values(2, 'Engineering', 2);
INSERT 0 1
runoobdb=# insert into departments (id, dept, emp_id) values(3, 'Finance', 7);
INSERT 0 1
此时,departments 表的记录如下:
runoobdb=# select * from departments;
id | dept | emp_id
----+----------------------------------------------------+--------
1 | IT Billing | 1
2 | Engineering | 2
3 | Finance | 7
(3 rows)
交叉连接(CROSS JOIN)把第一个表的每一行与第二个表的每一行进行匹配。如果两个输入表分别有 x 和 y 行,则结果表有 x*y 行。
由于交叉连接(CROSS JOIN)有可能产生非常大的表,使用时必须谨慎,只在适当的时候使用它们。
下面是 CROSS JOIN 的基础语法:
SELECT ... FROM table1 CROSS JOIN table2 ...
基于上面的表,我们可以写一个交叉连接(CROSS JOIN),如下所示:
runoobdb=# SELECT EMP_ID, NAME, DEPT FROM COMPANY CROSS JOIN DEPARTMENT;
得到结果如下:
runoobdb=# select emp_id, name, dept from company cross join departments;
emp_id | name | dept
--------+-------+----------------------------------------------------
1 | Paul | IT Billing
1 | Allen | IT Billing
1 | Teddy | IT Billing
1 | Mark | IT Billing
1 | David | IT Billing
1 | Kim | IT Billing
1 | James | IT Billing
1 | Paul | IT Billing
1 | James | IT Billing
1 | James | IT Billing
2 | Paul | Engineering
2 | Allen | Engineering
2 | Teddy | Engineering
2 | Mark | Engineering
2 | David | Engineering
2 | Kim | Engineering
2 | James | Engineering
2 | Paul | Engineering
2 | James | Engineering
2 | James | Engineering
7 | Paul | Finance
7 | Allen | Finance
7 | Teddy | Finance
7 | Mark | Finance
7 | David | Finance
7 | Kim | Finance
7 | James | Finance
7 | Paul | Finance
7 | James | Finance
7 | James | Finance
(30 rows)
内连接(INNER JOIN)根据连接谓词结合两个表(table1 和 table2)的列值来创建一个新的结果表。查询会把 table1 中的每一行与 table2 中的每一行进行比较,找到所有满足连接谓词的行的匹配对。
当满足连接谓词时,A 和 B 行的每个匹配对的列值会合并成一个结果行。
内连接(INNER JOIN)是最常见的连接类型,是默认的连接类型。
INNER 关键字是可选的。
下面是内连接(INNER JOIN)的语法:
SELECT table1.column1, table2.column2...
FROM table1
INNER JOIN table2
ON table1.common_filed = table2.common_field;
基于上面的表,我们可以写一个内连接,如下所示:
runoobdb=# select emp_id, name, dept from company inner join departments on company.id = departments.emp_id;
emp_id | name | dept
--------+-------+----------------------------------------------------
1 | Paul | IT Billing
2 | Allen | Engineering
7 | James | Finance
(3 rows)
外部连接是内部连接的扩展。SQL 标准定义了三种类型的外部连接: LEFT、RIGHT 和 FULL, PostgreSQL 支持所有这些。
对于左外连接,首先执行一个内连接。然后,对于表 T1 中不满足表 T2 中连接条件的每一行,其中 T2 的列中有 null 值也会添加一个连接行。因此,连接的表在 T1 中每一行至少有一行。
下面是左外连接( LEFT OUTER JOIN )的基础语法:
SELECT ... FROM table1 LEFT OUTER JOIN table2 ON conditional_expression ...
基于上面两张表,我们可以写个左外连接,如下:
runoobdb=# select emp_id, name, dept from company left outer join departments on company.id = departments.emp_id;
emp_id | name | dept
--------+-------+----------------------------------------------------
1 | Paul | IT Billing
2 | Allen | Engineering
7 | James | Finance
| James |
| David |
| Paul |
| Kim |
| Mark |
| Teddy |
| James |
(10 rows)
首先,执行内部连接。然后,对于表T2中不满足表T1中连接条件的每一行,其中T1列中的值为空也会添加一个连接行。这与左联接相反;对于T2中的每一行,结果表总是有一行。
下面是右外连接( RIGHT OUT JOIN)的基本语法:
SELECT ... FROM table1 RIGHT OUTER JOIN table2 ON conditional_expression ...
基于上面两张表,我们建立一个右外连接:
runoobdb=# select emp_id, name, dept from company right outer join departments on company.id = departments.emp_id;
emp_id | name | dept
--------+-------+----------------------------------------------------
1 | Paul | IT Billing
2 | Allen | Engineering
7 | James | Finance
(3 rows)
首先,执行内部连接。然后,对于表 T1 中不满足表 T2 中任何行连接条件的每一行,如果 T2 的列中有 null 值也会添加一个到结果中。此外,对于 T2 中不满足与 T1 中的任何行连接条件的每一行,将会添加 T1 列中包含 null 值的到结果中。
下面是外连接的基本语法:
SELECT ... FROM table1 FULL OUTER JOIN table2 ON conditional_expression ...
基于上面两张表,可以建立一个外连接:
runoobdb=# select emp_id, name, dept from company full outer join departments on company.id = departments.emp_id;
emp_id | name | dept
--------+-------+----------------------------------------------------
1 | Paul | IT Billing
2 | Allen | Engineering
7 | James | Finance
| James |
| David |
| Paul |
| Kim |
| Mark |
| Teddy |
| James |
(10 rows)
3,PostgreSQL UNION 操作符
PostgreSQL UNION 操作符合并两个或多个 SELECT 语句的结果。
UNION 操作符用于合并两个或多个 SELECT 语句的结果集。
请注意,UNION 内部的每个 SELECT 语句必须拥有相同数量的列。列也必须拥有相似的数据类型。同时,每个 SELECT 语句中的列的顺序必须相同。
UNIONS 基础语法如下:
SELECT column1 [, column2 ]
FROM table1 [, table2 ]
[WHERE condition]
UNION
SELECT column1 [, column2 ]
FROM table1 [, table2 ]
[WHERE condition]
这里的条件语句可以根据您的需要设置任何表达式。
创建 COMPANY 表,数据内容如下:
runoobdb=# select * from company;
id | name | age | address | salary | join_date
----+-------+-----+----------------------------------------------------+--------+------------
1 | Paul | 32 | California | 20000 | 2014-01-01
2 | Allen | 25 | Texas | 27500 | 2015-02-18
3 | Teddy | 23 | Norway | 18000 | 2016-03-28
4 | Mark | 25 | Rich-Mond | 6500 | 2018-05-20
5 | David | 27 | Texas | 80500 | 2018-07-20
6 | Kim | 22 | South-Hall | 40500 | 2019-07-10
7 | James | 24 | Houston | 10000 | 2017-03-02
8 | Paul | 24 | Houston | 20000 | 2018-09-02
9 | James | 44 | Norway | 5000 | 2017-09-20
10 | James | 45 | Texas | 5000 | 2015-10-02
(10 rows)
创建 DEPARTMENTS表,数据内容如下:
runoobdb=# select * from departments;
id | dept | emp_id
----+----------------------------------------------------+--------
1 | IT Billing | 1
2 | Engineering | 2
3 | Finance | 7
4 | Engineering | 3
5 | Finance | 4
6 | Engineering | 5
7 | Finance | 6
(7 rows)
现在,我们在 SELECT 语句中使用 UNION 子句将两张表连接起来,如下所示:
select emp_id, name, dept from company inner join departments on company.id = departments.emp_id union select emp_id, name, dept from company left outer join departments on company.id = departments.emp_id;
得到结果如下:
emp_id | name | dept
--------+-------+----------------------------------------------------
7 | James | Finance
6 | Kim | Finance
2 | Allen | Engineering
3 | Teddy | Engineering
5 | David | Engineering
4 | Mark | Finance
| James |
1 | Paul | IT Billing
| Paul |
(9 rows)
两个语句分开执行的结果如下:
runoobdb=# select emp_id, name, dept from company inner join departments on company.id = departments.emp_id;
emp_id | name | dept
--------+-------+----------------------------------------------------
1 | Paul | IT Billing
2 | Allen | Engineering
7 | James | Finance
3 | Teddy | Engineering
4 | Mark | Finance
5 | David | Engineering
6 | Kim | Finance
(7 rows)
runoobdb=# select emp_id, name, dept from company left outer join departments on company.id = departments.emp_id;
emp_id | name | dept
--------+-------+----------------------------------------------------
1 | Paul | IT Billing
2 | Allen | Engineering
7 | James | Finance
3 | Teddy | Engineering
4 | Mark | Finance
5 | David | Engineering
6 | Kim | Finance
| James |
| Paul |
| James |
(10 rows)
UNION ALL 操作符可以连接两个有重复行的 SELECT 语句,默认地,UNION 操作符选取不同的值。如果允许重复的值,请使用 UNION ALL。
UINON ALL 子句基础语法如下:
SELECT column1 [, column2 ]
FROM table1 [, table2 ]
[WHERE condition]
UNION ALL
SELECT column1 [, column2 ]
FROM table1 [, table2 ]
[WHERE condition]
这里的条件语句可以根据您的需要设置任何表达式。
现在,让我们把上面提到的两张表用 SELECT 语句结合 UNION ALL 子句连接起来:
runoobdemp_id, name, dept from company inner join departments on company.id = departments.emp_id union all select emp_id, name, dept from company left outer join departments on company.id = departments.emp_id;
得到结果如下:
emp_id | name | dept
--------+-------+----------------------------------------------------
1 | Paul | IT Billing
2 | Allen | Engineering
7 | James | Finance
3 | Teddy | Engineering
4 | Mark | Finance
5 | David | Engineering
6 | Kim | Finance
1 | Paul | IT Billing
2 | Allen | Engineering
7 | James | Finance
3 | Teddy | Engineering
4 | Mark | Finance
5 | David | Engineering
6 | Kim | Finance
| James |
| Paul |
| James |
(17 rows)
4,PostgreSQL NULL 值
NULL 值代表遗漏的未知数据。
默认地,表的列可以存放 NULL 值。
本章讲解 IS NULL 和 IS NOT NULL 操作符。
当创建表时,NULL 的基本语法如下:
runoobdb=# create table company2(
id int primary key not null,
name text not null,
age int not null,
address char(50),
salary real);
CREATE TABLE
runoobdb=# select * from company2;
id | name | age | address | salary
----+------+-----+---------+--------
(0 rows)
这里,NOT NULL 表示强制字段始终包含值。这意味着,如果不向字段添加值,就无法插入新记录或者更新记录。
具有 NULL 值的字段表示在创建记录时可以留空。
在查询数据时,NULL 值可能会导致一些问题,因为一个未知的值去与其他任何值比较,结果永远是未知的。
另外无法比较 NULL 和 0,因为它们是不等价的。
runoobdb=# insert into company2 values(1, 'Paul', 32, 'California', 20000);
runoobdb=# insert into company2 values(2, 'Allen', 25, 'Texas', 15000);
runoobdb=# insert into company2 values(3, 'Teddy', 23, 'Norway', 20000);
runoobdb=# insert into company2 values(4, 'Mark', 25, 'Rich-Mond', 65000);
runoobdb=# insert into company2 values(5, 'David', 27, 'Texas', 85000);
runoobdb=# insert into company2 values(6, 'Kim', 22, 'South-Hall', 45000);
runoobdb=# insert into company2 values(7, 'James', 24, 'Houston', 10000);
INSERT 0 1
runoobdb=# select * from company2;
id | name | age | address | salary
----+-------+-----+----------------------------------------------------+--------
1 | Paul | 32 | California | 20000
2 | Allen | 25 | Texas | 15000
3 | Teddy | 23 | Norway | 20000
4 | Mark | 25 | Rich-Mond | 65000
5 | David | 27 | Texas | 85000
6 | Kim | 22 | South-Hall | 45000
7 | James | 24 | Houston | 10000
(7 rows)
接下来我们用 UPDATE 语句把几个可设置为空的字段设置为 NULL :
runoobdb=# update company2 set address=null, salary=null where id in(6,7);
UPDATE 2
runoobdb=# select * from company2;
id | name | age | address | salary
----+-------+-----+----------------------------------------------------+--------
1 | Paul | 32 | California | 20000
2 | Allen | 25 | Texas | 15000
3 | Teddy | 23 | Norway | 20000
4 | Mark | 25 | Rich-Mond | 65000
5 | David | 27 | Texas | 85000
6 | Kim | 22 | |
7 | James | 24 | |
(7 rows)
IS NOT NULL
现在,我们用 IS NOT NULL 操作符把所有 SALARY(薪资) 值不为空的记录列出来:
runoobdb=# select id, age, address, salary from company2 where salary is not null;
id | age | address | salary
----+-----+----------------------------------------------------+--------
1 | 32 | California | 20000
2 | 25 | Texas | 15000
3 | 23 | Norway | 20000
4 | 25 | Rich-Mond | 65000
5 | 27 | Texas | 85000
(5 rows)
IS NULL
IS NULL 用来查找为 NULL 值的字段。
下面是 IS NULL 操作符的用法,列出 SALARY(薪资) 值为空的记录:
runoobdb=# select id, age, address, salary from company2 where salary is null;
id | age | address | salary
----+-----+---------+--------
6 | 22 | |
7 | 24 | |
(2 rows)
5,PostgreSQL 别名
我们可以用 SQL 重命名一张表或者一个字段的名称,这个名称就叫着该表或该字段的别名。
创建别名是为了让表名或列名的可读性更强。
SQL 中 使用 AS 来创建别名。
表的别名语法:
SELECT column1, column2....
FROM table_name AS alias_name
WHERE [condition];
列的别名语法:
SELECT column_name AS alias_name
FROM table_name
WHERE [condition];
实例
创建 COMPANY 表,数据内容如下:
runoobdb=# select * from company2; id | name | age | address | salary
----+-------+-----+----------------------------------------------------+--------
1 | Paul | 32 | California | 20000
2 | Allen | 25 | Texas | 15000
3 | Teddy | 23 | Norway | 20000
4 | Mark | 25 | Rich-Mond | 65000
5 | David | 27 | Texas | 85000
6 | Kim | 22 | South-Hall | 45000
7 | James | 24 | Houston | 10000
(7 rows)
创建 DEPARTMENTS表,数据内容如下:
runoobdb=# select * from departments;
id | dept | emp_id
----+----------------------------------------------------+--------
1 | IT Billing | 1
2 | Engineering | 2
3 | Finance | 7
4 | Engineering | 3
5 | Finance | 4
6 | Engineering | 5
7 | Finance | 6
(7 rows)
下面我们分别用 C 和 D 表示 COMPANY 表和 DEPAERMENT 表的别名:
runoobdb=# select c.id, c.name, c.age, d.dept from company2 as c, departments as d where c.id = d.emp_id;
id | name | age | dept
----+-------+-----+----------------------------------------------------
1 | Paul | 32 | IT Billing
2 | Allen | 25 | Engineering
7 | James | 24 | Finance
3 | Teddy | 23 | Engineering
4 | Mark | 25 | Finance
5 | David | 27 | Engineering
6 | Kim | 22 | Finance
(7 rows)
下面,我们用 COMPANY_ID 表示 ID 列,COMPANY_NAME 表示 NAME 列,来展示列别名的用法:
runoobdb=# select c.id as company_id, c.name as company_name, c.age, d.dept from company2 as c, departments as d where c.id = d.emp_id;
company_id | company_name | age | dept
------------+--------------+-----+----------------------------------------------------
1 | Paul | 32 | IT Billing
2 | Allen | 25 | Engineering
7 | James | 24 | Finance
3 | Teddy | 23 | Engineering
4 | Mark | 25 | Finance
5 | David | 27 | Engineering
6 | Kim | 22 | Finance
(7 rows)
6,PostgreSQL 触发器
PostgreSQL 触发器是数据库的回调函数,它会在指定的数据库事件发生时自动执行/调用。
下面是关于 PostgreSQL 触发器几个比较重要的点:
PostgreSQL 触发器可以在下面几种情况下触发:
触发器的 FOR EACH ROW 属性是可选的,如果选中,当操作修改时每行调用一次;相反,选中 FOR EACH STATEMENT,不管修改了多少行,每个语句标记的触发器执行一次。
WHEN 子句和触发器操作在引用 NEW.column-name 和 OLD.column-name 表单插入、删除或更新时可以访问每一行元素。其中 column-name 是与触发器关联的表中的列的名称。
如果存在 WHEN 子句,PostgreSQL 语句只会执行 WHEN 子句成立的那一行,如果没有 WHEN 子句,PostgreSQL 语句会在每一行执行。
BEFORE 或 AFTER 关键字决定何时执行触发器动作,决定是在关联行的插入、修改或删除之前或者之后执行触发器动作。
要修改的表必须存在于同一数据库中,作为触发器被附加的表或视图,且必须只使用 tablename,而不是 database.tablename。
当创建约束触发器时会指定约束选项。这与常规触发器相同,只是可以使用这种约束来调整触发器触发的时间。当约束触发器实现的约束被违反时,它将抛出异常。
创建触发器时的基础语法如下:
CREATE TRIGGER trigger_name [BEFORE|AFTER|INSTEAD OF] event_name
ON table_name
[
-- 触发器逻辑....
];
在这里,event_name 可以是在所提到的表 table_name 上的 INSERT、DELETE 和 UPDATE 数据库操作。您可以在表名后选择指定 FOR EACH ROW。
以下是在 UPDATE 操作上在表的一个或多个指定列上创建触发器的语法:
CREATE TRIGGER trigger_name [BEFORE|AFTER] UPDATE OF column_name
ON table_name
[
-- 触发器逻辑....
];
让我们假设一个情况,我们要为被插入到新创建的 COMPANY 表(如果已经存在,则删除重新创建)中的每一个记录保持审计试验:
runoobdb=# create table company(
id int primary key not null,
name text not null,
age int not null,
address char(50),
salary real);
CREATE TABLE
为了保持审计试验,我们将创建一个名为 AUDIT 的新表。每当 COMPANY 表中有一个新的记录项时,日志消息将被插入其中:
runoobdb=# create table audit(emp_id int not null, entry_date text null);
CREATE TABLE
在这里,ID 是 AUDIT 记录的 ID,EMP_ID 是来自 COMPANY 表的 ID,DATE 将保持 COMPANY 中记录被创建时的时间戳。所以,现在让我们在 COMPANY 表上创建一个触发器,如下所示:
runoobdb=# CREATE TRIGGER example_trigger AFTER INSERT ON COMPANY FOR EACH ROW EXECUTE PROCEDURE auditlogfunc();
auditlogfunc() 是 PostgreSQL 一个程序,其定义如下:
CREATE OR REPLACE FUNCTION auditlogfunc() RETURNS TRIGGER AS $example_table$
BEGIN
INSERT INTO AUDIT(EMP_ID, ENTRY_DATE) VALUES (new.ID, current_timestamp);
RETURN NEW;
END;
$example_table$ LANGUAGE plpgsql;
现在,我们开始往 COMPANY 表中插入数据:
runoobdb=# INSERT INTO COMPANY (ID,NAME,AGE,ADDRESS,SALARY) VALUES (2, 'Paul', 32, 'California', 30000.00 );
这时,COMPANY 表中插入了一条记录:
同时, AUDIT 表中也插入了一条记录,因为我们在插入 COMPANY 表时创建了一个触发器。相似的,我们也可以根据需求在更新和删除时创建触发器:
runoobdb=# select * from audit;
emp_id | entry_date
--------+-------------------------------
2 | 2020-06-20 17:40:31.780016+08
(1 row)
你可以把从 pg_trigger 表中把当前数据库所有触发器列举出来:
runoobdb=# SELECT * FROM pg_trigger;
结果如下:
oid | tgrelid | tgname | tgfoid | tgtype | tgenabled | tgisinternal | tgconstrrelid | tgconstrindid | tgconstraint | tgdeferrable | tginitdeferred | tgnargs | tgattr | tgargs | tgqual | tgoldtable | tgnewtable
-------+---------+------------------------------+--------+--------+-----------+--------------+---------------+---------------+--------------+--------------+----------------+---------+--------+--------+--------+------------+------------
16478 | 16464 | RI_ConstraintTrigger_a_16478 | 1654 | 9 | O | t | 16472 | 16470 | 16477 | f | f | 0 | | \x | | |
16479 | 16464 | RI_ConstraintTrigger_a_16479 | 1655 | 17 | O | t | 16472 | 16470 | 16477 | f | f | 0 | | \x | | |
16480 | 16472 | RI_ConstraintTrigger_c_16480 | 1644 | 5 | O | t | 16464 | 16470 | 16477 | f | f | 0 | | \x | | |
16481 | 16472 | RI_ConstraintTrigger_c_16481 | 1645 | 17 | O | t | 16464 | 16470 | 16477 | f | f | 0 | | \x | | |
17195 | 17180 | example_trigger | 17194 | 5 | O | f | 0 | 0 | 0 | f | f | 0 | | \x | | |
(5 rows)
如果,你想列举出特定表的触发器,语法如下:
runoobdb=# SELECT tgname FROM pg_trigger, pg_class WHERE tgrelid=pg_class.oid AND relname='company';
得到结果如下:
tgname
-----------------
example_trigger
(1 row)
删除触发器基础语法如下:
drop trigger ${trigger_name} on ${table_of_trigger_dependent};
删除本文上表 company 上的触发器 example_trigger 的指令为:
drop trigger example_trigger on company;
7,PostgreSQL 索引
索引是加速搜索引擎检索数据的一种特殊表查询。简单地说,索引是一个指向表中数据的指针。一个数据库中的索引与一本书的索引目录是非常相似的。
拿汉语字典的目录页(索引)打比方,我们可以按拼音、笔画、偏旁部首等排序的目录(索引)快速查找到需要的字。
索引有助于加快 SELECT 查询和 WHERE 子句,但它会减慢使用 UPDATE 和 INSERT 语句时的数据输入。索引可以创建或删除,但不会影响数据。
使用 CREATE INDEX 语句创建索引,它允许命名索引,指定表及要索引的一列或多列,并指示索引是升序排列还是降序排列。
索引也可以是唯一的,与 UNIQUE 约束类似,在列上或列组合上防止重复条目。
CREATE INDEX (创建索引)的语法如下:
CREATE INDEX index_name ON table_name;
单列索引
单列索引是一个只基于表的一个列上创建的索引,基本语法如下:
CREATE INDEX index_name ON table_name (column_name);
组合索引
组合索引是基于表的多列上创建的索引,基本语法如下:
CREATE INDEX index_name ON table_name (column1_name, column2_name);
不管是单列索引还是组合索引,该索引必须是在 WHEHE 子句的过滤条件中使用非常频繁的列。
如果只有一列被使用到,就选择单列索引,如果有多列就使用组合索引。
唯一索引
使用唯一索引不仅是为了性能,同时也为了数据的完整性。唯一索引不允许任何重复的值插入到表中。基本语法如下:
CREATE UNIQUE INDEX index_name on table_name (column_name);
局部索引
局部索引 是在表的子集上构建的索引;子集由一个条件表达式上定义。索引只包含满足条件的行。基础语法如下:
CREATE INDEX index_name on table_name (conditional_expression);
隐式索引
隐式索引 是在创建对象时,由数据库服务器自动创建的索引。索引自动创建为主键约束和唯一约束。
下面实例将在 COMPANY 表的 SALARY 列上创建索引:
runoobdb=# CREATE INDEX salary_index ON COMPANY (salary);
CREATE INDEX
现在,用 \d company 命令列出 COMPANY 表的所有索引:
runoobdb=# \d company
得到的结果如下,company_pkey 是隐式索引 ,是表创建表时创建的:
Table "public.company"
Column | Type | Collation | Nullable | Default
---------+---------------+-----------+----------+---------
id | integer | | not null |
name | text | | not null |
age | integer | | not null |
address | character(50) | | |
salary | real | | |
Indexes:
"company_pkey" PRIMARY KEY, btree (id)
"salary_index" btree (salary)
Triggers:
example_trigger AFTER INSERT ON company FOR EACH ROW EXECUTE FUNCTION auditlogfunc()
你可以使用 \di 命令列出数据库中所有索引:
runoobdb=# \di
List of relations
Schema | Name | Type | Owner | Table
--------+-------------------------+-------+----------+-------------
public | company1_pkey | index | postgres | company1
public | company2_pkey | index | postgres | company2
public | company_pkey | index | postgres | company
public | department_pkey | index | postgres | department
public | departments_pkey | index | postgres | departments
public | employee1_age_key | index | postgres | employee1
public | employee1_pkey | index | postgres | employee1
public | employee2_pkey | index | postgres | employee2
public | employee3_pkey | index | postgres | employee3
public | employee4_pkey | index | postgres | employee4
public | employee5_name_age_excl | index | postgres | employee5
public | employee5_pkey | index | postgres | employee5
public | employee6_name_age_excl | index | postgres | employee6
public | employee6_pkey | index | postgres | employee6
public | employee_pkey | index | postgres | employee
public | salary_index | index | postgres | company
(16 rows)
一个索引可以使用 PostgreSQL 的 DROP 命令删除。
DROP INDEX index_name;
您可以使用下面的语句来删除之前创建的索引:
runoobdb=# DROP INDEX salary_index;
DROP INDEX
删除后,可以看到 salary_index 已经在索引的列表中被删除:
runoobdb=# \di
List of relations
Schema | Name | Type | Owner | Table
--------+-------------------------+-------+----------+-------------
public | company1_pkey | index | postgres | company1
public | company2_pkey | index | postgres | company2
public | company_pkey | index | postgres | company
public | department_pkey | index | postgres | department
public | departments_pkey | index | postgres | departments
public | employee1_age_key | index | postgres | employee1
public | employee1_pkey | index | postgres | employee1
public | employee2_pkey | index | postgres | employee2
public | employee3_pkey | index | postgres | employee3
public | employee4_pkey | index | postgres | employee4
public | employee5_name_age_excl | index | postgres | employee5
public | employee5_pkey | index | postgres | employee5
public | employee6_name_age_excl | index | postgres | employee6
public | employee6_pkey | index | postgres | employee6
public | employee_pkey | index | postgres | employee
(15 rows)
虽然索引的目的在于提高数据库的性能,但这里有几个情况需要避免使用索引。
使用索引时,需要考虑下列准则:
8,PostgreSQL ALTER TABLE 命令
在 PostgreSQL 中,ALTER TABLE 命令用于添加,修改,删除一张已经存在表的列。
另外你也可以用 ALTER TABLE 命令添加和删除约束。
用 ALTER TABLE 在一张已存在的表上添加列的语法如下:
ALTER TABLE table_name ADD column_name datatype;
在一张已存在的表上 DROP COLUMN(删除列),语法如下:
ALTER TABLE table_name DROP COLUMN column_name;
修改表中某列的 DATA TYPE(数据类型),语法如下:
ALTER TABLE table_name ALTER COLUMN column_name TYPE datatype;
给表中某列添加 NOT NULL 约束,语法如下:
ALTER TABLE table_name MODIFY column_name datatype NOT NULL;
给表中某列 ADD UNIQUE CONSTRAINT( 添加 UNIQUE 约束),语法如下:
ALTER TABLE table_name
ADD CONSTRAINT MyUniqueConstraint UNIQUE(column1, column2...);
给表中 ADD CHECK CONSTRAINT(添加 CHECK 约束),语法如下
ALTER TABLE table_name
ADD CONSTRAINT MyUniqueConstraint CHECK (CONDITION);
给表 ADD PRIMARY KEY(添加主键),语法如下:
ALTER TABLE table_name
ADD CONSTRAINT MyPrimaryKey PRIMARY KEY (column1, column2...);
DROP CONSTRAINT (删除约束),语法如下:
ALTER TABLE table_name
DROP CONSTRAINT MyUniqueConstraint;
如果是 MYSQL ,代码是这样:
ALTER TABLE table_name
DROP INDEX MyUniqueConstraint;
DROP PRIMARY KEY (删除主键),语法如下:
ALTER TABLE table_name
DROP CONSTRAINT MyPrimaryKey;
如果是 MYSQL ,代码是这样:
ALTER TABLE table_name
DROP PRIMARY KEY;
创建 COMPANY 表,数据内容如下:
runoobdb=# select * from company;
id | name | age | address | salary
----+------+-----+----------------------------------------------------+--------
1 | Paul | 32 | California | 20000
2 | Paul | 32 | California | 30000
(2 rows)
下面实例在这张表中添加新的列:
runoobdb=# ALTER TABLE COMPANY ADD GENDER char(1);
ALTER TABLE
现在表长这样:
runoobdb=# select * from company;
id | name | age | address | salary | gender
----+------+-----+----------------------------------------------------+--------+--------
1 | Paul | 32 | California | 20000 |
2 | Paul | 32 | California | 30000 |
(2 rows)
下面实例删除 GENDER 列:
runoobdb=# ALTER TABLE COMPANY DROP GENDER;
ALTER TABLE
得到结果如下:
runoobdb=# select * from company;
id | name | age | address | salary
----+------+-----+----------------------------------------------------+--------
1 | Paul | 32 | California | 20000
2 | Paul | 32 | California | 30000
(2 rows)
9,PostgreSQL TRUNCATE TABLE
PostgreSQL 中 TRUNCATE TABLE 用于删除表的数据,但不删除表结构。
也可以用 DROP TABLE 删除表,但是这个命令会连表的结构一起删除,如果想插入数据,需要重新建立这张表。
TRUNCATE TABLE 与 DELETE 具有相同的效果,但是由于它实际上并不扫描表,所以速度更快。 此外,TRUNCATE TABLE 可以立即释放表空间,而不需要后续 VACUUM 操作,这在大型表上非常有用。
PostgreSQL VACUUM 操作用于释放、再利用更新/删除行所占据的磁盘空间。
TRUNCATE TABLE 基础语法如下:
TRUNCATE TABLE table_name;
创建 COMPANY 表,数据内容如下:
runoobdb=# select * from company;
id | name | age | address | salary
----+------+-----+----------------------------------------------------+--------
1 | Paul | 32 | California | 20000
2 | Paul | 32 | California | 30000
(2 rows)
下面实例使用了 TRUNCATE TABLE 来清除 COMPANY 表:
runoobdb=# TRUNCATE TABLE COMPANY;
TRUNCATE TABLE
得到结果如下:
runoobdb=# select * from company;
id | name | age | address | salary
----+------+-----+---------+--------
(0 rows)
10,PostgreSQL View(视图)
View(视图)是一张假表,只不过是通过相关的名称存储在数据库中的一个 PostgreSQL 语句。
View(视图)实际上是一个以预定义的 PostgreSQL 查询形式存在的表的组合。
View(视图)可以包含一个表的所有行或从一个或多个表选定行。
View(视图)可以从一个或多个表创建,这取决于要创建视图的 PostgreSQL 查询。
View(视图)是一种虚拟表,允许用户实现以下几点:
PostgreSQL 视图是只读的,因此可能无法在视图上执行 DELETE、INSERT 或 UPDATE 语句。但是可以在视图上创建一个触发器,当尝试 DELETE、INSERT 或 UPDATE 视图时触发,需要做的动作在触发器内容中定义。
在 PostgreSQL 用 CREATE VIEW 语句创建视图,视图创建可以从一张表,多张表或者其他视图。
CREATE VIEW 基础语法如下:
CREATE [TEMP | TEMPORARY] VIEW view_name AS
SELECT column1, column2.....
FROM table_name
WHERE [condition];
您可以在 SELECT 语句中包含多个表,这与在正常的 SQL SELECT 查询中的方式非常相似。如果使用了可选的 TEMP 或 TEMPORARY 关键字,则将在临时数据库中创建视图。
创建 COMPANY 1表,数据内容如下:
runoobdb=# select * from company1;
id | name | age | address | salary | join_date
----+-------+-----+----------------------------------------------------+--------+------------
4 | Mark | 25 | Rich-Mond | 65000 | 2017-04-26
5 | David | 27 | Texas | 85000 | 2018-04-06
6 | Kim | 22 | South-Hall | 45000 | 2019-09-12
(3 rows)
现在,下面是一个从 COMPANY1 表创建视图的实例。视图只从 COMPANY 表中选取几列:
runoobdb=# CREATE VIEW COMPANY_VIEW AS SELECT ID, NAME, AGE FROM COMPANY1;
CREATE VIEW
现在,可以查询 COMPANY_VIEW,与查询实际表的方式类似。下面是实例:
runoobdb=# SELECT * FROM COMPANY_VIEW;
id | name | age
----+-------+-----
4 | Mark | 25
5 | David | 27
6 | Kim | 22
(3 rows)
要删除视图,只需使用带有 view_name 的 DROP VIEW 语句。DROP VIEW 的基本语法如下:
runoobdb=# DROP VIEW view_name;
下面的命令将删除我们在前面创建的 COMPANY_VIEW 视图:
runoobdb=# DROP VIEW COMPANY_VIEW;
DROP VIEW
查询结果如下:
runoobdb=# SELECT * FROM COMPANY_VIEW;
ERROR: relation "company_view" does not exist
LINE 1: SELECT * FROM COMPANY_VIEW;
11,PostgreSQL TRANSACTION(事务)
TRANSACTION(事务)是数据库管理系统执行过程中的一个逻辑单位,由一个有限的数据库操作序列构成。
数据库事务通常包含了一个序列的对数据库的读/写操作。包含有以下两个目的:
当事务被提交给了数据库管理系统(DBMS),则 DBMS 需要确保该事务中的所有操作都成功完成且其结果被永久保存在数据库中,如果事务中有的操作没有成功完成,则事务中的所有操作都需要回滚,回到事务执行前的状态;同时,该事务对数据库或者其他事务的执行无影响,所有的事务都好像在独立的运行。
事务具有以下四个标准属性,通常根据首字母缩写为 ACID:
某人要在商店使用电子货币购买100元的东西,当中至少包括两个操作:
支持事务的数据库管理系统就是要确保以上两个操作(整个"事务")都能完成,或一起取消,否则就会出现 100 元平白消失或出现的情况。
使用下面的命令来控制事务:
事务控制命令只与 INSERT、UPDATE 和 DELETE 一起使用。他们不能在创建表或删除表时使用,因为这些操作在数据库中是自动提交的。
事务可以使用 BEGIN TRANSACTION 命令或简单的 BEGIN 命令来启动。此类事务通常会持续执行下去,直到遇到下一个 COMMIT 或 ROLLBACK 命令。不过在数据库关闭或发生错误时,事务处理也会回滚。以下是启动一个事务的简单语法:
COMMIT;
或者
END TRANSACTION;
ROLLBACK 命令是用于撤消尚未保存到数据库的事务命令,即回滚事务。
ROLLBACK 命令的语法如下
ROLLBACK;
创建 COMPANY 表,数据内容如下:
runoobdb=# select * from company2;
id | name | age | address | salary
----+-------+-----+----------------------------------------------------+--------
1 | Paul | 32 | California | 20000
2 | Allen | 25 | Texas | 15000
3 | Teddy | 23 | Norway | 20000
4 | Mark | 25 | Rich-Mond | 65000
5 | David | 27 | Texas | 85000
6 | Kim | 22 | South-Hall | 45000
7 | James | 24 | Houston | 10000
(7 rows)
现在,让我们开始一个事务,并从表中删除 age = 25 的记录,最后,我们使用 ROLLBACK 命令撤消所有的更改。
runoobdb=# BEGIN;
DELETE FROM COMPANY2 WHERE AGE = 25;
ROLLBACK;
检查 COMPANY 2表,仍然有以下记录:
runoobdb=# select * from company2;
id | name | age | address | salary
----+-------+-----+----------------------------------------------------+--------
1 | Paul | 32 | California | 20000
2 | Allen | 25 | Texas | 15000
3 | Teddy | 23 | Norway | 20000
4 | Mark | 25 | Rich-Mond | 65000
5 | David | 27 | Texas | 85000
6 | Kim | 22 | South-Hall | 45000
7 | James | 24 | Houston | 10000
(7 rows)
现在,让我们开始另一个事务,从表中删除 age = 25 的记录,最后我们使用 COMMIT 命令提交所有的更改。
runoobdb=# BEGIN;
DELETE FROM COMPANY2 WHERE AGE = 25;
COMMIT;
检查 COMPANY 2表,记录已被删除:
runoobdb=# select * from company2;
id | name | age | address | salary
----+-------+-----+----------------------------------------------------+--------
1 | Paul | 32 | California | 20000
3 | Teddy | 23 | Norway | 20000
5 | David | 27 | Texas | 85000
6 | Kim | 22 | South-Hall | 45000
7 | James | 24 | Houston | 10000
(5 rows)
12,PostgreSQL LOCK(锁)
锁主要是为了保持数据库数据的一致性,可以阻止用户修改一行或整个表,一般用在并发较高的数据库中。
在多个用户访问数据库的时候若对并发操作不加控制就可能会读取和存储不正确的数据,破坏数据库的一致性。
数据库中有两种基本的锁:排它锁(Exclusive Locks)和共享锁(Share Locks)。
如果数据对象加上排它锁,则其他的事务不能对它读取和修改。
如果加上共享锁,则该数据库对象可以被其他事务读取,但不能修改。
LOCK 命令基础语法如下:
LOCK [ TABLE ]
name
IN
lock_mode
一旦获得了锁,锁将在当前事务的其余时间保持。没有解锁表命令;锁总是在事务结束时释放。
当两个事务彼此等待对方完成其操作时,可能会发生死锁。尽管 PostgreSQL 可以检测它们并以回滚结束它们,但死锁仍然很不方便。为了防止应用程序遇到这个问题,请确保将应用程序设计为以相同的顺序锁定对象。
PostgreSQL 提供了创建具有应用程序定义含义的锁的方法。这些被称为咨询锁。由于系统不强制使用它们,所以正确使用它们取决于应用程序。咨询锁对于不适合 MVCC 模型的锁定策略非常有用。
例如,咨询锁的一个常见用途是模拟所谓"平面文件"数据管理系统中典型的悲观锁定策略。虽然存储在表中的标志可以用于相同的目的,但是通知锁更快,避免了表膨胀,并且在会话结束时由服务器自动清理。
创建 COMPANY2 表,数据内容如下:
runoobdb=# runoobdb=# select * from company2;
id | name | age | address | salary
----+-------+-----+----------------------------------------------------+--------
1 | Paul | 32 | California | 20000
3 | Teddy | 23 | Norway | 20000
5 | David | 27 | Texas | 85000
6 | Kim | 22 | South-Hall | 45000
7 | James | 24 | Houston | 10000
(5 rows)
下面的示例将 runoobdb 数据库中的 COMPANY 2表锁定为 ACCESS EXCLUSIVE 模式。
LOCK 语句只在事务模式下工作。
runoobdb=# BEGIN;
LOCK TABLE company1 IN ACCESS EXCLUSIVE MODE;
上面操作将得到下面结果:
LOCK TABLE
上面的消息指示表被锁定,直到事务结束,并且要完成事务,您必须回滚或提交事务。
13,PostgreSQL 子查询
子查询或称为内部查询、嵌套查询,指的是在 PostgreSQL 查询中的 WHERE 子句中嵌入查询语句。
一个 SELECT 语句的查询结果能够作为另一个语句的输入值。
子查询可以与 SELECT、INSERT、UPDATE 和 DELETE 语句一起使用,并可使用运算符如 =、<、>、>=、<=、IN、BETWEEN 等。
以下是子查询必须遵循的几个规则:
子查询必须用括号括起来。
子查询在 SELECT 子句中只能有一个列,除非在主查询中有多列,与子查询的所选列进行比较。
ORDER BY 不能用在子查询中,虽然主查询可以使用 ORDER BY。可以在子查询中使用 GROUP BY,功能与 ORDER BY 相同。
子查询返回多于一行,只能与多值运算符一起使用,如 IN 运算符。
BETWEEN 运算符不能与子查询一起使用,但是,BETWEEN 可在子查询内使用。
子查询通常与 SELECT 语句一起使用。基本语法如下:
SELECT column_name [, column_name ]
FROM table1 [, table2 ]
WHERE column_name OPERATOR
(SELECT column_name [, column_name ]
FROM table1 [, table2 ]
[WHERE])
创建 COMPANY 2表,数据内容如下:
runoobdb=# select * from company2;
id | name | age | address | salary
----+-------+-----+----------------------------------------------------+--------
1 | Paul | 32 | California | 20000
3 | Teddy | 23 | Norway | 20000
5 | David | 27 | Texas | 85000
6 | Kim | 22 | South-Hall | 45000
7 | James | 24 | Houston | 10000
(5 rows)
现在,让我们在 SELECT 语句中使用子查询:
runoobdb=# SELECT * FROM COMPANY2 WHERE ID IN (SELECT ID FROM COMPANY2 WHERE SALARY > 45000) ;
得到结果如下:
id | name | age | address | salary
----+-------+-----+----------------------------------------------------+--------
5 | David | 27 | Texas | 85000
(1 row)
子查询也可以与 INSERT 语句一起使用。INSERT 语句使用子查询返回的数据插入到另一个表中。
在子查询中所选择的数据可以用任何字符、日期或数字函数修改。
基本语法如下:
INSERT INTO table_name [ (column1 [, column2 ]) ]
SELECT [ *|column1 [, column2 ] ]
FROM table1 [, table2 ]
[ WHERE VALUE OPERATOR ]
检查 COMPANY 2表中的内容:
runoobdb=# select * from company2;
id | name | age | address | salary
----+-------+-----+----------------------------------------------------+--------
1 | Paul | 32 | California | 20000
3 | Teddy | 23 | Norway | 20000
5 | David | 27 | Texas | 85000
6 | Kim | 22 | South-Hall | 45000
7 | James | 24 | Houston | 10000
(5 rows)
检查 COMPANY 表中的内容:
runoobdb=# select * from company;
id | name | age | address | salary
----+------+-----+---------+--------
(0 rows)
假设 COMPANY 的结构与 COMPANY2 表相似,且可使用相同的 CREATE TABLE 进行创建,只是表名改为 COMPANY。现在把整个 COMPANY2 表复制到 COMPANY,语法如下:
runoobdb=# INSERT INTO COMPANY SELECT * FROM COMPANY2 WHERE ID IN (SELECT ID FROM COMPANY2) ;
再次检查 COMPANY 表中的内容:
runoobdb=# select * from company;
id | name | age | address | salary
----+-------+-----+----------------------------------------------------+--------
1 | Paul | 32 | California | 20000
3 | Teddy | 23 | Norway | 20000
5 | David | 27 | Texas | 85000
6 | Kim | 22 | South-Hall | 45000
7 | James | 24 | Houston | 10000
(5 rows)
子查询可以与 UPDATE 语句结合使用。当通过 UPDATE 语句使用子查询时,表中单个或多个列被更新。
基本语法如下:
UPDATE table
SET column_name = new_value
[ WHERE OPERATOR [ VALUE ]
(SELECT COLUMN_NAME
FROM TABLE_NAME)
[ WHERE) ]
假设,我们有 COMPANY表,是 COMPANY2 表的备份。
下面的实例把 COMPANY2 表中所有 AGE 大于 27 的客户的 SALARY 更新为原来的 0.50 倍:
runoobdb=# UPDATE COMPANY2 SET SALARY = SALARY * 0.50 WHERE AGE IN (SELECT AGE FROM COMPANY WHERE AGE >= 27 );
UPDATE 2
这将影响两行,最后 COMPANY 2表中的记录如下:
runoobdb=# select * from company2;
id | name | age | address | salary
----+-------+-----+----------------------------------------------------+--------
3 | Teddy | 23 | Norway | 20000
6 | Kim | 22 | South-Hall | 45000
7 | James | 24 | Houston | 10000
1 | Paul | 32 | California | 10000
5 | David | 27 | Texas | 42500
(5 rows)
子查询可以与 DELETE 语句结合使用,就像上面提到的其他语句一样。
基本语法如下:
DELETE FROM TABLE_NAME
[ WHERE OPERATOR [ VALUE ]
(SELECT COLUMN_NAME
FROM TABLE_NAME)
[ WHERE) ]
假设,我们有 COMPANY 表,是 COMPANY2 表的备份。
下面的实例删除 COMPANY 2表中所有 AGE 大于或等于 27 的客户记录:
runoobdb=# DELETE FROM COMPANY2 WHERE AGE IN (SELECT AGE FROM COMPANY WHERE AGE > 27 );
这将影响一行,最后 COMPANY 表中的记录如下:
runoobdb=# select * from company2;
id | name | age | address | salary
----+-------+-----+----------------------------------------------------+--------
3 | Teddy | 23 | Norway | 20000
6 | Kim | 22 | South-Hall | 45000
7 | James | 24 | Houston | 10000
5 | David | 27 | Texas | 42500
(4 rows)
14,PostgreSQL AUTO INCREMENT(自动增长)
AUTO INCREMENT(自动增长) 会在新记录插入表中时生成一个唯一的数字。
PostgreSQL 使用序列来标识字段的自增长,数据类型有 smallserial、serial 和 bigserial 。这些属性类似于 MySQL 数据库支持的 AUTO_INCREMENT 属性。
使用 MySQL 设置自动增长的语句如下:
CREATE TABLE IF NOT EXISTS `runoob_tbl`(
`runoob_id` INT UNSIGNED AUTO_INCREMENT,
`runoob_title` VARCHAR(100) NOT NULL,
`runoob_author` VARCHAR(40) NOT NULL,
`submission_date` DATE,
PRIMARY KEY ( `runoob_id` )
)ENGINE=InnoDB DEFAULT CHARSET=utf8;
MySQL 是用 AUTO_INCREMENT 这个属性来标识字段的自增。
PostgreSQL 使用序列来标识字段的自增长:
CREATE TABLE runoob
(
id serial NOT NULL,
alttext text,
imgurl text
)
SMALLSERIAL、SERIAL 和 BIGSERIAL 范围:
伪类型 | 存储大小 | 范围 |
---|---|---|
SMALLSERIAL |
2字节 | 1 到 32,767 |
SERIAL |
4字节 | 1 到 2,147,483,647 |
BIGSERIAL |
8字节 | 1 到 922,337,2036,854,775,807 |
SERIAL 数据类型基础语法如下:
CREATE TABLE tablename (
colname SERIAL
);
假定我们要创建一张 COMPANY3 表,并创建下面几个字段:
runoobdb=# CREATE TABLE COMPANY3(
ID SERIAL PRIMARY KEY,
NAME TEXT NOT NULL,
AGE INT NOT NULL,
ADDRESS CHAR(50),
SALARY REAL
);
检查 COMPANY 3表中的内容:
runoobdb=# select * from company3;
id | name | age | address | salary
----+------+-----+---------+--------
(0 rows)
现在往表中插入几条记录:
INSERT INTO COMPANY3 (NAME,AGE,ADDRESS,SALARY)
VALUES ( 'Paul', 32, 'California', 20000.00 );
INSERT INTO COMPANY3 (NAME,AGE,ADDRESS,SALARY)
VALUES ('Allen', 25, 'Texas', 15000.00 );
INSERT INTO COMPANY3 (NAME,AGE,ADDRESS,SALARY)
VALUES ('Teddy', 23, 'Norway', 20000.00 );
INSERT INTO COMPANY3 (NAME,AGE,ADDRESS,SALARY)
VALUES ( 'Mark', 25, 'Rich-Mond ', 65000.00 );
INSERT INTO COMPANY3 (NAME,AGE,ADDRESS,SALARY)
VALUES ( 'David', 27, 'Texas', 85000.00 );
INSERT INTO COMPANY3 (NAME,AGE,ADDRESS,SALARY)
VALUES ( 'Kim', 22, 'South-Hall', 45000.00 );
INSERT INTO COMPANY3 (NAME,AGE,ADDRESS,SALARY)
VALUES ( 'James', 24, 'Houston', 10000.00 );
再次检查 COMPANY 3表中的内容:
runoobdb=# select * from company3; id | name | age | address | salary
----+-------+-----+----------------------------------------------------+--------
1 | Paul | 32 | California | 20000
2 | Allen | 25 | Texas | 15000
3 | Teddy | 23 | Norway | 20000
4 | Mark | 25 | Rich-Mond | 65000
5 | David | 27 | Texas | 85000
6 | Kim | 22 | South-Hall | 45000
7 | James | 24 | Houston | 10000
(7 rows)
15,PostgreSQL PRIVILEGES(权限)
无论何时创建数据库对象,都会为其分配一个所有者,所有者通常是执行 create 语句的人。
对于大多数类型的对象,初始状态是只有所有者(或超级用户)才能修改或删除对象。要允许其他角色或用户使用它,必须为该用户设置权限。
在 PostgreSQL 中,权限分为以下几种:
根据对象的类型(表、函数等),将指定权限应用于该对象。
要向用户分配权限,可以使用 GRANT 命令。
GRANT 命令的基本语法如下:
GRANT privilege [, ...]
ON object [, ...]
TO { PUBLIC | GROUP group | username }
另外,我们可以使用 REVOKE 命令取消权限,REVOKE 语法:
REVOKE privilege [, ...]
ON object [, ...]
FROM { PUBLIC | GROUP groupname | username }
为了理解权限,创建一个用户:
runoobdb=# CREATE USER runoob WITH PASSWORD 'password';
CREATE ROLE
信息 CREATE ROLE 表示创建了一个用户 "runoob"。
创建 COMPANY3 表,数据内容如下:
runoobdb=# select * from company3;
id | name | age | address | salary
----+-------+-----+----------------------------------------------------+--------
1 | Paul | 32 | California | 20000
2 | Allen | 25 | Texas | 15000
3 | Teddy | 23 | Norway | 20000
4 | Mark | 25 | Rich-Mond | 65000
5 | David | 27 | Texas | 85000
6 | Kim | 22 | South-Hall | 45000
7 | James | 24 | Houston | 10000
(7 rows)
现在给用户 "runoob" 分配权限:
runoobdb=# GRANT ALL ON COMPANY3 TO runoob;
GRANT
信息 GRANT 表示所有权限已经分配给了 "runoob"。
下面撤销用户 "runoob" 的权限:
runoobdb=# REVOKE ALL ON COMPANY FROM runoob;
REVOKE
信息 REVOKE 表示已经将用户的权限撤销。
你也可以删除用户:
runoobdb=# DROP USER runoob;
DROP ROLE
信息 DROP ROLE 表示用户 "runoob" 已经从数据库中删除。
16,PostgreSQL 时间/日期函数和操作符
下表演示了基本算术操作符的行为(+,*, 等):
操作符 | 例子 | 结果 |
---|---|---|
+ | date '2001-09-28' + integer '7' | date '2001-10-05' |
+ | date '2001-09-28' + interval '1 hour' | timestamp '2001-09-28 01:00:00' |
+ | date '2001-09-28' + time '03:00' | timestamp '2001-09-28 03:00:00' |
+ | interval '1 day' + interval '1 hour' | interval '1 day 01:00:00' |
+ | timestamp '2001-09-28 01:00' + interval '23 hours' | timestamp '2001-09-29 00:00:00' |
+ | time '01:00' + interval '3 hours' | time '04:00:00' |
- | - interval '23 hours' | interval '-23:00:00' |
- | date '2001-10-01' - date '2001-09-28' | integer '3' (days) |
- | date '2001-10-01' - integer '7' | date '2001-09-24' |
- | date '2001-09-28' - interval '1 hour' | timestamp '2001-09-27 23:00:00' |
- | time '05:00' - time '03:00' | interval '02:00:00' |
- | time '05:00' - interval '2 hours' | time '03:00:00' |
- | timestamp '2001-09-28 23:00' - interval '23 hours' | timestamp '2001-09-28 00:00:00' |
- | interval '1 day' - interval '1 hour' | interval '1 day -01:00:00' |
- | timestamp '2001-09-29 03:00' - timestamp '2001-09-27 12:00' | interval '1 day 15:00:00' |
* | 900 * interval '1 second' | interval '00:15:00' |
* | 21 * interval '1 day' | interval '21 days' |
* | double precision '3.5' * interval '1 hour' | interval '03:30:00' |
/ | interval '1 hour' / double precision '1.5' | interval '00:40:00' |
函数 | 返回类型 | 描述 | 例子 | 结果 |
---|---|---|---|---|
age(timestamp, timestamp) |
interval | 减去参数后的"符号化"结果,使用年和月,不只是使用天 | age(timestamp '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 |
clock_timestamp() |
timestamp with time zone | 实时时钟的当前时间戳(在语句执行时变化) | ||
current_date |
date | 当前的日期; | ||
current_time |
time with time zone | 当日时间; | ||
current_timestamp |
timestamp with time zone | 当前事务开始时的时间戳; | ||
date_part(text, timestamp) |
double precision | 获取子域(等效于extract ); |
date_part('hour', timestamp '2001-02-16 20:38:40') | 20 |
date_part(text, interval) |
double precision | 获取子域(等效于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 |
date_trunc(text, interval) |
interval | 截取指定的精度, | date_trunc('hour', interval '2 days 3 hours 40 minutes') | 2 days 03:00:00 |
extract (field from timestamp) |
double precision | 获取子域; | extract(hour from timestamp '2001-02-16 20:38:40') | 20 |
extract (field from interval) |
double precision | 获取子域; | extract(month from interval '2 years 3 months') | 3 |
isfinite(date) |
boolean | 测试是否为有穷日期(不是 +/-无穷) | isfinite(date '2001-02-16') | true |
isfinite(timestamp) |
boolean | 测试是否为有穷时间戳(不是 +/-无穷) | isfinite(timestamp '2001-02-16 21:28:30') | true |
isfinite(interval) |
boolean | 测试是否为有穷时间间隔 | isfinite(interval '4 hours') | true |
justify_days(interval) |
interval | 按照每月 30 天调整时间间隔 | justify_days(interval '35 days') | 1 mon 5 days |
justify_hours(interval) |
interval | 按照每天 24 小时调整时间间隔 | justify_hours(interval '27 hours') | 1 day 03:00:00 |
justify_interval(interval) |
interval | 使用justify_days 和justify_hours 调整时间间隔的同时进行正负号调整 |
justify_interval(interval '1 mon -1 hour') | 29 days 23:00:00 |
localtime |
time | 当日时间; | ||
localtimestamp |
timestamp | 当前事务开始时的时间戳; | ||
make_date(year int, month int, day int) |
date | 为年、月和日字段创建日期 | make_date(2013, 7, 15) | 2013-07-15 |
make_interval(years int DEFAULT 0, months int DEFAULT 0, weeks int DEFAULT 0, days int DEFAULT 0, hours int DEFAULT 0, mins int DEFAULT 0, secs double precision DEFAULT 0.0) |
interval | 从年、月、周、天、小时、分钟和秒字段中创建间隔 | make_interval(days := 10) | 10 days |
make_time(hour int, min int, sec double precision) |
time | 从小时、分钟和秒字段中创建时间 | make_time(8, 15, 23.5) | 08:15:23.5 |
make_timestamp(year int, month int, day int, hour int, min int, sec double precision) |
timestamp | 从年、月、日、小时、分钟和秒字段中创建时间戳 | make_timestamp(2013, 7, 15, 8, 15, 23.5) | 2013-07-15 08:15:23.5 |
make_timestamptz(year int, month int, day int, hour int, min int, sec double precision, [ timezone text ]) |
timestamp with time zone | 从年、月、日、小时、分钟和秒字段中创建带有时区的时间戳。 没有指定timezone时,使用当前的时区。 | make_timestamptz(2013, 7, 15, 8, 15, 23.5) | 2013-07-15 08:15:23.5+01 |
now() |
timestamp with time zone | 当前事务开始时的时间戳; | ||
statement_timestamp() |
timestamp with time zone | 实时时钟的当前时间戳; | ||
timeofday() |
text | 与clock_timestamp 相同,但结果是一个text 字符串; |
||
transaction_timestamp() |
timestamp with time zone | 当前事务开始时的时间戳; |
18,PostgreSQL 常用函数
PostgreSQL 内置函数也称为聚合函数,用于对字符串或数字数据执行处理。
下面是所有通用 PostgreSQL 内置函数的列表:
下面是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 |
函数 | 返回类型 | 描述 | 实例 |
---|---|---|---|
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 | 数字转换为字符串 | to_char(-125.8, '999D99S') |
to_date(text, text) | date | 字符串转换为日期 | to_date('05 Dec 2000', 'DD Mon YYYY') |
to_number(text, text) | numeric | 转换字符串为数字 | to_number('12,454.8-', '99G999D9S') |
to_timestamp(text, text) | timestamp | 转换为指定的时间格式 time zone convert string to time stamp | to_timestamp('05 Dec 2000', 'DD Mon YYYY') |
to_timestamp(double precision) | timestamp | 把UNIX纪元转换成时间戳 | to_timestamp(1284352323) |