PG 11.5 中刪除的歷史表設計
我有一個關於 Postgres 中歷史表設計的問題。
設置是我有一個包含需求列表的表格。一個位置每五分鐘重新計算一次需求項目,並將該列表推送到 Postgres。然後,各種客戶端應用程序都可以訪問目前的“熱門”列表以進行拉取。因此,每五分鐘,與特定位置相關的行被刪除,然後重新填充現在熱門的內容。想像一下倉庫牆上的螢幕,人們抬頭看緊急任務之類的東西。這或多或少是一個隊列/通知表,而不是一個真正的儲存表。
我們在需求商品列表中跟踪的是帶有 ID 的特定元件。隨著時間的推移收集數據(或至少是統計數據)對我們來說很有價值。我們可能會發現每天都有特定項目出現在列表中,而其他項目則很少出現。這可以幫助指導購買選擇等。
這就是背景,我在 Postgres 11.5 中,所以沒有生成列。下面描述的策略看起來是正確的,還是可以改進?基表被呼叫
need
,歷史表被呼叫need_history
need
– 儲存感興趣的數據–作為表設置的一部分,
有一個
NOW()
分配給created_dts
on 。 – 有一個後觸發器來獲取已刪除行的“轉換錶”。–保存數據 的語句觸發器。INSERT
PER STATEMENT
INSERTS INTO
need_history
need_history
——這幾乎是需要的複製,但增加了一些額外的欄位。具體來說,在插入數據時預設deleted_dts
分配,並儲存記錄在需要表中存在的 ~ 秒數。
- 由於這是 PG 11.5,沒有生成列,所以我需要一個觸發器來計算.
NOW()``duration_seconds
EACH ROW``duration_seconds
更短:
need
使用語句級刪除觸發器推送到need_history
.
need_history
使用行級觸發器進行計算duration_seconds
,因為我沒有生成 PG 11.x 中可用的列。而且,為了解決顯而易見的問題,不,我不必儲存派生
duration_seconds
值,因為它可以即時生成,但是在這種情況下,我想進行非規範化以簡化各種查詢、排序和摘要.我的大腦也在說“詢問填充因子”,我不知道為什麼。
以下是初始設置程式碼,以防上面的摘要不清楚。我還沒有通過這個推送任何數據,所以它可能有缺陷。
對於如何在 Postgres 中最好地做到這一點的任何建議或建議,我將不勝感激。
BEGIN; DROP TABLE IF EXISTS data.need CASCADE; CREATE TABLE IF NOT EXISTS data.need ( id uuid NOT NULL DEFAULT NULL, item_id uuid NOT NULL DEFAULT NULL, facility_id uuid NOT NULL DEFAULT NULL, hsys_id uuid NOT NULL DEFAULT NULL, total_qty integer NOT NULL DEFAULT 0, available_qty integer NOT NULL DEFAULT 0, sterile_qty integer NOT NULL DEFAULT 0, still_need_qty integer NOT NULL DEFAULT 0, perc_down double precision NOT NULL DEFAULT '0', usage_ integer NOT NULL DEFAULT 0, need_for_case citext NOT NULL DEFAULT NULL, status citext NOT NULL DEFAULT NULL, created_dts timestamptz NOT NULL DEFAULT NOW(), CONSTRAINT need_id_pkey PRIMARY KEY (id) ); ALTER TABLE data.need OWNER TO user_change_structure; COMMIT; /* Define the trigger function to copy the deleted rows to the history table. */ CREATE FUNCTION data.need_delete_copy_to_history() RETURNS trigger AS $BODY$ BEGIN /* need.deleted_dts is auto-assigned on INSERT over in need, and need.duration_seconds is calculated in an INSERT trigger (PG 11.5, not PG 12, no generated columns). */ INSERT INTO data.need_history (id, item_id, facility_id, hsys_id, total_qty, available_qty, sterile_qty, still_need_qty, perc_down, usage_, need_for_case, status, created_dts) SELECT id, item_id, facility_id, hsys_id, total_qty, available_qty, sterile_qty, still_need_qty, perc_down, usage_, need_for_case, status, created_dts FROM deleted_rows; RETURN NULL; -- result is ignored since this is an AFTER trigger END; $BODY$ LANGUAGE plpgsql; /* Bind a trigger event to the function. */ DROP TRIGGER IF EXISTS trigger_need_after_delete ON data.need; CREATE TRIGGER trigger_need_after_delete AFTER DELETE ON data.need REFERENCING OLD TABLE AS deleted_rows FOR EACH STATEMENT EXECUTE FUNCTION data.need_delete_copy_to_history(); /* Define the table. */ BEGIN; DROP TABLE IF EXISTS data.need_history CASCADE; CREATE TABLE IF NOT EXISTS data.need_history ( id uuid NOT NULL DEFAULT NULL, item_id uuid NOT NULL DEFAULT NULL, facility_id uuid NOT NULL DEFAULT NULL, hsys_id uuid NOT NULL DEFAULT NULL, total_qty integer NOT NULL DEFAULT 0, available_qty integer NOT NULL DEFAULT 0, sterile_qty integer NOT NULL DEFAULT 0, still_need_qty integer NOT NULL DEFAULT 0, perc_down double precision NOT NULL DEFAULT '0', usage_ integer NOT NULL DEFAULT 0, need_for_case citext NOT NULL DEFAULT NULL, status citext NOT NULL DEFAULT NULL, created_dts timestamptz NOT NULL DEFAULT NULL, deleted_dts timestamptz NOT NULL DEFAULT NOW(), duration_seconds int4 NOT NULL DEFAULT 0, CONSTRAINT need_history_id_pkey PRIMARY KEY (id) ); ALTER TABLE data.need_history OWNER TO user_change_structure; COMMIT; /* Define the trigger function to update the duration count. In PG 12 we'll be able to do this with a generated column...easier. */ CREATE OR REPLACE FUNCTION data.need_history_insert_trigger() RETURNS trigger AS $BODY$ BEGIN /* Use DATE_TRUNC seconds to get just the whole seconds part of the timestamps. */ NEW.duration_seconds = EXTRACT(EPOCH FROM ( DATE_TRUNC('second', NEW.deleted_dts) - DATE_TRUNC('second', NEW.created_dts) )); RETURN NEW; END; $BODY$ LANGUAGE plpgsql; /* Bind a trigger event to the function. */ DROP TRIGGER IF EXISTS trigger_need_history_before_insert ON data.need_history; CREATE TRIGGER trigger_need_history_before_insert BEFORE INSERT ON data.need_history FOR EACH ROW EXECUTE FUNCTION data.need_history_insert_trigger();```
看起來不錯。
在 SQL 中實現隊列的難點不是歷史化,而是如何管理隊列本身(添加、查找和刪除項目)。如果有大量流量,您可能需要對隊列表進行積極的自動清理設置。
我會分區歷史表。人們通常忘記設計的是如何擺脫舊數據。歷史記錄表可能會變大,您不會無限期地需要數據。如果您已經對錶進行了分區(因此有 10 到幾百個分區),則很容易擺脫舊數據。