PostgreSQL 更新触发器比较 Hstore 值

PostgreSQL update trigger Comparing Hstore values

我正在 PostgresSQL 中创建触发器。在更新时,我想比较 Hstore 列中的所有值并更新我的镜像 table 中的更改。我设法在变量 k 中获取我的列的名称,但我无法使用它从 NEWOLD.

获取值
CREATE OR REPLACE FUNCTION function_replication() RETURNS TRIGGER AS
$BODY$
DECLARE
    k text;
BEGIN
        FOR k IN SELECT key FROM EACH(hstore(NEW)) LOOP
            IF NEW.k != OLD.k THEN
                EXECUTE 'UPDATE ' || TG_TABLE_NAME || '_2' || 'SET ' || k || '=' || new.k || ' WHERE ID=.ID;' USING OLD;
            END IF;
        END LOOP;
        RETURN NEW;
END;
$BODY$
language plpgsql;

您应该对记录 newold 的 hstore 表示进行操作。此外,使用 format() 函数以获得更好的控制和可读性。

create or replace function function_replication() 
returns trigger as
$body$
declare
    newh hstore = hstore(new);
    oldh hstore = hstore(old);
    key text;
begin
    foreach key in array akeys(newh) loop
        if newh->key != oldh->key then
            execute format(
                'update %s_2 set %s = %L where id = %s',
                tg_table_name, key, newh->key, oldh->'id');
        end if;
    end loop;
    return new;
end;
$body$
language plpgsql;

另一个版本 - 更新数量最少 - 在部分功能设计中(在可能的情况下)。 这个触发器应该是 AFTER 触发器,以确保正确的行为。

CREATE OR REPLACE FUNCTION function_replication()
RETURNS trigger AS $$
DECLARE
  newh hstore;
  oldh hstore;
  update_vec text[];
  pair text[];
BEGIN
  IF new IS DISTINCT FROM old THEN
    IF new.id <> old.id THEN
      RAISE EXCEPTION 'id should be immutable';
    END IF;
    newh := hstore(new); oldh := hstore(old); update_vec := '{}';
    FOREACH pair SLICE 1 IN ARRAY hstore_to_matrix(newh - oldh)
    LOOP
      update_vec := update_vec || format('%I = %L', pair[1], pair[2]);
    END LOOP; 
    EXECUTE  
      format('UPDATE %I SET %s WHERE id = ',
         tg_table_name || '_2',
         array_to_string(update_vec, ', '))
      USING old.id;
  END IF;
  RETURN NEW; -- the value is not important in AFTER trg 
END;
$$ LANGUAGE plpgsql;

CREATE TABLE foo(id int PRIMARY KEY, a int, b int);
CREATE TABLE foo_2(LIKE foo INCLUDING ALL);
CREATE TRIGGER xxx AFTER UPDATE ON foo
  FOR EACH ROW EXECUTE PROCEDURE function_replication();

INSERT INTO foo VALUES(1, NULL, NULL);
INSERT INTO foo VALUES(2, 1,1);
INSERT INTO foo_2 VALUES(1, NULL, NULL);
INSERT INTO foo_2 VALUES(2, 1,1);

UPDATE foo SET a = 20, b = 30 WHERE id = 1;
UPDATE foo SET a = NULL WHERE id = 1;

这段代码有点复杂,但是所有应该转义的都被转义了,减少了执行的UPDATE命令的数量。 UPDATE 是完整 SQL 命令,完整 SQL 命令的开销应该明显高于减少完整 SQL 命令数量的代码。