【发布时间】:2015-07-18 14:33:38
【问题描述】:
我编写了 3 个函数来将事务记录到指定表:
CREATE OR REPLACE FUNCTION log_sites() RETURNS TRIGGER AS $body$
DECLARE
target_row sites%ROWTYPE;
BEGIN
IF (TG_OP = 'DELETE') THEN
-- No NEW row
target_row = OLD;
ELSE
target_row = NEW;
END IF;
INSERT INTO sites_history (transaction_type,
transaction_time,
site_id,
address,
name,
shared_key)
VALUES (TG_OP,
NOW(),
target_row.site_id,
target_row.address,
target_row.name,
target_row.shared_key);
RETURN target_row;
END;
$body$
LANGUAGE plpgsql;
CREATE OR REPLACE FUNCTION log_licenses() RETURNS TRIGGER AS $body$
DECLARE
target_row licenses%ROWTYPE;
BEGIN
IF (TG_OP = 'DELETE') THEN
target_row = OLD;
ELSE
target_row = NEW;
END IF;
INSERT INTO licenses_history (transaction_type,
transaction_time,
license_id,
start_date,
expiration_date,
site_id)
VALUES (TG_OP,
NOW(),
target_row.license_id,
target_row.start_date,
target_row.expiration_date,
target_row.site_id);
RETURN target_row;
END;
$body$
LANGUAGE plpgsql;
CREATE OR REPLACE FUNCTION log_clients() RETURNS TRIGGER AS $body$
DECLARE
target_row clients%ROWTYPE;
BEGIN
IF (TG_OP = 'DELETE') THEN
target_row = OLD;
ELSE
target_row = NEW;
END IF;
INSERT INTO clients_history (transaction_type,
transaction_time,
mac_address,
hardware,
license_id,
site_id)
VALUES (TG_OP,
NOW(),
target_row.mac_address,
target_row.hardware,
target_row.license_id,
target_row.site_id);
RETURN target_row;
END;
$body$
LANGUAGE plpgsql;
这导致 PL/pgSQL 的一大块丑陋,在我的工作中没有人特别熟悉,包括我自己。一位同事建议整合/干燥所有这些东西会很好,但对于我的生活,我不确定如何,特别是考虑到每个表都需要一个单独的触发器,以及way triggers pass data to their functions。有什么建议吗?
预计到达时间: 1) 以下是触发器:
CREATE TRIGGER sites_log
AFTER INSERT OR UPDATE OR DELETE
ON sites
FOR EACH ROW EXECUTE PROCEDURE log_transactions();
CREATE TRIGGER licenses_log
AFTER INSERT OR UPDATE OR DELETE
ON licenses
FOR EACH ROW EXECUTE PROCEDURE log_transactions();
CREATE TRIGGER clients_log
AFTER INSERT OR UPDATE OR DELETE
ON clients
FOR EACH ROW EXECUTE PROCEDURE log_transactions();
这是我现在得到的,经过相当多的混乱:
CREATE OR REPLACE FUNCTION log_transactions() RETURNS TRIGGER LANGUAGE plpgsql AS $body$
DECLARE
target_row RECORD;
target_cols text[];
col_name RECORD;
col_name_str text;
right_now timestamp without time zone;
q_str text;
BEGIN
right_now := now();
target_cols := '{}';
FOR col_name IN SELECT column_name::text FROM information_schema.columns WHERE table_name = TG_TABLE_NAME AND table_schema = TG_TABLE_SCHEMA LOOP
col_name_str := col_name.column_name::text;
target_cols = ARRAY_APPEND(target_cols, col_name_str);
END LOOP;
RAISE NOTICE 'target_cols: %', target_cols;
IF (TG_OP = 'DELETE') THEN
target_row := OLD;
ELSE
target_row := NEW;
END IF;
RAISE NOTICE 'target_row: %', target_row;
EXECUTE format('INSERT INTO %I_history (transaction_time, transaction_type) VALUES (%L, %L)', TG_TABLE_NAME, right_now, TG_OP);
q_str := format('UPDATE %I_history SET (%s) = ', TG_TABLE_NAME, array_to_string(target_cols, ', ')) || '$1' || format(' WHERE transaction_type = %L AND transaction_time = %L', TG_OP, right_now);
EXECUTE q_str USING target_row;
RETURN target_row;
END;
$body$;
这也不起作用,而且在复杂性方面正在失控。
【问题讨论】:
-
使用
EXECUTE和动态 SQL。 wiki 上有很多例子。 (我并没有忘记发布关于 DRY 的常见问题解答的讽刺意味)。 -
您应该添加实际触发器以配合功能,否则可能会出现歧义/错误。以及触发器应该做什么的一些描述确切地。
标签: postgresql triggers plpgsql postgresql-9.4