当前位置: 首页 > news >正文

PostgreSQL 范围、空间唯一性约束

PostgreSQL 范围、空间唯一性约束

在 PostgreSQL 中,实现数据唯一性不仅限于 UNIQUEPRIMARY KEY,还可以使用 EXCLUDE 约束配合 GiST 索引来实现更灵活的唯一性检查。


1. UNIQUE 约束

作用

  • 保证某列(或多列组合)的值在表中不重复。
  • 可以有多个 UNIQUE 约束。
  • 允许列中出现多个 NULL(因为 NULL 被认为是“未知”,彼此不相等)。

测试语句

DROP TABLE IF EXISTS customers;postgres=# CREATE TABLE customers (
postgres(#     customerid INT UNIQUE,
postgres(#     name TEXT
postgres(# );
CREATE TABLE
postgres=# 
postgres=# -- 插入测试数据
postgres=# INSERT INTO customers VALUES (1, 'Alice'); -- 成功
INSERT 0 1
postgres=# INSERT INTO customers VALUES (2, 'Bob');   -- 成功
INSERT 0 1
postgres=# INSERT INTO customers VALUES (1, 'Charlie'); -- 报错:违反唯一约束
ERROR:  duplicate key value violates unique constraint "customers_customerid_key"
DETAIL:  Key (customerid)=(1) already exists.
postgres=# 
postgres=# -- NULL 测试
postgres=# INSERT INTO customers VALUES (NULL, 'David'); -- 成功
INSERT 0 1
postgres=# INSERT INTO customers VALUES (NULL, 'Eve');   -- 成功(UNIQUE 允许多个 NULL)

特点总结
可在同一个表中定义多个。可接受多个 NULL。自动创建唯一 B-Tree 索引。

2. PRIMARY KEY 约束

作用
唯一标识表中每一行数据。每个表只能有一个 PRIMARY KEY。自动包含 NOT NULL 和唯一性约束。
测试语句

DROP TABLE IF EXISTS customers_pk;
postgres=# CREATE TABLE customers_pk (
postgres(#     customerid INT PRIMARY KEY,
postgres(#     name TEXT
postgres(# );
CREATE TABLE
postgres=# -- 插入测试数据
postgres=# INSERT INTO customers_pk VALUES (1, 'Alice'); -- 成功
INSERT 0 1
postgres=# INSERT INTO customers_pk VALUES (2, 'Bob');   -- 成功
INSERT 0 1
postgres=# INSERT INTO customers_pk VALUES (1, 'Charlie'); -- 报错:违反主键唯一性
ERROR:  duplicate key value violates unique constraint "customers_pk_pkey"
DETAIL:  Key (customerid)=(1) already exists.
postgres=# -- NULL 测试
postgres=# INSERT INTO customers_pk VALUES (NULL, 'David');
ERROR:  null value in column "customerid" of relation "customers_pk" violates not-null constraint
DETAIL:  Failing row contains (null, David).
-- 报错:主键列不允许 NULL

特点总结
每表只能有一个主键,可由一列或多列组合。自动 NOT NULL。自动创建唯一 B-Tree 索引。

3. EXCLUDE USING gist

作用
用 GiST 索引实现更灵活的唯一性检查,不仅能比较相等,还能比较空间重叠、范围重叠等复杂条件。常见于几何类型(point、box)、范围类型(int4range、tsrange 等)。

几何类型示例
postgres=# CREATE TABLE boxes (
postgres(#     id SERIAL PRIMARY KEY,
postgres(#     position box,
postgres(#     EXCLUDE USING gist (position WITH &&)
postgres(# );
CREATE TABLE
postgres=# 
postgres=# -- 插入测试数据
postgres=# INSERT INTO boxes (position) VALUES (box(point(0,0), point(1,1))); -- 成功
INSERT 0 1
postgres=# INSERT INTO boxes (position) VALUES (box(point(2,2), point(3,3))); -- 成功
INSERT 0 1
postgres=# INSERT INTO boxes (position) VALUES (box(point(0.5,0.5), point(1.5,1.5)));
ERROR:  conflicting key value violates exclusion constraint "boxes_position_excl"
DETAIL:  Key ("position")=((1.5,1.5),(0.5,0.5)) conflicts with existing key ("position")=((1,1),(0,0)).
-- 报错:两个矩形重叠
时间区间冲突检测示例

假设我们要做一个 会议室预约系统,要求同一个会议室不能在同一时间段被重复预约。我们可以用 EXCLUDE USING gist 来做时间范围的唯一性约束。满足以下三点要求
1、同一会议室 + confirmed 状态 → 不能有时间交叉。
2、不同会议室 → 不受影响。
3、status 不是 confirmed → 不受影响。

-- 1) GiST 索引支持等值比较
CREATE EXTENSION IF NOT EXISTS btree_gist;  --GiST 索引默认能处理几何类型、范围类型的操作符(如 &&),本范例中加入= 的比较,所以增加btree_gist插件辅助。-- 2) 表结构
drop  table if exists reservations;
CREATE TABLE reservations (id serial PRIMARY KEY,room text NOT NULL,during tstzrange NOT NULL,status text NOT NULL DEFAULT 'pending',-- 同一 room 在 status='confirmed' 时,不允许 during 区间重叠CONSTRAINT no_overlap_confirmedEXCLUDE USING gist (room WITH =,   -- 同一个会议室during WITH &&   -- 时间范围有重叠)WHERE (status = 'confirmed')
);postgres=# -- ✅ 成功:第一次预约,会议室 A
postgres=# INSERT INTO reservations (room, during, status)
postgres-# VALUES ('A', '[2025-08-12 09:00,2025-08-12 10:00)', 'confirmed');
INSERT 0 1
postgres=# -- ❌ 失败:同一会议室 A,时间区间有交叉
postgres=# INSERT INTO reservations (room, during, status)
postgres-# VALUES ('A', '[2025-08-12 09:30,2025-08-12 10:30)', 'confirmed');
ERROR:  conflicting key value violates exclusion constraint "no_overlap_confirmed"
DETAIL:  Key (room, during)=(A, ["2025-08-12 09:30:00+08","2025-08-12 10:30:00+08")) conflicts with existing key (room, during)=(A, ["2025-08-12 09:00:00+08","2025-08-12 10:00:00+08"))
postgres=# INSERT INTO reservations (room, during, status)
postgres-# VALUES ('A', '[2025-08-12 10:00,2025-08-12 11:00)', 'confirmed');INSERT 0 1
postgres=# 
postgres=# -- ✅ 成功:会议室 B,即使时间重叠也没问题
postgres=# INSERT INTO reservations (room, during, status)
postgres-# VALUES ('B', '[2025-08-12 09:30,2025-08-12 10:30)', 'confirmed');
INSERT 0 1
postgres=# 
postgres=# -- ✅ 成功:会议室 A,但 status 不是 confirmed,不受限制
postgres=# INSERT INTO reservations (room, during, status)
postgres-# VALUES ('A', '[2025-08-12 09:30,2025-08-12 10:30)', 'pending');
INSERT 0 1

小结
UNIQUE:适合需要唯一性但允许多个 NULL 的列。
PRIMARY KEY:适合作为表的主标识,不允许 NULL,只能有一个。
EXCLUDE USING gist:适合范围/空间/时间等需要复杂冲突检测的场景,比唯一约束灵活。

http://www.lryc.cn/news/618255.html

相关文章:

  • 用 Apache Iceberg 与 Apache Spark 在 Google Cloud 打造高性能、可扩展的数据湖仓
  • Flink运行时的实现细节
  • SQL 语言分类
  • Spark 运行流程核心组件(一)作业提交
  • 数据量暴涨时,抓取架构该如何应对?
  • 开发npm包【详细教程】
  • Bevy渲染引擎核心技术深度解析:架构、体积雾与Meshlet渲染
  • C++Linux八股
  • 08--深入解析C++ list:高效操作与实现原理
  • K8S 节点初始化一键脚本(禁用 SELinux + 关闭 swap + 开启 ipvs 亲测实用)
  • 微前端架构:原理、场景与实践案例
  • 前端JS处理时间,适用于聊天、操作记录等(包含刚刚、x分钟前、x小时前、x天前)
  • Windows已经安装了一个MySQL8,通过修改配置文件的端口号跑2个或多个Mysql服务方法,并注册为系统服务
  • lesson36:MySQL从入门到精通:全面掌握数据库操作与核心原理
  • 嵌入式系统学习Day17(文件编程)
  • 项目实战2——LAMP_LNMP实践
  • 智能化评估体系:数据生产、在线化与自动化的三重奏
  • 解锁 Appium Inspector:移动端 UI 自动化定位的利器
  • 【论文阅读】一种基于经典机器学习的肌电下肢意图检测方法,用于人机交互系统
  • Secure CRT做代理转发
  • 【element树组件】el-tree实现连接线及hover编辑效果
  • ip归属地批量查询脚本
  • 视频输入输出模块介绍和示例
  • 【Node.js从 0 到 1:入门实战与项目驱动】2.1 安装 Node.js 与 npm(Windows/macOS/Linux 系统的安装步骤)
  • history命令增强记录执行时间与登录IP
  • 线性代数 · 矩阵 | 最小多项式
  • 【debug 解决 记录】stm32 debug模式的时候可以运行,但是烧录没法执行
  • Mac如何安装telnet命令
  • 论答题pk小程序软件版权的
  • 家政小程序系统开发:推动家政行业数字化转型,共创美好未来