处理 'infinity' 时获取日期范围最短的行
Fetch row with shortest date range while dealing with 'infinity'
考虑我的模式:
CREATE TABLE t_date (
t_date_id int PRIMARY KEY
, valid_from date NOT NULL
, valid_to date DEFAULT 'infinity'
);
有时我有valid_to
个约会对象,有时我有infinity
个...
如何正确过滤以获得范围最短的行?
我试过:
(DATE_PART('day', valid_to::timestamp - valid_from::timestamp))
但这导致:
PG::DatetimeFieldOverflow: ERROR: cannot subtract infinite timestamps`
我有过滤器 select 有效范围:valid_from <= ? AND valid_to > ?
想法是获取具有最短范围的(一个)有效行。
例子
INSERT INTO t_date VALUES
(1, '2020-01-01', '2020-09-01')
, (2, '2020-01-10', '2020-01-12')
, (3, '2020-01-15', 'INFINITY')
, (4, '2020-01-16', 'INFINITY') -- shortest among infinities
, (5, '2020-01-14', 'INFINITY')
;
如果今天是 11/jan
我希望得到 '2020-01-10' | '2020-01-12'
因为它在 11/jan 和最短的时间内有效。
如果今天是 14/jan
我希望得到 '2020-01-01' | '2020-09-01'
因为它在 14/jan 和最短的时间内有效。
如果今天是 17/jan
我希望得到 '2020-01-16' | 'INFINITY'
.
如果稍后我创建了类似 '2020-01-15' | '2059-01-15'
的东西,应该返回它,因为它比 INFINITY 行短。
SELECT *
FROM t_date
WHERE valid_from <= ?
AND valid_to > ?
ORDER BY NULLIF(valid_to, 'infinity') - valid_from
, valid_from DESC -- tiebreaker: later start first
-- , t_date_id -- optional additional tiebreaker
LIMIT 1;
db<>fiddle here
NULLIF()
将 'infinity'
转换为 NULL
,因此整个表达式变为 NULL
,默认排序 last ASCENDING
顺序。无限范围应该最后完成你的任务。
要打破无限范围之间的关系,另外按 valid_from DESC
排序 - 定义为 NOT NULL
,因此我们不需要对 NULL
情况进行特殊处理。作为(欢迎?)副作用,这也打破了其他等长范围之间的联系,选择了最新的开始。
要获得确定性结果,如果允许相同的范围,您可能需要添加另一个明确的决胜局(如 PK 列)。
相关:
- Sort NULL values to the end of a table
考虑我的模式:
CREATE TABLE t_date (
t_date_id int PRIMARY KEY
, valid_from date NOT NULL
, valid_to date DEFAULT 'infinity'
);
有时我有valid_to
个约会对象,有时我有infinity
个...
如何正确过滤以获得范围最短的行?
我试过:
(DATE_PART('day', valid_to::timestamp - valid_from::timestamp))
但这导致:
PG::DatetimeFieldOverflow: ERROR: cannot subtract infinite timestamps`
我有过滤器 select 有效范围:valid_from <= ? AND valid_to > ?
想法是获取具有最短范围的(一个)有效行。
例子
INSERT INTO t_date VALUES
(1, '2020-01-01', '2020-09-01')
, (2, '2020-01-10', '2020-01-12')
, (3, '2020-01-15', 'INFINITY')
, (4, '2020-01-16', 'INFINITY') -- shortest among infinities
, (5, '2020-01-14', 'INFINITY')
;
如果今天是 11/jan
我希望得到 '2020-01-10' | '2020-01-12'
因为它在 11/jan 和最短的时间内有效。
如果今天是 14/jan
我希望得到 '2020-01-01' | '2020-09-01'
因为它在 14/jan 和最短的时间内有效。
如果今天是 17/jan
我希望得到 '2020-01-16' | 'INFINITY'
.
如果稍后我创建了类似 '2020-01-15' | '2059-01-15'
的东西,应该返回它,因为它比 INFINITY 行短。
SELECT *
FROM t_date
WHERE valid_from <= ?
AND valid_to > ?
ORDER BY NULLIF(valid_to, 'infinity') - valid_from
, valid_from DESC -- tiebreaker: later start first
-- , t_date_id -- optional additional tiebreaker
LIMIT 1;
db<>fiddle here
NULLIF()
将 'infinity'
转换为 NULL
,因此整个表达式变为 NULL
,默认排序 last ASCENDING
顺序。无限范围应该最后完成你的任务。
要打破无限范围之间的关系,另外按 valid_from DESC
排序 - 定义为 NOT NULL
,因此我们不需要对 NULL
情况进行特殊处理。作为(欢迎?)副作用,这也打破了其他等长范围之间的联系,选择了最新的开始。
要获得确定性结果,如果允许相同的范围,您可能需要添加另一个明确的决胜局(如 PK 列)。
相关:
- Sort NULL values to the end of a table