我有一个字段,每个日期需要一个有效的行。从文档中我看到如何通过EXCLUDE使用gist来防止重叠。如何确保数据始终紧凑,即没有孔?
CREATE TABLE observations (
vintage daterange,
value numeric,
EXCLUDE USING gist (vintage WITH &&)
);
如何确保每个日期都有值?在实际应用中有第一个观察,最新的观察结果为null,作为日期范围中的第二个字段。
答案 0 :(得分:2)
假设您按时间顺序输入数据,最好的选择可能是INSERT
触发器。这是因为您不仅需要插入新数据,还需要在当前条目之前关闭最近条目的日期范围。
CREATE FUNCTION trf_bi_observations() RETURNS trigger AS $$
DECLARE
recent_date date;
BEGIN
-- Find the most recently entered date
SELECT max(lower(vintage)) INTO recent_date
FROM observations;
-- Make sure the new date range makes sense: it has to be after the most recently
-- entered date range and it has to have an open upper range.
IF lower(NEW.vintage) <= recent_date OR NOT upper_inf(NEW.vintage) THEN
RAISE NOTICE 'New vintage must be more recent than latest vintage and have an open upper bound';
RETURN NULL; -- Fail the insert
END IF;
-- Update the most recent record to close the upper bound of vintage
UPDATE observations
SET vintage = daterange(recent_date, lower(NEW.vintage)) -- upper bound exclusive
WHERE lower(vintage) = recent_date;
-- All done, make the INSERT happen
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER tr_bi_observations
BEFORE INSERT ON observations
FOR EACH ROW EXECUTE PROCEDURE trf_bi_observations();
请注意,使用此INSERT
触发器,您不再需要EXCLUDE
约束,因为触发器功能的逻辑不允许重叠。但是,您必须考虑更新和删除。如果您想禁止此表上的删除,您可以简单地撤销该权限,或者使用BEFORE DELETE
触发器除RETURN NULL
之外什么也不做(这会使DELETE
操作失败)。您必须允许更新(或上面的插入触发器将失败),但您可以将其限制为value
列中的更改并关闭最近行的上限:
-- OLD upper bound must be open, else disallow change to vintage column
IF upper_inf(OLD.vintage) THEN
-- Only allow changes in upper bound
NEW.vintage = daterange(lower(OLD.vintage), upper(NEW.vintage));
ELSE
NEW.vintage = OLD.vintage -- silently ignore changes
END IF;
根据您的要求 - 没有重叠,没有差距 - 您只需使用date
处理&#34;年份&#34;就可以大大简化您的数据模型。列,而不是daterange
。范围始终可以从当前行的date
到以下角色的date
构建。这隐含地满足了这两个要求,并且触发函数变得更加简单:
CREATE FUNCTION trf_bi_observations2() RETURNS trigger AS $$
BEGIN
-- Make sure the new date makes sense: it has to be after the most recently
-- entered date.
PERFORM * FROM observations WHERE vintage >= NEW.vintage;
IF FOUND THEN
RAISE NOTICE 'New vintage must be more recent than latest vintage';
RETURN NULL; -- Fail the insert
END IF;
-- All done, make the INSERT happen
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER tr_bi_observations2
BEFORE INSERT ON observations
FOR EACH ROW EXECUTE PROCEDURE trf_bi_observations2();
现在,您不必使用打开的上限更新最近的行,因此您可以禁止对列&#34; vintage&#34;进行任何更新。您还可以决定允许插入和更新任何日期,以便各个行的日期范围随此类事件而变化。由你决定。在任何一种情况下,使用简单的date
值都比使用daterange
更容易,更快。
您仍然可以使用&#34; vintage&#34;来检索数据。以daterange
格式使用正确的SELECT
语句:
SELECT daterange(vintage, lead(vintage) OVER (ORDER BY vintage ASC)) AS vintage, value
FROM observations;
lead()
窗口功能会为您提供&#34;年份的date
&#34;窗口框架中下一行的列,由于订购而成为下一个日期。