在 PostgreSQL 中每天存储日志

Guf*_*ran 6 postgresql clustering

这里的要求是存储历史日志。

为简单起见,我将假设这个示例场景,我们在我们的网站上销售一些产品,我们需要保留销售员每天销售的每种产品的销售记录。销售员和产品的数量是已知且恒定的。

现在,假设我们有 3 种产品在售,笔记本电脑咖啡杯
这里我有这张表来记录今天的销售记录(实时记录,将全天更新)

CREATE TABLE IF NOT EXISTS sales_record (
    id SERIAL,
    salesman_id INT NOT NULL,
    sold_laptop INT NOT NULL,
    sold_mugs INT NOT NULL,
    sold_pen INT NOT NULL,
    PRIMARY KEY (id)
);
Run Code Online (Sandbox Code Playgroud)

另一个表来保存旧数据的记录

CREATE TABLE IF NOT EXISTS sales_record_log (
    id SERIAL,
    salesman_id INT NOT NULL,
    sold_laptop INT NOT NULL,
    sold_mugs INT NOT NULL,
    sold_pen INT NOT NULL,
    record_for_day TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (id)
);
Run Code Online (Sandbox Code Playgroud)

启动并运行它后,我们需要做的就是不断更新记录集并为每次销售将适当的列增加 1。

我们在此设置中遇到的问题是,必须在每天结束时将数据从实时表移动到另一个表并从实时表中刷新sell_*记录,如下所示:

INSERT INTO sales_record_log
    (salesman_id, sold_laptop, sold_mugs, sold_pen)
        SELECT salesman_id, sold_laptop, sold_mugs, sold_pen
        FROM sales_record;

UPDATE sales_record SET sold_laptop = 0, sold_pen = 0, sold_mugs = 0;
Run Code Online (Sandbox Code Playgroud)

为了自动化日志记录、移动数据和清理过程,我正在研究 PostgreSQL 中的表集群和触发器的可能情况,以便在新记录插入父表时自动更新后代表,但我似乎找不到方法来同步在每一个恰当的纪录UPDATE直播表,然后检查是否登录表有今天的记录,如果是,那么别的更新适当的立柱插入新行,然后更新的价值。
请记住,可能有一个销售员几天没有进行任何销售,当他在 5 天后进行销售时,日志表将没有该销售员关于那 5 天没有进行销售的记录。

我有一种强烈的感觉,可以有比这更好的设置,但我一无所知。如果我做错了,任何人都可以帮我设置或指导我吗?

编辑


我决定在同一个表中同时维护 Live 和 Old 记录,并为其选择 Old-Record 的表结构,在PRIMARY KEYie 中稍作修改

CREATE TABLE IF NOT EXISTS sales_record (
    id SERIAL,
    salesman_id INT NOT NULL,
    sold_laptop INT NOT NULL,
    sold_mugs INT NOT NULL,
    sold_pen INT NOT NULL,
    record_for_day DATE NOT NULL DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (salesman_id, record_for_day)
);
Run Code Online (Sandbox Code Playgroud)

请注意PRIMARY KEYrecord_for_day 列的数据类型和变化。

现在,我使用类似UPSERT 的函数插入数据:

CREATE FUNCTION update_log(salesman INT,
                           laptops INT DEFAULT 0,
                           mugs INT DEFAULT 0,
                           pens INT DEFAULT 0,
                           for_day DATE DEFAULT CURRENT_TIMESTAMP)
RETURNS VOID AS
'
BEGIN
    LOOP
        UPDATE sales_record SET
            sold_laptop = sold_laptop + laptops,
            sold_mugs = sold_mugs + mugs,
            sold_pen = sold_pen + pens
            WHERE salesman_id = salesman AND record_for_day = for_day;

        IF found THEN
            RETURN;
        END IF;

        BEGIN
            INSERT INTO sales_record
                (salesman_id, sold_laptop, sold_mugs, sold_pen, record_for_day)
            VALUES
                (salesman, laptop, mugs, pens, for_day);
            RETURN;

            EXCEPTION WHEN unique_violation THEN
                -- Encountered only if someone else has inserted the record in meantime
                -- In that case, lets loop and try to update again
        END;
    END LOOP;
END;
'
LANGUAGE plpgsql;
Run Code Online (Sandbox Code Playgroud)

尝试插入

SELECT * FROM sales_record;

0 Records

SELECT update_log(2, 1, 0, 0, CURRENT_TIMESTAMP);

SELECT * FROM sales_record;

+---------------------------------------------------------------------+
| id  | salesman_id | sold_laptop | sold_mugs | sold_pen |   for_day  |
|---------------------------------------------------------------------|
| 1   |    2        |    1        |    0      |   0      | 18/02/2013 |
+---------------------------------------------------------------------+

SELECT update_log(2, 0, 1, 2, CURRENT_TIMESTAMP);

+---------------------------------------------------------------------+
| id  | salesman_id | sold_laptop | sold_mugs | sold_pen |   for_day  |
|---------------------------------------------------------------------|
| 1   |    2        |    1        |    1      |   1      | 18/02/2013 |
+---------------------------------------------------------------------+

SELECT update_log(32, 0, 1, 1, CURRENT_TIMESTAMP);

SELECT * FROM sales_record;

+---------------------------------------------------------------------+
| id  | salesman_id | sold_laptop | sold_mugs | sold_pen |   for_day  |
|---------------------------------------------------------------------|
| 2   |    32       |    0        |    1      |   1      | 18/02/2013 |
|---------------------------------------------------------------------|
| 1   |    2        |    1        |    1      |   1      | 18/02/2013 |
+---------------------------------------------------------------------+
Run Code Online (Sandbox Code Playgroud)

比 CRON、触发器或规则好,但仍不令人满意。
我知道我表现得太贪心了,但是伙计们,谁不想要更多呢?

对我来说,它看起来更像是一个 hack,虽然我没有测试过它,但它可能会在某个时间点失败。

我仍然愿意接受建议。

Chr*_*ers 3

IMO 的最佳选择是使用 cron 作业来生成 'yesterday'::date 的日志。您还可以在插入/更新/删除之前使用触发器来更新另一个表,但这会增加复杂性和开销,并且对于当前来说,这会变得非常复杂。一旦数据不再改变,就生成历史日志。

在本例中,您编写一个 sql 查询并通过 psql 和 cron 运行它。

如果可以的话,我还会添加一个触发器,拒绝更新或删除历史数据中涵盖的记录。

这给您带来了一些好处:

  1. 断掉的时候更明显

  2. 更简单,故障案例更简单

现在,根据您的担忧:

  1. 你说你每天都需要行。在 PostgreSQL 中可以通过多种相对简单的方法来处理(请记住,日期支持整数数学,因此您可以采用基准日期并向其添加一系列,以生成日期系列)。如果您每周每天生成行等,这是一种非常简单的解决方法。

  2. 你说你不能保证事情不会改变。这里的关键问题是您的更改窗口是什么以及在此窗口关闭后如何制作历史报告。例如,如果是在一个月之后,您可以生成一个月前一个月内所有日期的报告(即在三月初期间生成一月份的所有日期)。然后,您可以依靠视图来实时处理较新的行和较旧的行。然后,您可以使用触发器来确保订单表中插入行的日期比其他表中的最新日期新。

根据我的经验,通常没有必要担心将其保留为实时摘要。小型组织(数据集较小)往往每年至少结账一次,实时报告是其中的一种选择。拥有较大数据集的大型组织倾向于每月左右结清一次应收账款和应付账款(即发票),因此唯一需要实时报告的领域(因为它们可能会进行调整或修订)是未结订单(可以被修改)和可能需要偶尔审查的发票(并且永远不应该被修改,但可能会针对它们进行调整,这些调整可能需要也可能不需要在这样的系统中跟踪)。