pgsql的特性讲解
如果你已经会 MySQL 或写过后端 CRUD,换到 PostgreSQL 时真正要补的不是「SQL 是什么」,而是 PG 方言里多出来的写法:RETURNING、Identity、JSONB、数组、DISTINCT ON ……本文环境按 PostgreSQL 14+。
1. 用 Psql 跑 SQL
psql 是 PostgreSQL 自带的交互客户端,类似 MySQL 的 mysql 命令。
1.1 连上数据库
1 | # 本地或容器内,按你的用户名、库名改 |
没有本地 PG 时,可以用 Docker 起一个:
1 | docker run --name pg-demo -e POSTGRES_PASSWORD=postgres -p 5432:5432 -d postgres:16 |
1.2 执行 SQL
1 | -- 交互里直接写,分号结束 |
1.3 看表结构
1 | \dt # 当前 schema 下的表 |
如果你来自 MySQL:
\dt≈SHOW TABLES;\d 表名≈DESCRIBE 表名;数据库名用\c切换,不是USE db。
2. SQL 核心速览
下面用同一张 users 表把「会 SQL 的人」需要的语法对齐一遍,不展开教程。
1 | DROP TABLE IF EXISTS users; |
要点 bullet:
- 类型:
text不限长字符串;时间推荐timestamptz(带时区),别用 MySQL 式DATETIME思维硬套。 - 标识符:未加引号的表名、列名会自动转小写;要保留大小写需双引号
"UserId"(尽量少用)。 - 字符串:单引号
'hello';双引号是标识符,不是字符串。
3. RETURNING
插入、更新、删除后,同一条语句把受影响的行返回出来,不必再 SELECT 一遍。
1 | DROP TABLE IF EXISTS users; |
典型用途:拿自增主键、审计日志、ORM 里减少一次往返。
如果你来自 MySQL:没有等价关键字;常见做法是
INSERT后再SELECT LAST_INSERT_ID(),或驱动层getGeneratedKeys()。PG 的RETURNING更直接,且在UPDATE/DELETE上同样可用。
4. 自增与 Identity
4.1 SERIAL(简写,仍常见)
1 | CREATE TABLE t1 ( |
serial / bigserial 本质是 integer + 独立 sequence,老项目、教程里很多。
4.2 GENERATED AS IDENTITY(PG 10+,推荐)
1 | CREATE TABLE t2 ( |
SQL 标准语法,和 Oracle、SQL Server 的 Identity 模型一致,新表优先用这个。
如果你来自 MySQL:
AUTO_INCREMENT列 ≈GENERATED BY DEFAULT AS IDENTITY;PG 没有「表选项」里的AUTO_INCREMENT,写在列定义上。
5. JSONB
jsonb 是二进制 JSON,可索引、可包含查询;Plain json 只存文本、每次解析,查询慢——除非有特殊理由,一律用 jsonb。
1 | DROP TABLE IF EXISTS events; |
给 JSONB 建 GIN 索引(路径、包含查询会快很多):
1 | CREATE INDEX idx_events_payload ON events USING GIN (payload); |
如果你来自 MySQL:MySQL 8 有
JSON类型和->/->>,概念接近;PG 的@>、?、GIN 默认支持更成熟,复杂文档检索常少挂一个 Elasticsearch。
6. 数组
PG 列可以直接是数组,不必拆关联表或塞 JSON。
1 | DROP TABLE IF EXISTS articles; |
标签、权限列表、多值枚举这类「一列多个值」,数组比 JSON 数组类型更轻、运算符更直接。
7. DISTINCT ON
每组按某排序取第一条,语法是 PG 独有(标准 SQL 没有直接等价)。
1 | DROP TABLE IF EXISTS scores; |
DISTINCT ON 括号里的表达式必须先出现在 ORDER BY 最左侧,且顺序一致——这是最容易写错的地方。
如果你来自 MySQL:常用窗口函数
ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY exam_at DESC) = 1实现同样效果;PG 两种都能写,DISTINCT ON往往更短。
8. 其他 PG 常用写法
零散但高频的差异,收在一节方便查。
8.1 大小写不敏感匹配:ILIKE
1 | SELECT * FROM users WHERE name ILIKE '%alice%'; |
8.2 类型转换:::
1 | SELECT '42'::int; |
8.3 COALESCE
1 | SELECT COALESCE(nickname, name, '匿名') FROM users; |
8.4 LIMIT / OFFSET
1 | SELECT * FROM users ORDER BY id LIMIT 10 OFFSET 20; |
8.5 布尔类型
1 | CREATE TABLE flags (ok boolean NOT NULL DEFAULT false); |
MySQL 用 TINYINT(1) 冒充布尔;PG 有原生 boolean。