下表(我们将其命名为 validity_period ):
------------------------------- id | valid_from | valid_until ------------------------------- 1 2012-11-12 2012-12-02 2 2012-12-03 NULL 3 2012-12-15 2012-12-21
( valid_from 不可为空; valid_until 可以为空,但不必为空)
现在我想找出今天哪个条目有效(2012-12-19)。从逻辑视图来看,它必须是条目3,因为条目可以相互重叠,但一天只有一个条目有效。 (在2012-12-22,它必须是有效的第2项。)
请注意,所有条目都可以包含 valid_until ,但 valid_until 为NULL的条目不能超过一个。
我如何在SQL查询中执行此操作? (如果可能在SQLAlchemy中,但我也可以自己从原始SQL翻译它)
(我正在使用PostgreSQL 9.1)
编辑:这是我的最终解决方案。感谢所有贡献者!SELECT *
FROM validity_period
WHERE valid_from <= CURRENT_DATE AND
valid_until >= CURRENT_DATE
UNION
SELECT *
FROM validity_period
WHERE valid_from <= CURRENT_DATE AND
valid_until IS NULL
ORDER BY valid_from DESC
LIMIT 1;
答案 0 :(得分:1)
您可以使用WINDOW函数(例如滞后或引导)来引用上一个/下一个记录,并限制“打开”间隔:
DROP SCHEMA tmp CASCADE;
CREATE SCHEMA tmp ;
SET search_path=tmp;
CREATE TABLE daterange
( id INTEGER NOT NULL
, valid_from DATE NOT NULL
, valid_until DATE
);
INSERT INTO daterange (id, valid_from, valid_until) VALUES
(1, '2012-11-12', '2012-12-02' )
,(2, '2012-12-03', NULL)
,(3, '2012-12-15', '2012-12-21' )
,(4, '2012-12-22', NULL )
;
-- The CTE is for convenience; could be a subquery or view
WITH magic AS (
SELECT dr.id
, dr.valid_from
, COALESCE(dr.valid_until , lead(valid_from) OVER ww) AS valid_until
FROM daterange dr
WINDOW ww AS (ORDER BY dr.valid_from )
)
SELECT *
FROM magic ma
WHERE now() BETWEEN ma.valid_from AND ma.valid_until
;
答案 1 :(得分:0)
尝试这样的事情:
SELECT *
FROM validity_period
WHERE valid_from <= current_date
AND valid_to >=current_date
UNION
--Gets the last instance where valid_until is null
SELECT *
FROM validity_period
WHERE valid_from <= current_date
AND valid_to IS NULL
ORDER BY id DESC LIMIT 1
如果您想要第一个空字段而不是最后一个空字段,只需删除DESC
答案 2 :(得分:0)
作为示例,valid_until在您提供的数据中在逻辑上是冗余的,如果存在valid_from上的索引并且PostgreSQL将执行降序索引扫描,则可以使用此逻辑获得更快的结果:
select *
from validity_period
where valid_from <= current_date
order by valid_from desc
limit 1
检查解释计划,看看它是否是对较大数据集的改进。
编辑:如果可能存在“最后”记录在今天之前有效的直到日期的情况,其中没有要返回的行,请尝试:
select most_recent_record.*
from (
select *
from validity_period
where valid_from <= current_date
order by valid_from desc
limit 1) most_recent_record
where coalesce(most_recent_record.valid_until,current_date) <= current_date
最后一个谓词可能更好地读作:
where not (most_recent_record.valid_until > current_date)
答案 3 :(得分:0)
你的问题不是很清楚。可以与非空valid_until
匹配多个匹配项吗?选哪个?你想要什么回报?一栏?一些?所有?
无论如何,您发布的解决方案可以简化为:
SELECT *
FROM validity_period
WHERE valid_from <= now()::date
AND (valid_until >= now()::date OR valid_until IS NULL) -- parenthesis needed!
ORDER BY valid_from DESC
LIMIT 1;