让我们假设有一个下表:

CREATE TABLE time_interval (
    id SERIAL PRIMARY KEY,
    start_time TIMESTAMP NOT NULL,
    end_time TIMESTAMP
);

样本数据:

INSERT INTO time_interval (start_time, end_time) VALUES
    ('2024-02-10 01:30:00',                  null), -- pending    
    ('2024-02-10 03:00:00',                  null), -- pending
    ('2024-02-10 07:00:00', '2024-02-10 10:30:00'),
    ('2024-02-10 09:00:00', '2024-02-10 12:00:00'),
    ('2024-02-10 11:30:00', '2024-02-10 15:00:00'),
    ('2024-02-10 13:30:00', '2024-02-10 17:30:00'),
    ('2024-02-10 16:00:00', '2024-02-10 20:00:00'),
    ('2024-02-10 18:30:00', '2024-02-10 22:00:00'),
    ('2024-02-10 21:00:00', '2024-02-10 23:30:00');

-你在说什么?

Give me all intervals between 100 including overlapping ones

预期结果:

    ('2024-02-10 01:30:00',                  null)
    ('2024-02-10 03:00:00',                  null)
    ('2024-02-10 07:00:00', '2024-02-10 10:30:00')
    ('2024-02-10 09:00:00', '2024-02-10 12:00:00')
    ('2024-02-10 11:30:00', '2024-02-10 15:00:00')
    ('2024-02-10 13:30:00', '2024-02-10 17:30:00')
    ('2024-02-10 16:00:00', '2024-02-10 20:00:00')

I was trying to use union to split the logic between those that pending and those that not but I am not sure about it.
Here is the simple version:

SELECT *
FROM time_interval
WHERE start_time < '2024-02-10 17:00:00'
  AND (end_time is null or end_time  > '2024-02-10 10:00:00');

结果很好,但问题是……有那么简单吗?我是不是遗漏了什么?有没有人看到我应该包括的其他角落 case ?

推荐答案

Demo at db<>fiddle:

select * from time_interval
where time_range && '[2024-02-10 10:00:00,2024-02-10 17:00:00]';

你使用的是ranges of 102:这是built-in tsrange type.不需要用单独的开始/结束字段来模拟它,并重新实现already available的函数和运算符-在本例中您的值是&& range overlap operator.

附加的好处是,您甚至可以使用multiranges:一个值可以包含多个带分隔符的范围,而不仅仅是一个只有一个开始和结束的块.你可以用proper index美元来支持这些.


如果您希望保留支持这些列的所有应用程序逻辑,您可以根据这些逻辑将tsrange设置为单独的generated.

alter table time_interval 
  add column time_range tsrange 
  generated always as (tsrange(coalesce(start_time,'infinity'), 
                               coalesce(end_time,  'infinity'))) stored;

您还可以动态地将这些字段设置为正确的类型:

select * from time_interval
where tsrange(coalesce(start_time,'infinity'), 
              coalesce(end_time,  'infinity')) 
    && '[2024-02-10 10:00:00,2024-02-10 17:00:00]';

或在适当位置调换列:

alter table time_interval add column start_end_time tsrange;
update time_interval 
  set start_end_time=tsrange(coalesce(start_time,'infinity'), 
                             coalesce(end_time,  'infinity'),
                             '[]');
alter table time_interval drop column start_time,
                          drop column end_time;

或者保持你的列不变,加expression index.这样你就可以保持你的 struct ,但同时,你可以使用快速和高度灵活的查询,动态地投射你的目标范围:performance demo on 420 000 samples.

create index gist_idx on time_interval 
  using gist(tsrange(coalesce(start_time,'infinity'), 
                     coalesce(end_time,  'infinity')));

Sql相关问答推荐

帮助修复查询以识别SQL DW中数据中的递归关系

如何在SQL中按每个子组的顺序更新数据?

使用WHERE子句进行筛选时,SQL SELECT查询返回总计数

SQL:如何查找聚合满足条件的连续日期

如何将`now()`作为SQL插入语句的一部分?

重用传递给 node 的参数-postgres upsert查询

每小时 Select 1行

更正用于判断错误组合的SQL

在SQL查询中查找客户端的最短日期比较列和多行

SQL仅返回第一个字母在A-Z之间的值

从重复值中获取最新值

输出连续出现两次以上的行

使用 XML 作为 SQL 表

两个具有 NULL 值的表达式结果之间的差异

使用 SQL 计算一年中任意 3 个月期间的总成本

如何显示最常引用条目的详细信息

具有日期时间条件的存储过程

在 Athena / Presto 中提取 JSON 对象以获取动态密钥

如何在 SQL Server 中参数化 Select top 'n'

为什么这是 AND,OR with NULL 的真值表?