范围类型
范围类型表示某种标量值的连续区间,例如整数区间、日期区间和时间点区间。它把“开始值、结束值、是否包含边界、是否无界”组合成一个值,适合预约、有效期、版本区间和价格区间。
内置范围类型
| 类型 | 元素类型 | 典型用途 |
|---|---|---|
int4range | integer | 编号区间 |
int8range | bigint | 大整数区间 |
numrange | numeric | 数值区间 |
tsrange | timestamp | 不带时区时间段 |
tstzrange | timestamptz | 绝对时间段 |
daterange | date | 日期有效期 |
边界写法
SELECT '[1,10)'::int4range;
SELECT '[2026-09-21 09:00+08,2026-09-21 10:00+08)'::tstzrange;
SELECT daterange(date '2026-01-01', date '2027-01-01', '[)');
[ 或 ] 表示包含边界,( 或 ) 表示排除边界。半开区间 [start, end) 特别适合连续时间段:相邻区间可以无缝连接且不重叠。
范围可以无界,例如 (,10];缺失的下界和显式 NULL 不是完全相同的业务含义。规范化会根据离散元素类型调整表示,如整数范围可能把 (1,4] 规范为 [2,5)。
运算符
SELECT int4range(1, 10) @> 5;
SELECT int4range(1, 10) && int4range(9, 20);
SELECT int4range(1, 10) * int4range(5, 20); -- 交集
SELECT int4range(1, 10) - int4range(5, 20); -- 差集,结果可能不是单一范围
常用运算包括包含 @>、被包含 <@、重叠 &&、相邻 -|-、并集 + 和交集 *。差集可能得到多段结果,业务代码不能假设始终是一条范围。
排除重叠
PostgreSQL 的排斥约束可以防止同一资源的时间段重叠:
CREATE EXTENSION IF NOT EXISTS btree_gist;
CREATE TABLE bookings (
room_id bigint NOT NULL,
during tstzrange NOT NULL,
EXCLUDE USING gist (
room_id WITH =,
during WITH &&
)
);
约束把并发下的重叠检查交给数据库。仍需定义取消、草稿、时区和边界精度,并理解约束失败时事务的处理方式。
索引
GiST 和 SP-GiST 支持范围的包含、重叠等查询:
CREATE INDEX bookings_during_gist_idx
ON bookings USING gist (during);
范围查询的选择性和边界分布会影响计划。对单纯的开始时间排序,普通 B-tree 可能更合适;不要因为列是范围类型就自动选择 GiST。
常见误区
- 混用闭区间和半开区间,导致相邻预约被判定为重叠。
- 把
timestamp范围用于跨时区事件,却没有统一时区语义。 - 以为
NULL下界、无界和“永不过期”在业务上自动等价。 - 只在应用层检查重叠,忽略并发插入导致的竞态。
- 将范围差集当作单个范围处理。