PostgreSQL 18 - 时间约束 (Temporal Constraints)

PostgreSQL 18 引入了一项强大的新功能,允许你在时间段内强制执行唯一性:时间约束(Temporal Constraints) 。通过这一新增功能,你现在可以在定义 UNIQUE、PRIMARY KEY 或 FOREIGN KEY 约束时,利用日期范围(Date Range)或时间戳范围(Timestamp Range)字段,比以往更容易地防止数据重叠。

问题所在:基于时间的唯一性

在最新的这个版本中,PostgreSQL 增加了一种更灵活的方式来定义 UNIQUE、PRIMARY KEY 和 FOREIGN KEY 约束。我将把示例重点放在 UNIQUE 约束上,因为这在我看来是时间约束最有用的场景。

让我们从一个现实世界的场景开始:管理用户订阅。

设置示例

首先,我们有一个简单的 users 表,包含 2 行数据:

sql 复制代码
CREATE TABLE users (
  id uuid DEFAULT uuidv7() PRIMARY KEY,
  email VARCHAR(100) UNIQUE NOT NULL
);

INSERT INTO users (email) VALUES 
('darth@example.com'),
('luke@example.com');

使用 WITHOUT OVERLAPS 的时间约束

现在我想创建一个属于 users 表的 subscriptions(订阅)表。每个订阅都有开始和结束日期,我想确保每个用户的订阅是唯一的。用户可以有过去的订阅(也许他们升级了层级),但在任何给定的时间点只能有一个活跃的订阅。

以下是我们如何使用新的 WITHOUT OVERLAPS 语法来强制执行此操作:

sql 复制代码
CREATE TABLE subscriptions (
  user_id uuid NOT NULL,
  type VARCHAR(50) NOT NULL,
  valid_period daterange NOT NULL,
  FOREIGN KEY (user_id) REFERENCES users(id),
  UNIQUE (user_id, valid_period WITHOUT OVERLAPS)
);

如你所见,我们在 valid_period 这个 daterange 字段的 UNIQUE 约束中使用了 WITHOUT OVERLAPS。时间约束要求键列(valid_period)必须是范围类型(range type),因此通常你会使用 daterange 或 timestamprange,但它也适用于其他范围类型。

测试一下

让我们插入一些数据:

sql 复制代码
INSERT INTO subscriptions (user_id, type, valid_period)
SELECT u.id, 'basic', daterange('2024-01-01', '2025-01-01', '[)')
FROM users u;

INSERT INTO subscriptions (user_id, type, valid_period)
SELECT u.id, 'premium', daterange('2025-01-01', '2026-01-01', '[)')
FROM users u
WHERE u.email = 'darth@example.com';

SELECT * FROM subscriptions;

结果如下:

text 复制代码
               user_id                |  type   |      valid_period
--------------------------------------+---------+-------------------------
 0199f293-291a-70bf-b9ee-872247723d29 | basic   | [2024-01-01,2025-01-01)
 0199f293-291b-737a-9bd0-e0e0853e3377 | basic   | [2024-01-01,2025-01-01)
 0199f293-291a-70bf-b9ee-872247723d29 | premium | [2025-01-01,2026-01-01)

在这个例子中,Luke 显然在 2025 年停止了付费,而 Darth 升级到了高级版。我通常习惯使用单独的列来表示开始和结束日期,但我很欣赏范围类型(Ranges)能够启用此功能,并允许我们使用强大的范围运算符。

额外内容:查询活跃订阅

使用"冰淇淋筒"运算符(@>,即包含运算符),我们可以轻松找到特定日期有效的订阅:

sql 复制代码
SELECT * FROM subscriptions
WHERE valid_period @> '2025-01-01'::date;

这个查询会告诉我们哪些订阅在 2025 年 1 月 1 日是活跃的。

重要提示:GIST 与 B-Tree 索引

有一个陷阱:范围列使用 GIST 索引,而 UUID 列使用 B-Tree 索引。当我运行 CREATE TABLE subscriptions 命令时,我遇到了一个错误。为了解决这个问题,你需要启用以下扩展:

sql 复制代码
CREATE EXTENSION btree_gist;

了解更多

如果你想深入了解时间约束,请查看 PostgreSQL 文档中关于 WITHOUT OVERLAPS 的部分。

原文链接:hashrocket.com/blog/posts/...

作者:Vinicius Negrisolo

相关推荐
来自于狂人15 分钟前
GitHub 开源趋势日报 | 2026年10月7日用 AI Agent 逆向工程一切
人工智能·开源·github
SL_staff24 分钟前
制造业数字化协同:为什么甘特图只是起点,不是终点
java·开源·全栈
涼叶i1 小时前
反序列化漏洞
安全·开源
Data-Miner1 小时前
离线AI制表:模型适配与脚本固化实操
大数据·数据库·人工智能·excel
xixiaoyunya1 小时前
数据库三种备份方式怎么组合?从原理到落地的完整方案
数据库
2501_933670791 小时前
2027届数学与应用数学转经营分析:SQL、财务指标与业务指标补齐路线
数据库·sql·数学建模
我科绝伦(Huanhuan Zhou)1 小时前
oracle坏块导致备份不了
数据库·oracle·ffmpeg
miofly1 小时前
Google 发布 EmbeddingGemma 2:7.4 亿参数的多模态嵌入模型
开源·github
sbjdhjd2 小时前
智能体开始“动手”之后:OpenAI越权事件、Anthropic算力资本化与开放权重模型竞逐 | AI与SI行业日报整理(9月29日—10月6日)
大数据·人工智能·经验分享·笔记·ai·chatgpt·开源
IT大白鼠2 小时前
图数据库系列 · 第 08 篇——面试收官:高频题与全景总结
数据库·面试·职场和发展·nosql·图数据库