Postgres 默认值是两条记录的差

Posted

技术标签:

【中文标题】Postgres 默认值是两条记录的差【英文标题】:Postgres default value is the difference of two records 【发布时间】:2018-12-21 09:35:18 【问题描述】:

我有一个包含以下列的 postgres 表:id、文本、时间戳、差异 id 是自动生成的,文本是插入的,timestamp 是默认的,diff 应该是当前记录的时间戳和上一个记录的时间戳的差(prev by id)如果上一个与实际是同一天,如果不是,应该是0. 所以所有的列都是在插入文本时自动生成的。 您知道通过表达式等使此差异默认的方法吗?我是 sql 新手。

谢谢。

【问题讨论】:

DEFAULT 在这里没有用,因为它不能包含子查询。您可以使用ON INSERT 触发器填充该值。 默认表达式无法访问其他行。您必须为此编写触发器。但是为什么要生成 ans 来保持该值呢?以后可以随时计算。坚持它会带来不一致的风险。一般来说,可以从其他持久值计算出来的值不应该是持久的,除非有充分的理由并特别注意避免不一致。 【参考方案1】:

为此,您可以使用触发器:

demo:db<>fiddle

CREATE TABLE tbl (
    id int,
    ts timestamp DEFAULT now(),                                     -- 1
    diff float                                                      -- 2
);

CREATE FUNCTION set_ts_diff() RETURNS trigger AS $$
    DECLARE
        _last_ts timestamp;
    BEGIN
        SELECT COALESCE(MAX(ts), NEW.ts) FROM tbl INTO _last_ts;    -- 3

        IF (NEW.ts::date = _last_ts::date) THEN                     -- 4
            NEW.diff := EXTRACT(EPOCH FROM NEW.ts - _last_ts);      -- 5
        ELSE
            NEW.diff := 0;
        END IF;

        RETURN NEW;
    END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER tr_set_ts_diff BEFORE INSERT ON tbl                  -- 6
    FOR EACH ROW EXECUTE PROCEDURE set_ts_diff();
    创建您的表。 ts 默认获取当前时间戳 (now())。 diff 将用作以秒为单位的差异。因为可以在一秒钟内完成两次插入,所以 float 类型会有所帮助。另一种方法可能是 interval 类型,这是时间戳减法的默认类型(参见第 5 点) 触发器需要一个触发器函数来知道触发事件后要做什么。在这个函数中,我们得到最后一个 ts 之前存储在 tbl 中。如果不存在行,则在COALESCE 的帮助下获取当前的tsNEW 是要插入的当前行) 然后检查最后一个和当前ts 是否具有相同的日期(通过将两个时间戳转换为date 来检查)。 如果是这样,计算导致类型interval 的差异。要从 interval 获取秒数,请使用 EXTRACT(EPOCH FROM ...)。 创建触发器:在持久化新行 (ON BEFORE INSERT) 之前,执行触发器函数并设置 NEW.diff 列。

注意:

您应该几乎不保留以后可以计算的数据。可以借助LAG window function 计算两个时间戳之间的差异,它将上一行的值取为当前行的值(demo):

SELECT
    ts,
    EXTRACT(EPOCH FROM ts - lag(ts) OVER (ORDER BY ts)) as diff
FROM
    tbl;

如果您必须删除一行,则可以看出硬存储差异的缺点。下一个的差异现在有一个错误的值,因为它的引用丢失了。您的数据可能会变得不一致。

【讨论】:

【参考方案2】:

我会选择VIEW

CREATE OR REPLACE VIEW v_tbl AS
SELECT id,
       txt,
       tstamp,
       coalesce (  extract ( epoch FROM tstamp - LAG(tstamp)
        OVER(PARTITION BY date_trunc('day',tstamp) ORDER BY id)),0)
 FROM tbl;

Demo

【讨论】:

以上是关于Postgres 默认值是两条记录的差的主要内容,如果未能解决你的问题,请参考以下文章

在当前事件中初始化记录默认值

域名解析时要填的值是啥

Postgres:为 CAST 失败定义一个默认值?

mysql使用记录

在传递给Postgres之前使用Postgres默认值或生成它们是更好的做法吗?

Postgres 时间戳列的默认值设置未正确使用