为什么选择PostgreSQL
- 功能更全面:PGSQL的功能更加全面,支持开窗函数、物化视图、分区表、json等类型,MySQL8以上支持开窗函数、分区表、json等,但物化视图仍不支持。
- 高可用:PG更适合分布式环境,如流复制、逻辑复制和基于 WAL 日志的复制机制,而MySQL则基于BingLog。
- 数据类型更丰富:支持多种索引类型,如:数组、json,ip类型,text类型。
PostgreSQL和MySQL对比
MySQL | PgSQL | |
功能 | 8以下版本功能弱,8以上版本支持JSON、开窗函数、分区表,目前不支持物化视图 | 支持的功能更多,例如开窗函数、物化视图、JSON、分区表等 |
性能 | 对于读操作性能更好 | 在处理复杂查询和写密集操作时更有优势 |
可扩展性 | 可扩展性不强 | 可扩展性强,支持自定义列类型、插件等 |
PostgreSQL和MySQL如何选择
PgSQL更适合写操作频繁,以及复杂查询的情况,因为PgSQL内置了性能更好的MVCC来解决并发写,MySQL则是通过锁解决并发锁。如果项目中有一些特殊场景,例如json、gis等,则可以选择PgSQL。
MySQL更适合读操作多的情况,读取性能更好。MySQL运维成本更低,上手便捷,社区支持、技术方案更多。
PostgreSQL基础
--数据类型
-- 整数: smallint、int
-- 小数 numeric(5,3)
-- 字符串 char 定长、varchar变长、text无限长
-- 日期 date yyyy-MM-dd
-- 时间 time HH:mm:ss
-- 时间戳 timestamp yyyy-MM-dd HH:mm:ss.0000
数据类型
数字
类型 | 描述 |
int | 整数类型,4字节大小,范围为 -2147483648 到 2147483647 |
int2 | 整数类型,2字节大小,其范围为 -32768 到 32767 |
int4 | 整数类型,4字节大小,范围为 -2147483648 到 2147483647 |
int8 | 范围为 -9223372036854775808 到 9223372036854775807 |
serial、serial2、serial4、serial8 | 等于int,会自动创建序列实现主键自增。 |
numeric(m,n) | 小数类型,小数超长会四舍五入。numeric(5,2)最大为999,99 |
字符串
类型 | 描述 |
char | 定长字符串,不足补空格 |
varchar | 变长字符串 |
text | 无限长度字符串 |
日期
类型 | 描述 |
date | 日期,例如‘2023-11-10’ |
time | 时间,例如‘20:00:00.200’ |
timestamp | date+time |
基本语法
-- 创建数据库
create database "test1" encoding ='utf-8';
-- 修改数据库名称
alter database "test1" rename to "test";
-- 删除数据库
drop database if exists "test1";
-- 查看所有表
select * from pg_tables where schemaname = 'public';
-- 创建表 双引号表示对象,单引号表示字符串。表名为特殊字符需要加“”;
create table "user2" (
id serial8 primary key, -- serial表示主键自增,需要注意的时不能手动指定id了,不然会冲突
name varchar(255) not null default 'zs',
birthday date, -- 日期数据类型 date(日期) time(时间) timestamp(日期加时间)
age int,
money numeric(5,2) -- 小数类型,整数位为3,小数位为2。
);
-- 表注释
comment on table "user2" is '用户表';
comment on column "user2".id is '用户Id';
comment on column "user2".name is '名称';
comment on column "user2".birthday is '生日';
comment on column "user2".age is '年龄';
comment on column "user2".money is '存款';
-- 修改表名
alter table "user" rename to "user2";
-- 修改字段类型
alter table "user2" alter column age type numeric(3,0);
-- 新增字段
alter table "user" add column test numeric(3,0);
-- 修改字段名称
alter table "user" rename column test to test2;
-- 删除字段
alter table "user" drop column test;
-- 插入数据
-- 小数超过会四舍五入,numeric(5,2)表示最大不能超过999.99
-- id自增使用default
insert into "user" values (DEFAULT,'zs','2023-11-09 01:00:00',20.1,999.99);
-- 不要指定id,因为他会和自增id冲突
insert into "user"(id,name,birthday,age,money) values (4,'zs','2023-11-09 01:00:00',20.1,999.994);
-- upsert 不存在新增,存在修改 EXCLUDED是一个临时表,用于存放当前要插入的数据。
INSERT INTO "user"(id,name)
VALUES (default,'ls'),(id','zs2')
ON CONFLICT(id) -- 当id冲突时
DO UPDATE SET -- 进行修改操作
name= "user".name || EXCLUDED.name, -- name=原name+新name (||表示字符串拼接)
birthday=now();
-- 分页查询(刚好和mySQL反过来了)
-- 第一页= limit 3 offset 0
-- 第二页 limit 3 offset 3
-- limit pageSize offset pageNum*pageSize -1
select * from employee limit 3 offset 0;
特殊函数
-- string_agg 类似于mysql的 group_concat
SELECT country,string_agg(city,',') FROM city GROUP BY country;
-- array_agg 功能和string_agg类似,不同的是返回的是数组。
SELECT country,array_agg(city) FROM city GROUP BY country;
PostgreSQL扩展
物化视图
物化视图是一张真实存在的表,能够随着原表的改变而改变(需要手动刷新),主要用于提高一些更新不频繁的大表。
-- 创建物化视图
create materialized view user_view as select u.name,count(a.address) from "user" as u inner join "user_address" as a on u.id = a.user_id group by u.id,u.name;
-- 刷新物化视图
refresh materialized view user_view;
-- 并发刷新(需要创建唯一索引)
create unique index idx_id on user_view(id); -- 为物化视图创建索引
refresh materialized view concurrently user_view; -- 并发刷新
-- 删除物化视图
drop materialized view user_view;
开窗函数
如果想要获取聚合数据,但又不想让数据聚合,那么就可以使用聚合函数。
开窗函数的作用:对查询出来的数据再次进行分析,如聚合、排名、累计等。
例如:获取每个部门薪资排名前三的员工。
select dept_name,emp_name,salary,
rank() over(patition by dept_name order by salary desc) -- 开窗函数,根据部门分组,然后根据薪资排序
from salary_log;
开窗函数语法
想要使用的函数() -- 例如 rank()获取排名、sum()、ave()、count()
over(
partition by 列 -- 要分组的列(类似于group by)
order by 列 -- 排序列
rows between xxx and xxx -- 要选取的行
) -- over表示开窗,配合函数表示开窗函数,一定要加!
-- 执行顺序: 先执行over,然后数据传给前面的函数执行。
-- rows between xxx and xxx 详解:
-- 使用示例:
-- rows between unbounded proceding and current row 从第一行数据开始到当前行。
-- unbounded preceding 表示从当前行的前无限行开始(第一行)
-- 1 preceding 表示从当前行的前1行开始
-- current row 表示读取至当前行。
-- 1 following 表示读取至当前行的后一行
-- unbounded following 表示读取至最后一行
-- rows between 可以省略,不写默认为 rows between unbounded preceding and current row
常用开窗函数
row_number() -- 排名,分数相同,排名不同。
rank() -- 排名,分数相同,排名相同,但末位数不变,例如查询前三名: 1,1,3
dense_rank() -- 排名,分数相同,排名相同,但末位数减少,例如查询前三名 1,1,2
sum( "column" ) -- 求和
count( "column" ) -- 总数
avg( "column" ) -- 求平均分
lag( "column",1,0) -- 求当前行的上1行的值,如果没有上1行则返回0 (一般用于求环比)
leag( "column",1,0)-- 求当前行的下1行的值,如果没有下1行则返回0 (一般用于求环比)
row_number dense_rank
PostgreSQL与 SpringBoot、MybatisPlus 整合
- 引入依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.4.1</version>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
- 配置
spring.datasource.driver-class-name=org.postgresql.Driver
spring.datasource.username=postgres
spring.datasource.password=root
spring.datasource.url=jdbc:postgresql://localhost:5432/test1?currentSchema=public
mybatis-plus.configuration.map-underscore-to-camel-case=true
mybatis-plus.mapper-locations=classpath*:/mapper/**/*.xml
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
- 配置分页插件
@Configuration
public class MybatisPlusConfigure {
//必须配置分页插件,不然分页无效
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
//设置分页插件
PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor();
paginationInnerInterceptor.setOptimizeJoin(true);
//指定数据库方言
paginationInnerInterceptor.setDbType(DbType.POSTGRE_SQL);
paginationInnerInterceptor.setOverflow(true);
interceptor.addInnerInterceptor(paginationInnerInterceptor);
return interceptor;
}
}
- id自增
@Data
@NoArgsConstructor
@AllArgsConstructor
@TableName("employee")
public class Employee {
@TableId(type = IdType.AUTO)
private Long id;
private String name;
private Long deptId;
private String month;
private BigDecimal salary;
private Date sendDate;
}