仅当值不同时才使用Postgres UPSERT(INSERT或UPDATE) [英] Postgres UPSERT (INSERT or UPDATE) only if value is different

查看:224
本文介绍了仅当值不同时才使用Postgres UPSERT(INSERT或UPDATE)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在更新一个Postgres 8.4数据库(从C#代码开始),基本任务很简单:更新一个现有的行或插入一个新的行(如果尚不存在的话).通常我会这样做:

I'm updating a Postgres 8.4 database (from C# code) and the basic task is simple enough: either UPDATE an existing row or INSERT a new one if one doesn't exist yet. Normally I would do this:

UPDATE my_table
SET value1 = :newvalue1, ..., updated_time = now(), updated_username = 'evgeny'
WHERE criteria1 = :criteria1 AND criteria2 = :criteria2

,如果0行受到影响,则执行INSERT:

and if 0 rows were affected then do an INSERT:

INSERT INTO my_table(criteria1, criteria2, value1, ...)
VALUES (:criteria1, :criteria2, :newvalue1, ...)

但是,有一点扭曲.我不想更改 updated_time updated_username 列,除非任何新值实际上与现有值不同,以避免误导用户有关数据何时更新.

There is a slight twist, though. I don't want to change the updated_time and updated_username columns unless any of the new values are actually different from the existing values to avoid misleading users about when the data was updated.

如果我仅执行UPDATE,那么我也可以为这些值添加WHERE条件,但这在这里不起作用,因为如果数据库已经是最新的,那么UPDATE将影响0行,那么我会尝试插入.

If I was only doing an UPDATE then I could add WHERE conditions for the values as well, but that won't work here, because if the DB is already up to date the UPDATE will affect 0 rows and then I would try to INSERT.

除了SELECT之外,还有谁能想到一种优雅的方式来实现此目的,然后是UPDATE还是INSERT?

Can anyone think of an elegant way to do this, other than SELECT, then either UPDATE or INSERT?

推荐答案

这里有两件事. 首先,根据数据库中的活动级别,您可能会在检查记录与将记录插入到其他过程可能会在其中创建该记录的位置之间遇到竞争条件. 该手册包含如何执行此操作的示例 链接示例

Two things here. Firstly depending on activity levels in your database you may hit a race condition between checking for a record and inserting it where another process may create that record in the interim. The manual contains an example of how to do this link example

为避免进行更新,有prevent_redundant_updates_trigger()过程.若要按需使用它,必须在更新触发器之前有两个触发器,第一个将在没有进行任何更改的情况下调用prevent_redundant_updates_trigger()放弃更新,第二个将设置时间戳记和用户名(如果进行了更新).触发器按字母顺序触发. 这样做还意味着更改上面示例中的代码,以便在更新之前先尝试插入.

To avoid doing an update there is the suppress_redundant_updates_trigger() procedure. To use this as you wish you wold have to have two before update triggers the first will call the suppress_redundant_updates_trigger() to abort the update if no change made and the second to set the timestamp and username if the update is made. Triggers are fired in alphabetical order. Doing this would also mean changing the code in the example above to try the insert first before the update.

抑制更新如何工作的示例:

Example of how suppress update works:

    DROP TABLE sru_test;

    CREATE TABLE sru_test(id integer not null primary key,
    data text,
    updated timestamp(3));

    CREATE TRIGGER z_min_update
    BEFORE UPDATE ON sru_test
    FOR EACH ROW EXECUTE PROCEDURE suppress_redundant_updates_trigger();

    DROP FUNCTION set_updated();

    CREATE FUNCTION set_updated()
    RETURNS TRIGGER
    AS $$
    DECLARE
    BEGIN
        NEW.updated := now();
        RETURN NEW;
    END;
    $$ LANGUAGE plpgsql;

    CREATE TRIGGER zz_set_updated
    BEFORE INSERT OR UPDATE ON sru_test
    FOR EACH ROW EXECUTE PROCEDURE  set_updated();

insert into sru_test(id,data) VALUES (1,'Data 1');
insert into sru_test(id,data) VALUES (2,'Data 2');

select * from sru_test;

update sru_test set data = 'NEW';

select * from sru_test;

update sru_test set data = 'NEW';

select * from sru_test;

update sru_test set data = 'ALTERED'  where id = 1;

select * from sru_test;

update sru_test set data = 'NEW' where id = 2;

select * from sru_test;

这篇关于仅当值不同时才使用Postgres UPSERT(INSERT或UPDATE)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆