PostgreSQL 学习笔记
本文最后更新于 282 天前,内容如有失效请评论区留言。

DDL(数据定义语言)

数据库相关

-- 创建数据库
create database testdb;
-- 更改数据库
alter database testdb rename to mydb;
-- 删除数据库
drop database mydb;

表相关

-- 创建表
create table course (
    course_id char(4) primary key ,
    course_name varchar(20) not null unique ,
    course_type varchar(10) null check ( course_type in ('基础课', '专业课', '选修课') ),
    course_credit smallint null,
    course_period smallint null,
    test_method char(4) not null default '闭卷考试'
);

-- 修改表结构
ALTER TABLE student ADD selfinfo varchar(100);
ALTER TABLE student DROP selfinfo;
alter table course rename test_method to exam_method;
alter table course alter column exam_method type char(5);

-- 删除表
drop table course;

索引相关

-- 创建索引
create index course_name_idx on course(course_name);
-- 修改索引
alter index course_name_idx rename to c_n_idx;
-- 删除索引
drop index c_n_idx;

DML(数据操作语言)

-- 插入数据
insert into course(course_id, course_name, course_type, course_credit, course_period) VALUES
    ('001', '数据库', '基础课', '4', '36');
insert into course VALUES
    ('002', '操作系统', '基础课', '4', '36', '闭卷考试');
-- 批量插入数据
insert into course VALUES
    ('003', 'ARM', '基础课', '4', '36', '闭卷考试'),
    ('004', '计网', '基础课', '4', '36', '闭卷考试');

-- 更新数据
update course set course_credit=5, course_period=48 where course_name='计网';

-- 删除数据
delete from course where course_name='计网';

DQL(数据查询语言)

基础

# 去重
select DISTINCT workaddress as 工作地址 from emp;

# NULL 和 NOT NULL 查询
select * from emp where idcard is null;
select * from emp where idcard is not null;

# BETWEEN ... AND ...
select * from emp where age between 15 and 20;

# in
select * from emp where age in(18, 20, 40);

# LIKE 与 通配符
select * from emp where name like '__';
select * from emp where idcard like '%X';

聚合函数

==注意 : NULL值是不参与所有聚合函数运算的。==

# count
select count(*) from emp;
select count(idcard) from emp;

# avg max min
select avg(age) from emp;
select max(age) from emp;
select min(age) from emp;

# sum
select sum(age) from emp where workaddress = '西安';

分组查询

where与having区别

  • 执行时机不同:where是分组之前进行过滤,不满足where条件,不参与分组;而having是分组 之后对结果进行过滤。
  • 判断条件不同:where不能对聚合函数进行判断,而having可以
select gender, count(*) from emp group by gender;
select gender, avg(age) from emp group by gender;

# 查询年龄小于45的员工 , 并根据工作地址分组 , 获取员工数量大于等于3的工作地址
select workaddress, count(*) as address_count from emp where age < 45 group by workaddress having count(*) >= 3;

注意事项:

  • 分组之后,查询的字段一般为聚合函数和分组字段,查询其他字段无任何意义。
  • 执行顺序: where > 聚合函数 > having 。
  • 支持多字段分组, 具体语法为 : group by columnA,columnB

排序查询

排序方式 :

  • ASC : 升序(默认值)

  • DESC: 降序

# 升序和降序
select * from emp order by age asc;
select * from emp order by age desc;
select * from emp order by entrydate desc;

#  根据年龄对公司的员工进行升序排序 , 年龄相同 , 再按照入职时间进行降序排序
select * from emp order by age, entrydate desc;
select * from emp order by age asc, entrydate desc;

注意事项:

  • 如果是升序, 可以不指定排序方式ASC ;
  • 如果是多字段排序,当第一个字段值相同时,才会根据第二个字段进行排序 ;

分页查询

在 PostgreSQL 中,您可以使用 LIMITOFFSET 子句来实现分页查询。LIMIT 用于限制查询结果的数量,OFFSET 用于指定从哪个位置开始返回结果。这里有一个简单的示例:

假设您有一个名为 employees 的数据表,并且您想要查询第 2 页的数据,每页显示 10 条记录。您可以使用以下 SQL 语句实现分页查询:

SELECT * FROM employees
ORDER BY id  -- 按照某个字段排序,这里以 id 为例
LIMIT 10     -- 每页显示 10 条记录
OFFSET 10;   -- 跳过第一页的 10 条记录,从第 11 条记录开始

在这个示例中,我们首先使用 ORDER BY 对结果按照 id 字段进行排序。接着,我们使用 LIMIT 10 限制查询结果为 10 条记录。最后,我们使用 OFFSET 10 跳过第一页的 10 条记录,从第 11 条记录开始返回结果。

您可以根据实际需求调整 LIMITOFFSET 的值以实现不同的分页查询需求。


由于 PostgreSQL 的分页查询语法与 MySQL 略有不同,以上是 ChatGPT 4.0 的回答,

# 查询第1页员工数据, 每页展示10条记录
select * from emp limit 10 offset 0;
select * from emp limit 10;

# 查询第2页员工数据, 每页展示10条记录 
select * from emp limit 10 offset 10;

综合练习

-- 查询年龄为20,21,22,23岁的女员工信息
select * from emp where gender = '女' and age in (20, 21, 22, 23);

-- 查询性别为 男 ,并且年龄在 20-40 岁(含)以内的姓名为三个字的员工
select * from emp where gender = '男' and (age between 20 and 40) and name like '___';

-- 统计员工表中, 年龄小于60岁的 , 男性员工和女性员工的人数
select gender, count(*) 人数 from emp where age < 60 group by gender; 

-- 查询所有年龄小于等于35岁员工的姓名和年龄,并对查询结果按年龄升序排序,如果年龄相同按入职时间降序排序
select name, age, entrydate from emp where age <= 35 order by age asc, entrydate desc;

-- 查询性别为男,且年龄在20-40 岁(含)以内的前5个员工信息,对查询的结果按年龄升序排序,年龄相同按入职时间升序排序
select * from emp where gender = '男' and (age between 20 and 40) order by age asc, entrydate asc limit 5

执行顺序

image-20230424172414110

-- from 先于 where 执行,所以 e.age 可以使用
select name, age from emp e where e.age > 15 order by age asc;

-- select 先于 order 执行,所以 eage 可以使用
select name, age eage from emp e where e.age > 15 order by eage asc;

DCL(数据控制语言)

create user User_Employee_2021090906016 with
    nosuperuser
    nocreatedb
    nocreaterole
    noreplication
    login
    inherit
    connection limit -1
    password '123456';

create user User_Admin_2021090906016 with
    nosuperuser
    nocreatedb
    nocreaterole
    noreplication
    login
    inherit
    connection limit -1
    password '123456';

grant select on Department_2021090906016 to user_employee_2021090906016;
grant select, update on Employee_2021090906016 to user_employee_2021090906016;
grant select on Task_2021090906016 to user_employee_2021090906016;
grant select on Assessment_2021090906016 to user_employee_2021090906016;

revoke select on Department_2021090906016 from user_employee_2021090906016;

实测 pgsql 里没有 deny 语句

image-20230531215815085

约束

约束 描述 关键字
非空约束 限制该字段的数据不能为null NOT NULL
唯一约束 保证该字段的所有数据都是唯一、不重复的 UNIQUE
主键约束 主键是一行数据的唯一标识,要求非空且唯一 PRIMARY KEY
默认约束 保存数据时,如果未指定该字段的值,则采用默认值 DEFAULT
检查约束 保证字段值满足某一个条件 CHECK
外键约束 用来让两张表的数据之间建立连接,保证数据的一致 性和完整性 FOREIGN KEY

注意:约束是作用于表中字段上的,可以在创建表/修改表的时候添加约束。

create table tb_user(
    id serial primary key,
    name varchar(10) not NULL unique,
    age int check(age > 0 AND age <= 120),
    status char(1) default '1',
    gender char(1)
);

COMMENT ON COLUMN tb_user.id IS 'ID唯一标识';
COMMENT ON COLUMN tb_user.name IS '姓名';
COMMENT ON COLUMN tb_user.age IS '年龄';
COMMENT ON COLUMN tb_user.status IS '状态';
COMMENT ON COLUMN tb_user.gender IS '性别';

外键

alter table emp add constraint fk_emp_dept_id foreign key (dept_id) references dept(id);

ALTER TABLE emp DROP CONSTRAINT fk_emp_dept_id;

在 PostgreSQL 中,您不能直接删除外键约束,您需要通过删除约束名称来完成此操作

删除/更新行为

添加了外键之后,再删除父表数据时产生的约束行为,我们就称为删除/更新行为。具体的删除/更新行 为有以下几种:

行为 说明
NO ACTION 当在父表中删除/更新对应记录时,首先检查该记录是否有对应外键,如果有则不 允许删除/更新。 (与 RESTRICT 一致) 默认行为
RESTRICT 当在父表中删除/更新对应记录时,首先检查该记录是否有对应外键,如果有则不 允许删除/更新。 (与 NO ACTION 一致) 默认行为
CASCADE 当在父表中删除/更新对应记录时,首先检查该记录是否有对应外键,如果有,则 也删除/更新外键在子表中的记录。
SET NULL 当在父表中删除对应记录时,首先检查该记录是否有对应外键,如果有则设置子表 中该外键值为null(这就要求该外键允许取null)。
SET DEFAULT 父表有变更时,子表将外键列设置成一个默认的值 (Innodb不支持)
-- CASCADE
alter table emp add constraint fk_emp_dept foreign key (dept_id)  references dept(id) on delete cascade on update cascade

-- SET NULL
alter table emp add constraint fk_emp_dept foreign key (dept_id)  references dept(id) on delete set null on update set null

多表查询

多表关系

项目开发中,在进行数据库表结构设计时,会根据业务需求及业务模块之间的关系,分析并设计表结 构,由于业务之间相互关联,所以各个表结构之间也存在着各种联系,基本上分为三种:

  • 一对多(多对一)
  • 多对多
  • 一对一

一对多

  • 案例: 部门 与 员工的关系
  • 关系: 一个部门对应多个员工,一个员工对应一个部门
  • 实现: 在多的一方建立外键,指向一的一方的主键

image-20230426152452747

多对多

  • 案例: 学生 与 课程的关系
  • 关系: 一个学生可以选修多门课程,一门课程也可以供多个学生选择
  • 实现: 建立第三张中间表,中间表至少包含两个外键,分别关联两方主键

image-20230426152657563

一对一

  • 案例: 用户 与 用户详情的关系
  • 关系: 一对一关系,多用于单表拆分,将一张表的基础字段放在一张表中,其他详情字段放在另 一张表中,以提升操作效率
  • 实现: 在任意一方加入外键,关联另外一方的主键,并且设置外键为唯一的(UNIQUE)

image-20230426152640391

连接查询

  • 连接查询
    • 内连接:相当于查询A、B交集部分数据
    • 外连接: 左外连接:查询左表所有数据,以及两张表交集部分数据
    • 右外连接:查询右表所有数据,以及两张表交集部分数据
    • 自连接:当前表与自身的连接查询,自连接必须使用表别名
-- 多表查询 -- 笛卡尔积
select * from emp, dept;

表的别名:

  1. tablea as 别名1 , tableb as 别名2 ;
  2. tablea 别名1 , tableb 别名2 ;

注意事项:一旦为表起了别名,就不能再使用表名来指定对应的字段了,此时只能够使用别名来指定字 段。

内连接

-- 隐式内连接(where)
select * from emp, dept where emp.dept_id = dept.id;
select e.name, d.name from emp e, dept d where e.dept_id = d.id; /* 起了别名后不能再使用原来的名字 */

-- 显示内连接(inner join)
select e.name, d.name from emp e inner join dept d on e.dept_id = d.id;
select e.name, d.name from emp e join dept d on e.dept_id = d.id;

左外连接和右外连接

-- 查询emp表的所有数据, 和对应的部门信息(左外连接)
select e.*, d.name from emp e left outer join dept d on e.dept_id = d.id;
select e.*, d.name from emp e left join dept d on e.dept_id = d.id;

-- 查询dept表的所有数据, 和对应的员工信息(右外连接)
select d.*, e.* from emp e right outer join dept d on e.dept_id = d.id;

-- 右外连接转换为左外连接
select d.*, e.* from dept d left outer join emp e on e.dept_id = d.id;

注意事项:左外连接和右外连接是可以相互替换的,只需要调整在连接查询时SQL中,表结构的先后顺 序就可以了。而我们在日常开发使用时,更偏向于左外连接。

自连接

对于自连接查询,可以是内连接查询,也可以是外连接查询。

-- 查询员工及其所属领导的名字
select a.name, b.name from emp a, emp b where a.managerid = b.id;
select a.name, b.name from emp a left join emp b on a.managerid = b.id;

注意事项: 在自连接查询中,必须要为表起别名,要不然我们不清楚所指定的条件、返回的字段,到底 是哪一张表的字段。

联合查询

对于union查询,就是把多次查询的结果合并起来,形成一个新的查询结果集。

-- union all, 直接连接(不去重)
select * from emp where salary < 5000
union all
select * from emp where age > 50;

-- union, 去重
select * from emp where salary < 5000
union
select * from emp where age > 50;

子查询

标量子查询

子查询返回的结果是单个值(数字、字符串、日期等),最简单的形式,这种子查询称为标量子查询。 常用的操作符:= <> > >= < <=

-- 查询 "销售部" 的所有员工信息
select * from emp where dept_id = (select id from dept where name = '销售部');

-- 查询在 "方东白" 入职之后的员工信息
select * from emp where entrydate > (select entrydate from emp where name = '方东白')

列子查询

常用的操作符:IN 、NOT IN 、 ANY 、SOME 、 ALL

操作符 描述
IN 在指定的集合范围之内,多选一
NOT IN 不在指定的集合范围之内
ANY 子查询返回列表中,有任意一个满足即可
SOME 与ANY等同,使用SOME的地方都可以使用ANY
ALL 子查询返回列表的所有值都必须满足
-- 查询 "销售部" 和 "市场部" 的所有员工信息
select * from emp where emp.dept_id in (select id from dept where name in ('销售部', '市场部'));

-- 查询比 财务部 所有人工资都高的员工信息
select * from emp where salary > all (select salary from emp where dept_id in (select id from dept where name = '财务部'));

-- 查询比研发部其中任意一人工资高的员工信息
select * from emp where salary > any (select salary from emp where dept_id in (select id from dept where name = '研发部'));

行子查询

子查询返回的结果是一行(可以是多列),这种子查询称为行子查询。 常用的操作符:= 、<> 、IN 、NOT IN

-- 查询与 "张无忌" 的薪资及直属领导相同的员工信息
select salary, managerid from emp where name = '张无忌';
select * from emp where (salary, managerid) = (12500, 1);
select * from emp where (salary, managerid) = (select salary, managerid from emp where name = '张无忌');

表子查询

子查询返回的结果是多行多列,这种子查询称为表子查询。

常用的操作符:IN

--  查询与 "鹿杖客" , "宋远桥" 的职位和薪资相同的员工信息
-- a.查询 "鹿杖客" , "宋远桥" 的职位和薪资
select job, salary from emp where name in ('鹿杖客', '宋远桥'); 
-- b.查询与 "鹿杖客" , "宋远桥" 的职位和薪资相同的员工信息
select * from emp where (job, salary) in (select job, salary from emp where name in ('鹿杖客', '宋远桥'));

-- 查询入职日期是 "2006-01-01" 之后的员工信息 , 及其部门信息
-- a.入职日期是 "2006-01-01" 之后的员工信息
select * from emp where entrydate > '2006-01-01';
-- b.查询这部分员工, 对应的部门信息
select * from (select * from emp where entrydate > '2006-01-01') e left join dept d on e.dept_id = d.id;
select e.*, d.* from (select * from emp where entrydate > '2006-01-01') e left join dept d on e.dept_id = d.id;

-- 不使用子查询也行,下面是我想到的 SQL
select * from emp e left join dept d on e.dept_id = d.id where e.entrydate > '2006-01-01';

综合练习

-- 1.查询员工的姓名、年龄、职位、部门信息 (隐式内连接)
select e.name , e.age , e.job , d.name from emp e , dept d where e.dept_id = d.id;

-- 2.查询年龄小于30岁的员工的姓名、年龄、职位、部门信息(显式内连接)
select e.name , e.age , e.job , d.name from emp e inner join dept d on e.dept_id = d.id where e.age < 30;

-- 3.查询拥有员工的部门ID、部门名称
select distinct d.id , d.name from emp e , dept d where e.dept_id = d.id;

-- 4.查询所有年龄大于40岁的员工, 及其归属的部门名称; 如果员工没有分配部门, 也需要展示出来(外连接)
select e.*, d.name from emp e left join dept d on e.dept_id = d.id where e.age > 40;

-- 5.查询所有员工的工资等级
select e.* , s.grade , s.losal, s.hisal from emp e , salgrade s where e.salary between s.losal and s.hisal;

-- 6.查询 "研发部" 所有员工的信息及工资等级
select t.* from (emp e left join salgrade s on e.salary between s.losal and s.hisal) t join dept on (t.dept_id = dept.id and dept.name = '研发部');
select e.* , s.grade from emp e , dept d , salgrade s where e.dept_id = d.id and (e.salary between s.losal and s.hisal ) and d.name = '研发部';

-- 7.查询 "研发部" 员工的平均工资
select avg(e.salary) from emp e, dept d where e.dept_id = d.id and d.name = '研发部';

-- 8.查询工资比 "灭绝" 高的员工信息。
select * from emp where salary > (select salary from emp where name = '灭绝');
-- 自连接, my answer
select e1.* from emp e1, emp e2 where e2.name = '灭绝' and e1.salary > e2.salary;

-- 9.查询比平均薪资高的员工信息
select * from emp where salary > (select avg(salary) from emp);

-- 10.查询低于本部门平均工资的员工信息
-- my answer
select dept_id, avg(salary) from emp group by dept_id;
select e.*, t.avg from emp e, (select dept_id, avg(salary) avg from emp group by dept_id) t where e.dept_id = t.dept_id and e.salary < t.avg;
-- Jury's answer
select * from emp e2 where e2.salary < ( select avg(e1.salary) from emp e1 where e1.dept_id = e2.dept_id );

-- 11.查询所有的部门信息, 并统计部门的员工人数
-- my answer,存在BUG: 员工数量为0的部门查询不到
select dept_id, count(*) from emp group by dept_id;
select d.*, t.count from dept d, (select dept_id, count(*) from emp group by dept_id) t where d.id = t.dept_id
-- Jury's answer
select d.id, d.name, ( select count(*) from emp e where e.dept_id = d.id ) 人数 from dept d;

-- 12.查询所有学生的选课情况, 展示出学生名称, 学号, 课程名称
select s.name , s.no , c.name from student s , student_course sc , course c where s.id = sc.studentid and sc.courseid = c.id ;

事务

事务 是一组操作的集合,它是一个不可分割的工作单位,事务会把所有的操作作为一个整体一起向系 统提交或撤销操作请求,即这些操作要么同时成功,要么同时失败。

注意: 默认MySQL的事务是自动提交的,也就是说,当执行完一条DML语句时,MySQL会立即隐 式的提交事务。

事务操作

在 PostgreSQL 中,您可以通过 SHOW TRANSACTION ISOLATION LEVEL 查询事务隔离级别以了解事务是否自动提交。以下是将 MySQL 的 SQL 语句转换为 PostgreSQL 支持的 SQL 语句:

SHOW TRANSACTION ISOLATION LEVEL;

如果返回的隔离级别是 "READ COMMITTED",那么 PostgreSQL 默认使用自动提交。

begin; /* 或者 start transaction */
-- 1. 查询张三余额
select * from account where name = '张三';

-- 2. 张三的余额减少1000
update account set money = money - 1000 where name = '张三';

-- 3. 李四的余额增加1000
update account set money = money + 1000 where name = '李四';

-- 如果执行过程中报错, 则回滚事务
-- rollback;

-- 如果正常执行完毕, 则提交事务
commit;

-- 恢复
update account set money = 2000 where name = '张三' or name = '李四';

注意:上述的这种方式,我们是修改了事务的自动提交行为, 把默认的自动提交修改为了手动提 交, 此时我们执行的DML语句都不会提交, 需要手动的执行commit进行提交。

事务四大特性

  • 原子性(Atomicity):事务是不可分割的最小操作单元,要么全部成功,要么全部失败。
  • 一致性(Consistency):事务完成时,必须使所有的数据都保持一致状态。
  • 隔离性(Isolation):数据库系统提供的隔离机制,保证事务在不受外部并发操作影响的独立 环境下运行。
  • 持久性(Durability):事务一旦提交或回滚,它对数据库中的数据的改变就是永久的。

上述就是事务的四大特性,简称ACID。

并发事务问题

  • 赃读:一个事务读到另外一个事务还没有提交的数据。
  • 不可重复读:一个事务先后读取同一条记录,但两次读取的数据不同,称之为不可重复读。
  • 幻读:一个事务按照条件查询数据时,没有对应的数据行,但是在插入数据时,又发现这行数据 已经存在,好像出现了 "幻影"。

事务隔离级别

为了解决并发事务所引发的问题,在数据库中引入了事务隔离级别。主要有以下几种:

隔离级别 脏读 不可重复读 幻读
Read uncommitted 未解决 未解决 未解决
Read committed(PostgreSQL 默认) 解决 未解决 未解决
Repeatable Read(MySQL 默认) 解决 解决 未解决
Serializable 解决 解决 解决

注意:事务隔离级别越高,数据越安全,但是性能越低。

不可重复读 复现示例:

image-20230429173835553

-- 显示当前会话的默认隔离级别
show transaction isolation level ;

要更改当前会话的默认隔离级别为 SERIALIZABLE,您可以使用 SET 语句,而不需要在事务中进行设置。以下是将当前会话的默认隔离级别更改为 SERIALIZABLE 的示例:

SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL SERIALIZABLE;

在执行此语句后,当前会话中启动的所有新事务将使用 SERIALIZABLE 隔离级别。要查看当前会话的默认隔离级别,您可以使用以下查询:

SHOW default_transaction_isolation;

这将返回当前会话的默认隔离级别,如 SERIALIZABLE

请注意,这里设置的默认隔离级别仅适用于当前会话。当会话结束时,这个设置将失效。如果您想要为整个数据库设置默认的隔离级别,需要更改 postgresql.conf 配置文件中的 default_transaction_isolation 设置,并重启数据库服务。

如果只需要在一次事务中更改隔离级别的话,可以这样写:

BEGIN; -- 开始一个新事务
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE; -- 设置事务隔离级别为 SERIALIZABLE
-- 在这里执行您的事务操作(如查询、插入、更新等)
COMMIT; -- 提交事务

视图

视图(View)是一种虚拟存在的表。视图中的数据并不在数据库中实际存在,行和列数据来自定义视 图的查询中使用的表,并且是在使用视图时动态生成的。

通俗的讲,视图只保存了查询的SQL逻辑,不保存查询结果。所以我们在创建视图的时候,主要的工作 就落在创建这条SQL查询语句上。

基本语法

-- 创建视图
create or replace view stu_view as
    select id, name from student where id <= 10;

-- 查询视图定义
select pg_get_viewdef('stu_view', true);
select viewname, definition from pg_views where viewname = 'stu_view' and schemaname = 'public';

-- 查询视图
select * from stu_view where id < 3;

-- 修改视图
create or replace view stu_view as select id,name,no from student where id <= 10;
-- 在 PostgreSQL 中,您不能直接修改现有视图的定义,而是需要使用 CREATE OR REPLACE VIEW 语句来重新创建视图
-- alter view stu_view as select id,name from student where id <= 10;

-- 删除视图
drop view if exists stu_view;

检查选项

当使用 WITH CHECK OPTION 子句创建视图时,MySQL 会通过视图检查正在更改的每个行,例如 插 入,更新,删除,以使其符合视图的定义。 MySQL 允许基于另一个视图创建视图,它还会检查依赖视 图中的规则以保持一致性。为了确定检查的范围,MySQL 提供了两个选项: CASCADED 和 LOCAL ,默认值为 CASCADED 。

create view v1 as
    select id, name from student where id <= 20;

create view v2 as
    select id, name from v1 where id >= 10 with cascaded check option ;

insert into v1 values (25, 'pgsql'); /* 成功 */
insert into v2 values (15, 'pgsql'); /* 失败 */

create view v3 as
    select id, name from v2 where id >= 5 with local check option ;

insert into v3 values (8, 'pgsql'); /* 失败 */

create or replace view v3 as
    select id, name from v1 where id >= 5 with local check option ;

insert into v3 values (8, 'pgsql'); /* 成功 */

视图的更新

要使视图可更新,视图中的行与基础表中的行之间必须存在一对一的关系。如果视图包含以下任何一 项,则该视图不可更新:

​ A. 聚合函数或窗口函数(SUM()、 MIN()、 MAX()、 COUNT()等)

​ B. DISTINCT

​ C. GROUP BY

​ D. HAVING

​ E. UNION 或者 UNION ALL

视图作用

  1. 简单

    视图不仅可以简化用户对数据的理解,也可以简化他们的操作。那些被经常使用的查询可以被定义为视 图,从而使得用户不必为以后的操作每次指定全部的条件

  2. 安全

    数据库可以授权,但不能授权到数据库特定行和特定的列上。通过视图用户只能查询和修改他们所能见到的数据

  3. 数据独立

    视图可帮助用户屏蔽真实表结构变化带来的影响

存储过程

存储过程是事先经过编译并存储在数据库中的一段 SQL 语句的集合,调用存储过程可以简化应用开发 人员的很多工作,减少数据在数据库和应用服务器之间的传输,对于提高数据处理的效率是有好处的。

存储过程思想上很简单,就是数据库 SQL 语言层面的代码封装与重用。

基本语法

在 PostgreSQL 中,函数(Function)是一种类似于存储过程的可重用对象,它们通常用于封装一组 SQL 语句以执行特定任务、实现复杂的业务逻辑或封装常用操作。与存储过程不同,函数需要返回一个值。您可以使用 PL/pgSQL(一种类似于 SQL 的过程性语言)或其他过程性语言创建函数。

创建函数的基本语法如下:

CREATE [OR REPLACE] FUNCTION function_name (parameter_name parameter_type [,...])
RETURNS return_type
LANGUAGE plpgsql
AS $$
-- 函数的 PL/pgSQL 代码
$$;

其中:

  • CREATE [OR REPLACE]: 如果指定了 OR REPLACE,在创建函数时,如果同名函数已存在,则会用新的定义替换旧的定义。
  • FUNCTION function_name: 函数的名称。
  • parameter_name parameter_type: 函数的输入参数,可以有多个,用逗号分隔。参数有名称和类型。
  • RETURNS return_type: 函数返回值的类型。
  • LANGUAGE plpgsql: 指定函数使用的编程语言。在这里,我们使用 PL/pgSQL。
  • AS $$: 函数的 PL/pgSQL 代码部分的开始。
  • $$;: 函数的 PL/pgSQL 代码部分的结束。

要调用函数,您可以在 SQL 查询中像调用常规函数那样调用它:

SELECT function_name(parameter_values);

其中 function_name 是函数的名称,parameter_values 是传递给函数的参数值。

以下是一个简单的函数示例,该函数将两个整数相加并返回结果:

CREATE OR REPLACE FUNCTION add_numbers(a INTEGER, b INTEGER)
RETURNS INTEGER
LANGUAGE plpgsql
AS $$
BEGIN
  RETURN a + b;
END;
$$;

调用此函数:

SELECT add_numbers(3, 5);

这将返回 3 + 5 的结果,即 8


-- 存储过程基本语法
-- 创建
CREATE OR REPLACE FUNCTION p1()
RETURNS bigint AS $$
BEGIN
    RETURN (SELECT count(*) FROM student);
END;
$$ LANGUAGE plpgsql;

-- 调用
select p1();

-- 删除
drop function if exists p1;

触发器

触发器是与表有关的数据库对象,指在 insert/update/delete 之前 (BEFORE) 或之后 (AFTER) ,触 发并执行触发器中定义的 SQL 语句集合。触发器的这种特性可以协助应用在数据库端确保数据的完整性 , 日志记录 , 数据校验等操作 。

使用别名 OLD 和 NEW 来引用触发器中发生变化的记录内容,这与其他的数据库是相似的。MySQL 不支持语句级触发,但 PostgreSQL 支持语句级触发

-- 插入数据触发器
create or replace function tb_user_insert_trigger_function()
returns trigger as $$
begin
    insert into user_logs(operation, operate_time, operate_id, operate_params)
    values (tg_op, now(), new.id, concat('insert values: ', 'id = ', new.id, ', name = ', new.name, ', phone = ', new.phone, ', email = ', new.email));
    return null;
end;
$$ language plpgsql;

create trigger tb_insert_tri
    after insert on tb_user for each row
    execute function tb_user_insert_trigger_function();

drop trigger if exists tb_insert_tri on tb_user;

insert into tb_user(id, name, phone, email, profession, age, gender, status, createtime)
values (25,'二皇子','18809091212','erhuangzi@163.com','软件工程',23,'1','1',now());

-- 更新数据触发器
create or replace function tb_user_update_trigger_function()
returns trigger language plpgsql as $$
begin
    insert into user_logs(operation, operate_time, operate_id, operate_params)
    values (tg_op, now(), new.id, concat('old values: ', 'id = ', old.id, ', name = ', old.name, ', phone = ', old.phone, ', email = ', old.email,
            ' new values: ', 'id = ', new.id, ', name = ', new.name, ', phone = ', new.phone, ', email = ', new.email));
    return new;
end;
$$;

create trigger tb_update_tri
    after update on tb_user for each row
    execute function tb_user_update_trigger_function();

update tb_user set name = 'owo' where id = 25;

-- 删除数据触发器
create or replace function tb_user_delete_trigger_function()
returns trigger language plpgsql as $$
begin
    insert into user_logs(operation, operate_time, operate_id, operate_params)
    values (tg_op, now(), old.id, concat('delete values: ', 'id = ', old.id, ', name = ', old.name, ', phone = ', old.phone, ', email = ', old.email));
    return old;
end;
$$;

create trigger tb_delete_tri
    after delete on tb_user for each row
    execute function tb_user_delete_trigger_function();

delete from tb_user where id = 25;

游标

游标在 PostgreSQL 中是一个数据库查询,它允许你从一个大的结果集中一次获取一行记录。这对于处理大量数据非常有用,因为你不需要一次性加载所有数据。

在 PostgreSQL 中,游标通常在 PL/pgSQL 代码块(如函数或存储过程)中声明和使用,或者在事务中声明使用

image-20230602182142989

-- 游标案例1
create or replace function cursorDemo()
returns boolean language plpgsql as
$$
declare
    emp_cur cursor for select * from emp;
    re record;
begin
    open emp_cur;
    loop
        fetch next from emp_cur into re;
        exit when not found;
        raise notice '%, %', re.name, re.job;
    end loop;
    close emp_cur;
    return true;
end;
$$;

select cursorDemo();

-- 游标案例2
create or replace function cursorDemo2(in str varchar)
returns table(name varchar, job varchar) language plpgsql as
$$
declare
    emp_cur refcursor;
    re record;
begin
    open emp_cur for select * from emp where emp.job = str;
    fetch emp_cur into re;
    while FOUND loop
        name := re.name;
        job := re.job;
        return next;
        fetch emp_cur into re;
    end loop;
    close emp_cur;
end;
$$;

select * from cursorDemo2('职员');

杂项

select now();
SELECT current_time AT TIME ZONE 'Asia/Shanghai';
show time zone ;
SHOW config_file;
SET TIME ZONE 'Asia/Shanghai';

备份与恢复

在 PostgreSQL 中,备份通常使用 pg_dumppg_dumpall 命令行工具进行,而不是通过 SQL 语句。

例如,你可以使用以下命令来备份一个名为 "sample" 的数据库:

pg_dump sample > sample.bak

这个命令将创建一个包含 "sample" 数据库所有数据和结构的 SQL 文件 "sample.bak"。

如果你想备份所有数据库,你可以使用 pg_dumpall 命令:

pg_dumpall > alldbs.bak

这个命令将创建一个包含所有数据库的 SQL 文件 "alldbs.bak"。

请注意,这些命令需要在命令行中运行,而不是在 SQL 查询中。你可能需要具有适当的权限才能运行这些命令,具体取决于你的 PostgreSQL 安装和配置。

image-20230604174900982

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇