disgare 的博客
首页
博客
分类
标签
首页
博客
分类
标签
  • 网络

    • 计算机网络学习笔记
    • 网络安全相关
    • 域名和子网掩码
    • CORS 跨域资源共享
    • DNS、HTTP 与 HTTPS
    • Server-Sent Events (SSE)
    • WebSocket 长连接
  • 计算机基础

    • 操作系统 IO 相关知识
    • 操作系统学习笔记
    • 程序的机器级表示
    • 音频文件基础
    • 正则表达式相关概念
    • ffmpeg 的安装以及实现音频切分功能
    • Hex 和 Base64 编码
    • XML 的使用
  • 数据结构与算法

    • 动态规划算法学习笔记
    • 基于比较的排序算法的最坏情况下的最优下界为什么是O(nlogn)
    • 集合与数据结构学习笔记
    • 面试常见算法总结
    • 算法导论第二部分排序学习笔记
    • 算法导论第一部分学习笔记
  • Java

    • 对象之间的映射与转换
    • 反射学习笔记
    • 泛型相关概念
    • 关于 boolean 类型的坑
    • 如何使用 lambda 表达式实现排序
    • CompletableFuture 相关用法
    • CompletableFuture 源码浅要阅读
    • FutureTask 源码阅读
    • Guava 常用 API
    • Guava 源码阅读:Multimap 相关
    • Jackson 的各种使用
    • Java 的 Excel 相关操作
    • java 的常见性能问题分析以及出现场景
    • java 基础知识
    • JAVA 枚举的基础和原理
    • Java 图片文件上传下载处理
    • Java 序列化
    • Java 异常
    • Java 语法糖
    • Java 中关于字符串处理的常用方法
    • Java 中强、软、弱、虚引用
    • JAVA 注解小结
    • Java Http 访问框架
    • Java Stream 的使用
    • Java8 新特性
    • netty 学习笔记
    • Scanner 的各种用法
    • Servlet 学习笔记
    • String、StringBuffer、StringBuilder 学习笔记
  • JVM

    • 虚拟机执行子系统
    • JVM 自动内存管理
    • Linux 中 JVM 常用工具以及常见问题解决思路
  • Linux

    • crontab 表达式
    • Linux 常见命令
    • Linux 文件系统
  • 中间件

    • 关于定时任务原理
    • 详解 kafka
    • ES 搜索引擎
    • flink 提交流程
    • Grape-RAG
    • Hadoop 基础原理
  • 多线程

    • 多线程基础学习笔记
    • 简单了解并发集合
    • 如何手写单例
    • 深入理解 java 多线程安全
    • 生产者消费者问题
    • 线程池作用、用法以及原理
    • AQS 组件
    • ThreadLocal 原理以及使用
  • 非关系型数据库

    • Redis 集群
    • Redis 数据结构、对象与数据库
    • Redis 学习笔记
  • 关系型数据库

    • B+ 树的插入、删除和数据页分裂机制
    • MySQL 的 binglog、redolog、undolog
    • MySQL 的记录存储结构、存储引擎与 Buffer Pool
    • MySQL 基本的特性
    • MySQL 开发规范
    • MySQL 事务与锁与 MVCC
    • MySQL 数据类型、字符集相关内容
    • MySQL 索引与索引优化
    • PostgreSQL 更新数据时 HOT优化
    • PostgreSQL 相关用法
      • 常见语法
        • NUMERIC
        • SERIAL
        • LIMIT
        • WITH
        • 连接
        • UNION 联表
        • :: 强制转换
        • 正则匹配
        • ON CONFLICT
        • 建立索引
        • gin 索引原理
        • pgstatactivity 查询正在执行的语句和杀掉卡住的语句
        • 设置东8区
      • 常见函数
        • to_char 日期转字符串
        • to_timestamp 字符串转日期
        • date_part
        • extract (epoch | year... from timestamp)
        • string_agg
      • JSON 与 JSONB
        • 查询操作符
        • 通过 json 的键获取 json 的值
        • #> 获取指定路径上的对象
        • ?判断 json 中是否存在某个键
        • @> 判断 json 中是否存在某个键值对
        • 修改操作符
        • 函数
        • jsonbarrayelements 操作 json 数组
      • hstore
        • 操作符
        • 增删改
      • 数组
        • 操作符
        • 函数
      • ltree
        • 查询操作符
      • 性能分析
        • generate_series 生成数据
        • explain analyze
  • Python

    • Python 基础语法
    • Python 学习
  • Spring 项目

    • Lombok 的常用注解
    • maven 小结
    • MyBatis 框架的使用
    • MyBatis 重要知识点总结
    • MybatisPlus 的使用
    • Spring 框架基础使用
    • Spring 事务相关
    • Spring IOC 的原理及源码
    • Spring AOP 的使用和原理
    • SpringBoot 的原理
    • SpringBoot 基础使用
    • SpringWeb 重要知识点
  • 分布式

    • 初步了解 docker
    • 从 ACID 到 BASE 事务处理的实现
    • 访问远程服务
    • 分布式 id
    • 分布式缓存相关问题
    • 分布式集群理论和分布式事务协议
    • 分布式架构的观测
    • 分布式一致性算法
    • 负载均衡 Load Balancing
    • 关于分布式系统 RPC 中高可用功能的实现
    • 集群间数据同步的目的
    • 三高问题下的系统优化
    • 数据库分库分表
    • 详解 Spring Cloud
    • Dubbo 基础概念
    • Gossip 协议
    • nginx 学习笔记
    • Protobuf 通信协议
    • Zookeeper 基础学习
  • 架构设计

    • 参数校验与异常处理
    • 抽象方法与设计模式
    • 代码整洁之道
    • 权限系统设计
    • 用低内存处理大量数据
    • 设计模式——策略模式
    • 设计模式——过滤器模式在 Spring 中的实践
    • 状态模式
    • 统一结果返回
    • 为什么要打日志?怎么打日志?打什么日志?
    • 运维监控常见指标含义
    • 资深研发进阶
    • DDD 架构学习笔记
    • Java 常用的规则引擎
    • MVC 架构学习笔记
  • AI

    • 如何编写 Prompt
    • Agent 工程架构
    • LLM 相关内容
    • NLP 相关知识
    • vibe coding 最佳实践
    • windows 下 ollama 迁移到 D 盘
  • 开发工具

    • 如何画时序图、流程图、状态流转图
    • excel 关于 =vlookup 的用法
    • git 的学习以及使用
    • IDEA 插件推荐
    • IDEA 常用快捷键以及调试
    • Shell 脚本
    • swagger 的使用
  • 前端

    • 简单了解前端页面开发
    • 伪静态是什么
    • GitHub Pages 部署教程
    • Vercel 部署教程
    • vue-admin-template 简单使用
    • VuePress 博客搭建指南
  • 项目

    • 面试刷题网——技术方案
    • 影视资源聚合站——技术方案
  • 问题记录

    • 定时任务单线程消费 redis 中数据导致消费能力不足
    • 提供可传递的易受攻击的依赖项
    • Liteflow 在 SpringBoot 启动时无法注入组件问题 couldn‘t find chain with the id[THEN(NodeComponent)]
  • 金融

    • 股票分析——关于电力
    • 股票技术面——量价关系
    • 股票技术面——盘口
    • 股票技术面——基础
    • 基础的金融知识
    • 基金与股票
    • 韭菜的自我总结
    • 聊聊价值投资
  • 其他

    • 程序员职场工作需要注意什么
    • 创业全链路SOP:从灵光一现到系统化增长的实战指南
    • 观罗翔讲刑法随笔
    • 价格和价值
    • 立直麻将牌效益理论
    • 梅花易数学习笔记
    • 压力管理
2022-10-03
关系型数据库
目录

PostgreSQL 相关用法

# 常见语法

PG 大部分语法与 MySQL 一致,只有略微的区别。其中包含很多非常有用并且有趣的功能

# NUMERIC

NUMERIC 类型的语法:

NUMERIC(precision, scale)
1

precision 表示整个数据长度,scale 表示小数部分的长度。如: 1234.567 ,precision 为 7 ,scale 为 3

NUMERIC 类型在小数点前面长度可达到 131,072 ,小数点后面长度可达到 16,383。scale 可以为 0 或正数,下面示例表示 scale 为 0:

NUMERIC(precision)
1

如果 precision 和 scale 都忽略,则可以存储任何上面提及限制内的长度和精度

我们可以使用,更精确的计算结果。浮点数存在精度问题,可能会出现计算误差,而使用 numeric(5,2) 可以保证计算结果的精度

该类型在 java 中直接对应 BigDecimal 类型。在 PostgreSQL中 NUMERIC 和 DECIMAL 是等价的

# SERIAL

postgresql 序列号(SERIAL)类型包括 smallserial(smallint,short),serial(int) 和 bigserial(bigint,long long int),不管是 smallserial,serial 还是 bigserial,其范围都是(1,9223372036854775807),但是序列号类型其实不是真正的类型,当声明一个字段为序列号类型时其实是创建了一个序列,INSERT 时如果没有给该字段赋值会默认获取对应序列的下一个值,因此我们常常用它来作为主键

在创建表时输入这个值,事实上是为主键的默认值设置为 nextval,比如 在这里插入图片描述 我们可以使用查询语句查出这个值,也可以手动修改它的默认值

SELECT nextval('crm_user_info_v_id_seq'::regclass);
alter sequence if exists crm_user_info_v_id_seq restart with 50 cache 1;
select setval('crm_allocation_model_id_seq', max(id)) from crm_allocation_model;
1
2
3

# LIMIT

带有 LIMIT 子句的 SELECT 语句的基本语法如下:

SELECT column1, column2, columnN
FROM table_name
LIMIT 10
1
2
3

下面是 LIMIT 子句与 OFFSET 子句一起使用时的语法,OFFSET 可以允许从一个特定的偏移开始提取记录:

SELECT column1, column2, columnN 
FROM table_name
LIMIT [no of rows] OFFSET [row num]

-- 从第三位开始提取 10 个记录
SELECT column1, column2, columnN 
FROM table_name
LIMIT 10 OFFSET 2
1
2
3
4
5
6
7
8

mysql 是在 limit 后跟两个数字,以表示每页多少数据以及偏移量

# WITH

pg 带有 with 语句,可以定义一个子查询,然后再其他的查询中复用这个子查询,比如

with aaa as (select * from user_coupon)
select user_name from aaa;
1
2

注意 with 语句不要带分号

# 连接

PostgreSQL JOIN 子句用于把来自两个或多个表的行结合起来,基于这些表之间的共同字段

在 PostgreSQL 中,JOIN 有五种连接类型:

CROSS JOIN :交叉连接 INNER JOIN:内连接 LEFT OUTER JOIN:左外连接 RIGHT OUTER JOIN:右外连接 FULL OUTER JOIN:全外连接

pg 的连接做的比 MySQL 好,因此可以尽情使用联表了

# UNION 联表

用于将两个表的查询结果连接在一起,这里的连接不是联表的连接,而是纵向的连接,将两个查询结果合起来返回给用户

SELECT column1 [, column2 ]
FROM table1 [, table2 ]
[WHERE condition]

UNION

SELECT column1 [, column2 ]
FROM table1 [, table2 ]
[WHERE condition]
1
2
3
4
5
6
7
8
9

请注意,UNION 内部的每个 SELECT 语句必须拥有相同数量的列。列也必须拥有相似的数据类型。同时,每个 SELECT 语句中的列的顺序必须相同

这个功能还是很好用的,比如可以同时使用多种查询条件选择出数据

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]
1
2
3
4
5
6
7
8
9

# :: 强制转换

使用两个冒号来表示强制类型转换,下面的 sql,虽然 user_id 是 int 类型,不过查询出来的是 char 类型数据

select user_id::char
            from user
1
2

# 正则匹配

pg 在使用正则表达式的时候需要使用关键字~,表示关键字之前的内容要和之后的内容进行匹配,不匹配则在关键字之前加!可以直接使用飘号进行首字符匹配

select user_name from user where user_name ~ 'timo'
1

# ON CONFLICT

该语句用于实现当记录不存在时,执行插入;否则,进行更新这种操作

在关系数据库中,术语 upsert 被称为合并(merge)。意思是,当执行 INSERT 操作时,如果数据表中不存在对应的记录,PostgreSQL 执行插入操作;如果数据表中存在对应的记录,则执行更新操作。这就是将其称为 upsert(update or insert)的原因

模板

INSERT INTO table_name(column_list) VALUES(value_list)
ON CONFLICT target action;
1
2

target 可以是:

  • (column_name):一个或者一些字段名,左右两边加上小括号
  • WHERE predicate:带谓语的 WHERE 子句

action 可以是:

  • DO NOTHING:当记录存在时,什么都不做
  • DO UPDATE SET column_1 = value_1, … WHERE condition:当记录存在时,更新表中的一些字段

以下例子表示,向表 customers 中插入数据,如果插入的 name 存在,则更改 email 的值

INSERT INTO customers (name, email)
VALUES
 (
 'Microsoft',
 'hotline@microsoft.com'
 ) 
ON CONFLICT (name) 
DO
 UPDATE
   SET email = EXCLUDED.email;
1
2
3
4
5
6
7
8
9
10

该语法有以下几个要点:

1,支持在括号中使用多个属性,以下查询需要对 col1 和 col2 共同使用唯一索引,这样的索引不能保证 col1 和 col2 分别唯一

INSERT INTO dupes VALUES(3,2,'c')
ON CONFLICT (col1,col2) DO UPDATE SET col3 = 'c', col2 = 2
1
2

2,excluded 的使用,我们可以看到上面的语句使用起来非常不方便,已经输入了数据还要再输入一遍,同时,如果是执行批量插入的语句的话基本上无法使用 conflict 了。因此提供了 excluded 来获取对应行的对应值

3,向表中更新新值是使用 excluded,但是我们在某种情况下还需要获取原来的数据做操作,此时我们应该这么写

insert into weather2 as tos (city,temp_lo,date) SELECT p,q,s::date
FROM ROWS FROM
    (
		json_to_recordset('[{"a":40,"b":"sh"},{"a":"100","b":"qd"},{"a":"10","b":"qdd"},{"a":"3","b":"bj"}]')
            AS (b TEXT,a INTEGER),
        generate_series(1, 4),
		generate_series('2022-08-06'::date,'2022-08-09'::date,'1 day')
    ) AS x (p,q,r,s)
-- ORDER BY p
-- on conflict(date, city) do update set temp_lo = excluded.temp_lo; --保留当前要新插入的值
-- on conflict(date, city) do update set temp_lo = tos.temp_lo; --保留原始值
-- on conflict(date, city) do nothing; --保留原始值
on conflict(date, city) do update set temp_lo = tos.temp_lo+excluded.temp_lo; -- 原始值与当前值相加
1
2
3
4
5
6
7
8
9
10
11
12
13

这里额外说明一下 CONFLICT 的性能,ON CONFLICT 需要检查唯一约束或索引来确定冲突,冲突解决过程可能需要获取行锁

# 建立索引

建立索引时并行建立,不会影响数据库查询

create index concurrently on user_info (user_name)
1

使用 xx 索引

create index concurrently on user_info using gin (user_name)
create index concurrently on user_info using hash (user_name)
1
2

启动 gin 模块(gin 一般用于数组)

create extension if not exists btree_gin;
1

# gin 索引原理

GIN 是 Generalized Inverted Index 的缩写。就是所谓的倒排索引,它用于处理类似数组、ltree 等字段。它处理的数据类型的值不是原子的,而是由元素构成。我们称之为复合类型。如(‘hank’, ‘15:3 21:4’)中,表示hank在15:3和21:4这两个位置出现过

GIN 的主要应用领域是加速全文搜索,所以,这里我们使用全文搜索的例子介绍一下GIN索引。如下,建一张表:

postgres=## create table ts(doc text, doc_tsv tsvector);

postgres=## insert into ts(doc) values
  ('Can a sheet slitter slit sheets?'), 
  ('How many sheets could a sheet slitter slit?'),
  ('I slit a sheet, a sheet I slit.'),
  ('Upon a slitted sheet I sit.'), 
  ('Whoever slit the sheets is a good sheet slitter.'), 
  ('I am a sheet slitter.'),
  ('I slit sheets.'),
  ('I am the sleekest sheet slitter that ever slit sheets.'),
  ('She slits the sheet she sits on.');

postgres=## create index on ts using gin(doc_tsv);
1
2
3
4
5
6
7
8
9
10
11
12
13
14

该 GIN 索引结构如下,黑色方块是 TID 编号,比如0-2代表该单词在第0行的第2个元素中出现过,白色为单词 在这里插入图片描述

由上可见,sheet、slit、slitter 出现在多行之中,会有多个 TID,这样就会生成一个 TID 列表,并为之生成一棵单独的B-tree,以此增加搜索速度

总结来说,gin 就是将数组字段中的每一个元素都取出来,对每个元素都建立一个 b+ 索引。而索引最终存放的值也是一个 b+ 树,这个 b+ 树存放的是该元素出现的行以及编号

# pg_stat_activity 查询正在执行的语句和杀掉卡住的语句

有些更新语句非常耗时间卡住了其他的 sql 执行,我们可以通过以下语句查询正在执行的语句并且查询是否有 lock 的语句

select pg_blocking_pids(pid), pid, now() - xact_start, wait_event, wait_event_type, substr(query, 1, 100)
from pg_stat_activity
where state <> 'idle'
order by 3 desc;
1
2
3
4

order by 3 代表按第三列排序。我们查出 pid 后可以杀掉 pid

select pg_terminate_backend(21829);
1

# 设置东8区

set time zone 'PRC'
1

# 常见函数

# to_char 日期转字符串

to_char(cjsj, 'yyyy-mm-dd hh24:MI:SS')
1

# to_timestamp 字符串转日期

to_timestamp(cjsj, 'yyyy-mm-dd hh24:MI:SS')
1

# date_part

date_part('day', TIMESTAMP '2018-03-01 08:00:00') 结果是 1,如果用 hour 就可以求出小时数为 8

select date_part('day', biztime), sum(qty) from datatable where date_part('day',biztime) >= 1
1

# extract (epoch | year... from timestamp)

extract 用来提取时间类型的某个数据,比如年月日时分秒,以及从 linux 数据戳开始到现在的 long 类型时间

select extract(epoch from '2023-03-03'::timestamp);
select extract(month from '2023-03-03'::timestamp);
1
2

# string_agg

该函数用于将多个数据链接在一起成为一个字符串

select string_agg(id, '|') from wechat_kf_info group by open_kfid;
1

我们还可以让里面的数据按顺序排序

select string_agg(id, '|' order by id) from wechat_kf_info group by open_kfid;
1

# JSON 与 JSONB

pg 有很多自己的数据结构,比如 json。json 数据类型可以用来存储 JSON(JavaScript Object Notation)数据, 这样的数据也可以存储为 text,但是 json 数据类型更有利于检查每个存储的数值是可用的 JSON 值

一个 JSON 数值可以是一个简单值(数字、字符串、true/null/false),数组,对象

JSON 和 JSONB 类型在使用上几乎完全一致,两者的区别主要在存储上,json数据类型直接存储输入文本的完全的拷贝,JSONB 数据类型以二进制格式进行存储。同时 JSONB 相较于 JSON 更高效,处理速度提升非常大,且支持索引

# 查询操作符

我们可以对 JSON 类型做一些很有趣的操作,比如可以用箭头来取 json 中的值,比如 user 有很 home,我们可以直接查询他在北京的房子:

select user_home -> 'beijing' as aaa
from user
1
2

# 通过 json 的键获取 json 的值

如果将 -> 指向一个数字的话,就是对 json 数组的操作了,表示取第几个元素,比如以下代码,就是取第1个 json 元素

select user_home -> 1 as aaa
from user
1
2

除此之外,箭头还有可以有变种,->> 表示取对应值文本的形式,可以理解为用->>取出来的值,都是字符串。最直观的感受就是,在 idea 中分别用->与->>取数据,取出来的分别是彩色与白色

注意,->拿的只是 json 对象的域,因此有很多操作符不能用,如果像 map 一样只取对象的值的话,还是尽量使用 ->>

select user_home ->> 'beijing' as aaa
from user
1
2

# #> 获取指定路径上的对象

一个 json 数据可能会包含多个嵌套属性,使用 #> 获得在指定路径上的 JSON 对象,同样,该操作符有变种 #>>

select user_home #>> '{beijing, 汤臣一品}' as aaa
from user
1
2

# ?判断 json 中是否存在某个键

用户在使用 JSON 类型时,常见的一些 JSON 搜索比如,JSON 中是否存在某个 KEY,某些 KEY,某些 KEY 的任意一个,这些操作 pg 都为我们提供了

存在某个 KEY

select * from my_table where colmn ? 'key'
1

用 ?& 表示必须存在所有 KEY,才会返回 true

'{"a":1, "b":2, "c":3}'::jsonb ?& array['b', 'c']  
1

存在任意 KEY 元素

'["a", "b"]'::jsonb ?| array['a', 'b'] 
1

# @> 判断 json 中是否存在某个键值对

JSON 中是否存在指定的 key:value 对(支持嵌套 JSON)

'{"a":1, "b":2}'::jsonb @> '{"b":2}'::jsonb  
1

JSON 中某个路径下的 VALUE(数组)中,是否包含指定的所有元素

select jsonb '{"a":1, "b": {"c":[1,2,3], "d":["k","y","z"]}, "d":"kbc"}' @> '{"b":{"c":[2,3]}}';  
1

# 修改操作符

向某个 json 类型的列塞入数据,需要使用单引号配合大括号

update users set data = '{"uptate_data": "7"}'::jsonb where id = 3;
1

对一个 json 类型的数据增加某个字段或者修改某个字段,需要使用 ||

update users set data = data::jsonb || '{"uptate_minute": "10"}'::jsonb where id = 3;
1

# 函数

pg 还提供了很多函数用来对对应的数据类型做操作,函数的传入应该是查询后的结果,但是函数会允许下面格式的查询语句

SELECT jsonb_array_elements('[1, 2, [3, 4]]');

select jsonb_array_elements(user_house)
from user;
1
2
3
4

# jsonb_array_elements 操作 json 数组

比如你其实可以传入一个 json 类型的数组作为一个列,但是此时你就不能使用箭头来对数组中的每个元素进行同样的操作了。注意是一个,不是一群,需要严格限制个数,不然会报错。同时,该函数还允许传入 null

此时你可以使用 jsonb_array_elements 函数,将顶层 JSON 数组扩展为一个 JSONB 值的集合,然后使用箭头来查询它们里面包含的值,即解析 JSON 数组转成多行

select jsonb_array_elements(user_house) -> 'home'
from user
1
2

在这里插入图片描述

更多的函数: https://www.sjkjc.com/postgresql-ref/jsonb_array_elements/

# hstore

pg 可以使用 hstore 来储存一个类似 map 的结构,在数据库中,对应关系会使用 => 来表示,对该类型数据的增删改查有不同的方式

hstore 模块实现了将键/值对存储到单个值的 hstore 数据类型。注意,hstore 中的 键 和 值 都只能是字符串

我们可以使用 :: 强制类型转换一些数据为 hstore 类型

	'a=>x, b=>y'::hstore -> 'a'
1

并且在 hstore 中有一个特别奇怪的关键字 ARRAY,可以将零散的数据封装成数组来进行批量操作(一般来说像这种的都是函数)

	'a=>x, b=>y, c=>z'::hstore -> ARRAY['c','a']
1

如果键没有对应的值,hstore 的特性还可以让它拿到 null,而不是丢出异常

# 操作符

hstore 的操作符可以使用不同方式查询该数据,比如使用键来寻找值(箭头)、判断对应的键是否在 hstore 中(问号)、左操作符是否包含右操作符(@>)

// 获得键的值(如果不存在为NULL)	
'a=>x, b=>y'::hstore -> 'a'

// hstore 包含键吗?	
'a=>1'::hstore ? 'a'
// hstore 包含所有指定的键?	
'a=>1,b=>2'::hstore ?& ARRAY['a','b']
// hstore 包含任何指定的键?	
'a=>1,b=>2'::hstore ?| ARRAY['b','c']

// 左操作符包含右操作符?	
'a=>b, b=>1, c=>NULL'::hstore @> 'b=>1'
// 左操作符包含于右操作符?	
'a=>c'::hstore <@ 'a=>b, b=>1, c=>NULL'
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 增删改

操作符配合 ARRAY 函数即可实现大多数的 hstore 属性查询功能了,但是我们还是不知道如何将数据插入 hstore 中,以及如何创建一个 hstore 属性等问题

首先我们启用 hstore 模块,使用 HSTORE 数据类型之前,需要先启用 hstore 模块:

CREATE EXTENSION hstore;
1

创建包含 HSTORE 数据类型的表

CREATE TABLE books (
 id serial primary key,
 title VARCHAR (255),
 attr hstore
);
1
2
3
4
5

hstore 的增加操作

INSERT INTO books (title, attr)
VALUES
 (
 'PostgreSQL 轻松学',
 '"paperback" => "685",
    "publisher" => "sjk66.com",
    "language"  => "简体中文",
    "ISBN-13"   => "1234567890123",
 "weight"    => "412 克"'
 );
1
2
3
4
5
6
7
8
9
10

给已存在的数据添加键值对,也就是向 hstore 中增加数据。注意,|| 是语法规则,不是或的意思,你不能改成 &&

同时,想要在键值对之外加单引号,单引号里面想要箭头以及双引号,后面的转化符也是必须的

UPDATE books
SET attr = attr || '"freeshipping"=>"yes"' :: hstore;
1
2

在 mybatis 中,可以这么写

        update order_info
        set extra_info = extra_info || concat(#{key}, ' => ', #{value})::hstore
        where order_no = #{orderNo}
1
2
3

删除键值对

UPDATE books 
SET attr = delete(attr, 'freeshipping');
1
2

也可以这样删除数据(减号)

// 从左操作符中删除键	
'a=>1, b=>2, c=>3'::hstore - 'b'::text
1
2

# 数组

pg 支持数组类型数据结构,可以在一些数据类型后面加上中括号来表示数组这个结构,比如 integer[]

对于数组的增删改查与上面的 hstore 与 json 差不多

# 操作符

默认情况下,数组的下标是从 1 开始的,我们也可以指定下标的开始值,如下:

SELECT id[2], id[3], id[4] FROM test02;
1

但是我们一般都不会这么用,pg 给我们提供了很多操作数组的访问符

操作符 描述 示例 结果
= 相等 SELECT ARRAY[1.1,2.1,3.1]::int[] = ARRAY[1,2,3]; t
<> 不等于 select ARRAY[1,2,3] <> ARRAY[1,2,4]; t
< 小于 select ARRAY[1,2,3] < ARRAY[1,2,4](pg 的大于小于等比较是按数组的顺序一一比较); t
> 大于 select ARRAY[1,4,3] > ARRAY[1,2,4]; t
<= 小于或等于 select ARRAY[1,2,3] <= ARRAY[1,2,3]; t
>= 大于或等于 select ARRAY[1,4,3] >= ARRAY[1,4,3]; t
@> 包含 select ARRAY[1,4,3] @> ARRAY[3,1]; t
<@ 包含于 select ARRAY[2,7] <@ ARRAY[1,7,4,2,6]; t
&& 重叠(是否有相同元素) select ARRAY[1,4,3] && ARRAY[2,1]; t

# 函数

可以使用 sql 的 ARRAY 函数来将一系列数据变成函数,后面的 ::integer[] 强制转换为数组其实没有必要,但是还是推荐加上,这是为了规定类型

update my_table set department_id = ARRAY[1]::integer[] where id = 42
1

向数组中添加新元素则需要这么处理

-- 向一个数组的末端追加一个元素
select array_append(ARRAY[1,2], 3);
-- 连接两个数组
select array_cat(ARRAY[1,2,3], ARRAY[4,5])
-- 向一个数组的首部追加一个元素
select array_prepend(1, ARRAY[2,3])
1
2
3
4
5
6

array_to_string 可以把数组变成字符串。比如在需要将数组中的某个值与某个字符串做模糊匹配的时候,这个函数就可以派上用场

-- 数组转换字符串,用逗号分隔,null直接省略
select array_to_string(ARRAY[1, 2, 3, NULL, 5], ',')
-- null用*代替
select array_to_string(ARRAY[1, 2, 3, NULL, 5], ',', '*')
1
2
3
4

string_to_array 也可以把字符串变成数组

-- 用,分隔
select string_to_array('a,b,c', ',')
select string_to_array('xx~^~yy~^~zz', '~^~', 'yy')
1
2
3

unnest 可以将一个数组扩展成一组行,这个函数非常重要,可以将一个数组拆成多条数据

select unnest(ARRAY[1,2])
1

判断一个数据是否为空数组,array_length() 需要两个参数,第二个是数组的维度,下面的查询将不包括空数组和 NULL

array_length(id_clients, 1) > 0
1

array_agg 用于将多行数据合并成一个 数组

select array_agg(id) from wechat_kf_info group by open_kfid;
1

使用 any 函数做某个数据是否与任意一个数组中的数字相同的判断,但是一般尽量不要使用函数作为查询条件,因为用不了索引,使用强转配合关键字查询更加快速

select 'a' = any('{a, b, c}')
1

# ltree

# 查询操作符

ltree 提供两种数据类型

  • ltree:存储标签路径
  • lquery:表示用于匹配 ltree 值的类似正则表达式的模式。 一个简单的单词与路径中的标签匹配。 星号(*)匹配零个或多个标签。 例如:

foo 匹配确切的标签路径 foo .foo. 匹配包含标签 foo 的任何标签路径 *.foo 匹配最后一个标签为 foo 的任何标签路径

比如:

-- 匹配 sms.aaa
SELECT v FROM configuration WHERE k ~ lquery 'sms.*';
-- 匹配 sms
SELECT v FROM configuration WHERE k ~ lquery 'sms';
-- 匹配 aaa.sms.aaa
SELECT v FROM configuration WHERE k ~ lquery '*.sms.*';
1
2
3
4
5
6

# 性能分析

# generate_series 生成数据

该函数一般用于我们向 pg 插入大量的数据,该函数的作用是在给定间隔内生成一系列数字,一般用于测试性能。 序列值之间的间隔和步骤由用户定义,下面的 start 指定开始大小,stop 指定结束大小,默认按一增加,但是可以用 step 指定增加大小

GENERATE_SERIES ( start, stop [, step ] )
1

比如下面的语句,会生成1到10

SELECT value
FROM GENERATE_SERIES(1, 10);
-- value
-- -----------
-- 1
-- 2
-- 3
-- 4
-- 5
-- 6
-- 7
-- 8
-- 9
-- 10
1
2
3
4
5
6
7
8
9
10
11
12
13
14

配合插入语句使用,可以快速生成多条数据

insert into second_kill_batch (id, start_time, end_time, batch_name, activity_flag)
select generate_series(50, 1050), now(), now(), '测试千条性能', generate_series(150, 1150)::varchar;
1
2

# explain analyze

使用 pg 提供的 explain analyze sql 语句进行性能分析,返回的数据意义如下: 在这里插入图片描述

cost=0.00…234.00

  • 第一个数字 0.00 表示启动的成本,返回第一行需要多少 cost 值
  • 第二个数字 234.00 表示返回所有数据的成本

cost 使用将操作量化的方式返回给我们执行该语句大致需要多少代价,cost 基于如下的一些规则计算出的数字(默认):

顺序扫描一个块,cost 的值为 1;随机扫描一个块,cost 的值为 4;处理一个数据行的 CPU 代价,cost 的值为 0.01;处理一个索引行的 CPU 代价,cost 的值为 0.005;每个操作的 CPU 代价为 0.0025

rows=10000,表示会返回10000行

width=74,表示每行平均宽度为74字节

来个复杂的例子: 在这里插入图片描述

actual time=0.320..0.320,实际花费的时间

loops=1,循环的次数 buffers:缓冲命中数 planning time: 0.124 ms,生成执行计划时间 execution time:0.353 ms,执行执行计划时间,运行这个最快的查询计划的时间

关于生成执行计划与执行执行计划的具体区别如下:

1,你用 SQL 编写一个查询,这是某种脚本,你试图告诉服务器你想从他那里得到什么 2,大多数情况下,服务器可以通过编写查询来收集您请求的数据。有一种称为查询规划器的机制发挥作用。它尝试找到执行查询的最快方式(计划)。它通过估计几种可能的方式(计划)的执行时间来做到这一点(查询优化) 3,服务器使用被认为是最快的计划运行查询 4,服务器返回输出

EXPLAIN 命令打印该过程的说明。现在:

  • execution time 输出上的执行时间是服务器在步骤 3-4 上花费的时间
  • planning time 规划输出时间是服务器仅在步骤 2 上花费的时间。我相信您将其视为时间规划器认为查询需要,但这可以称为计划执行时间或估计执行时间
#PostgreSQL
最后更新: 2/23/2026, 9:23:04 AM
PostgreSQL 更新数据时 HOT优化
Python 基础语法

← PostgreSQL 更新数据时 HOT优化 Python 基础语法→

最近更新
01
vibe coding 最佳实践
02-24
02
立直麻将牌效益理论
02-23
03
伪静态是什么
02-08
更多文章>
Theme by Vdoing
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式