PostgreSQL更新触发器比较Hstore值

时间:2017-06-09 21:59:55

标签: postgresql plpgsql hstore

我在PostgresSQL中创建触发器。在更新时,我想比较Hstore列中的所有值并更新镜像表中的更改。我设法在变量NEW中获取列的名称,但我无法从OLDCREATE 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=$1.ID;' USING OLD; END IF; END LOOP; RETURN NEW; END; $BODY$ language plpgsql; 获取值。

DECLARE @CustomerPreferredTime DATETIME = '6/10/17 09:00:00'

DECLARE @AppointmentDateTime TABLE
    (
        ID INT IDENTITY(1,1)  PRIMARY KEY,
        AppointmentTime DATETIME,
        Available BIT 
    )

INSERT  INTO @AppointmentDateTime
        (AppointmentTime,Available)
VALUES
        ('6/10/17 08:00:00',0),
        ('6/10/17 08:30:00',1),
        ('6/10/17 09:00:00',0),
        ('6/10/17 09:30:00',0),
        ('6/10/17 10:00:00',1),
        ('6/10/17 10:30:00',0),
        ('6/10/17 11:00:00',0),
        ('6/10/17 11:30:00',0)

SELECT TOP 1
    ROW_NUMBER() OVER (ORDER BY CASE    WHEN [@AppointmentDateTime].AppointmentTime >= @CustomerPreferredTime
                                        THEN 1
                                        ELSE 0
                                END DESC, ABS(DATEDIFF(MINUTE,
                                                        @CustomerPreferredTime,
                                                        AppointmentTime))) AS ClosestAppointmentOrder,  
    [@AppointmentDateTime].AppointmentTime
FROM
    @AppointmentDateTime
WHERE
    [@AppointmentDateTime].Available = 1
    AND CONVERT(DATE,AppointmentTime) = CONVERT(DATE,@CustomerPreferredTime)
ORDER BY
    ClosestAppointmentOrder

2 个答案:

答案 0 :(得分:2)

您应该对记录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;

答案 1 :(得分:1)

另一个版本 - 具有极少数更新 - 部分功能设计(可能的地方)。 该触发器应该是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 = $1',
         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命令数量的代码。