唯一能覆盖端点相接、null安全、跨数据库可移植的写法是用overlaps运算符或手写等价条件start_a
直接用
OVERLAPS运算符,或手写等价条件start_a = start_b—— 这是唯一能覆盖端点相接、NULL 安全、跨数据库可移植的写法。用 OVERLAPS 运算符查重叠(PostgreSQL 专属)
PostgreSQL 的
OVERLAPS是专为区间设计的语法糖,语义清晰且自动处理边界包含逻辑(如'2024-03-10'和'2024-03-10'视为重叠)。
- 必须成对传入两个时间点:
(start1, end1) OVERLAPS (start2, end2),不能只传一个值或混用字段与字面量类型- 字段含
NULL时结果为UNKNOWN,整行被过滤;需提前用COALESCE补默认值,例如COALESCE(end_at, '9999-12-31')- 若字段是
TIMESTAMP WITH TIME ZONE,右侧字面量也必须带时区,否则隐式转换可能出错,例如写成'2024-03-10 00:00:00+08'而非'2024-03-10'- 示例:查
events表中与'2024-03-10'至'2024-03-15'有交集的记录:SELECT * FROM events WHERE (start_at, COALESCE(end_at, '9999-12-31')) OVERLAPS ('2024-03-10', '2024-03-15');手写重叠条件(兼容 MySQL/SQL Server,更可控)
等价于
OVERLAPS的底层逻辑是start_a = start_b。这个表达式不依赖数据库特性,但细节极易出错。
- 顺序不能颠倒:左侧必须是待查记录的
start_at与输入区间的右端比较,右侧是输入区间的左端与待查记录的end_at比较- 字段为
DATE类型时,输入值也必须是DATE;若字段含时分秒(如TIMESTAMP),输入值必须带时间,否则当天首尾记录可能被截断漏掉- 显式排除
NULL:WHERE start_at IS NOT NULL AND end_at IS NOT NULL AND start_at- 如果业务定义“端点相接不算重叠”(如
A_end = B_start不算),把和 <code> 改为 <code> 和 <code>,即 <code>start_at索引和性能关键点
没索引时,重叠查询会全表扫描;但普通单列索引几乎无效,必须针对性设计。
- 优先建复合 B-tree 索引:
CREATE INDEX idx_events_period ON events (start_at, end_at)—— PostgreSQL 16 的查询优化器能利用它做范围剪枝- 如果表数据量极大(>100 万行)且重叠查询频繁,改用
tsrange类型 + GiST 索引:ALTER TABLE events ADD COLUMN period tsrange; UPDATE events SET period = tsrange(start_at, COALESCE(end_at, '9999-12-31')); CREATE INDEX idx_events_period_gist ON events USING GIST (period);再用period && tsrange('2024-03-10', '2024-03-15')查询- 避免在条件中对字段做计算,如
DATE(start_at)或start_at::DATE,这会让所有索引失效最易被忽略的是时间精度对齐和 NULL 处理——哪怕逻辑完全正确,只要字段是
TIMESTAMP(6)而输入值是字符串'2024-03-10',隐式转换就可能让'2024-03-10 00:00:00.000000'和'2024-03-10 23:59:59.999999'的比较失效。务必统一用CAST或显式字面量。











