15

Postgres 8.4 データベースを (C# コードから) 更新していますが、基本的なタスクは単純です。既存の行を更新するか、まだ存在しない場合は新しい行を挿入します。通常、私はこれを行います:

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

0 行が影響を受けた場合は、INSERT を実行します。

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

ただし、わずかなねじれがあります。データがいつ更新されたかについてユーザーを誤解させないように、新しい値のいずれかが実際に既存の値と異なる場合を除き、updated_time 列と updated_username 列変更たくありません。

UPDATE のみを行っている場合は、値に WHERE 条件を追加することもできますが、ここでは機能しません。DB が既に最新の状態である場合、UPDATE は 0 行に影響し、INSERT を試行するからです。

SELECT、次にUPDATEまたはINSERT以外に、これを行うためのエレガントな方法を考えられる人はいますか?

4

6 に答える 6

6

BEFORE UPDATE トリガーを見て、正しい値を確認して設定します。

CREATE OR REPLACE FUNCTION my_trigger() RETURNS TRIGGER LANGUAGE plpgsql AS
$$
BEGIN
    IF OLD.content = NEW.content THEN
        NEW.updated_time= OLD.updated_time; -- use the old value, not a new one.
    ELSE
        NEW.updated_time= NOW();
    END IF;
    RETURN NEW;
END;
$$;

これで、UPDATE クエリでフィールド updated_time に言及する必要さえなくなりました。これはトリガーによって処理されます。

http://www.postgresql.org/docs/current/interactive/plpgsql-trigger.html

于 2010-08-12T07:25:06.250 に答える
6

ここで2つのこと。まず、データベースのアクティビティ レベルによっては、レコードのチェックと挿入の間に競合状態が発生する可能性があります。その間に、別のプロセスがそのレコードを作成する可能性があります。マニュアルには、この リンクの例を実行する方法の例が含まれています

更新を避けるために、suppress_redundant_updates_trigger() プロシージャがあります。これを必要に応じて使用するには、更新前にトリガーを 2 つ用意する必要があります。1 つ目は、変更が行われていない場合は、suppress_redundant_updates_trigger() を呼び出して更新を中止し、2 つ目は、更新が行われた場合にタイムスタンプとユーザー名を設定します。トリガーはアルファベット順に起動されます。これを行うことは、上記の例のコードを変更して、更新の前に最初に挿入を試みることも意味します。

更新を抑制する方法の例:

    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;
于 2010-08-12T10:00:06.247 に答える
3

Postgres は UPSERT をサポートしています。現在、2015 年 5 月 8 日以降、ツリーに含まれています ( commit )。

この機能はしばしば upsert と呼ばれます。

これは、「投機的挿入」と呼ばれる新しいインフラストラクチャを使用して実装されます。これは、最初に既存のタプルの事前チェックを行い、次に挿入を試みる、通常の挿入の楽観的な変形です。違反するタプルが同時に挿入された場合、投機的に挿入されたタプルは削除され、新しい試行が行われます。事前チェックで一致するタプルが見つかった場合、代替の DO NOTHING または DO UPDATE アクションが実行されます。競合を検出せずに挿入が成功した場合、タプルは挿入されたと見なされます。

スナップショットをダウンロードできます。まだリリースされていません。

于 2015-05-08T06:39:28.903 に答える
0

このRETURNING句を使用すると、クエリをチェーンできます。2 番目のクエリは最初の結果を使用します。(この場合、同じ行に再度触れるのを避けるため) (RETURNING は postgres 8.4 以降で利用可能です)

ここでは関数に埋め込まれていますが、プレーン SQL でも機能します

DROP SCHEMA tmp CASCADE;
CREATE SCHEMA tmp ;
SET search_path=tmp;

CREATE TABLE my_table
        ( updated_time timestamp NOT NULL DEFAULT now()
        , updated_username varchar DEFAULT '_none_'
        , criteria1 varchar NOT NULL
        , criteria2 varchar NOT NULL
        , value1 varchar
        , value2 varchar
        , PRIMARY KEY (criteria1,criteria2)
        );

INSERT INTO  my_table (criteria1,criteria2,value1,value2)
SELECT 'C1_' || gs::text
        , 'C2_' || gs::text
        , 'V1_' || gs::text
        , 'V2_' || gs::text
FROM generate_series(1,10) gs
        ;

SELECT * FROM my_table ;

CREATE function funky(_criteria1 text,_criteria2 text, _newvalue1 text, _newvalue2 text)
RETURNS VOID
AS $funk$
WITH ins AS (
        INSERT INTO my_table(criteria1, criteria2, value1, value2, updated_username)
        SELECT $1, $2, $3, $4, COALESCE(current_user, 'evgeny' )
        WHERE NOT EXISTS (
                SELECT * FROM my_table nx
                WHERE nx.criteria1 = $1 AND nx.criteria2 = $2
                )
        RETURNING criteria1 AS criteria1, criteria2 AS criteria2
        )
        UPDATE my_table upd
        SET value1 = $3, value2 = $4
        , updated_time = now()
        , updated_username = COALESCE(current_user, 'evgeny')
        WHERE 1=1
        AND criteria1 = $1 AND criteria2 = $2 -- key-condition
        AND (value1 <> $3 OR value2 <> $4 )   -- row must have changed
        AND NOT EXISTS (
                SELECT * FROM ins -- the result from the INSERT
                WHERE ins.criteria1 = upd.criteria1
                AND ins.criteria2 = upd.criteria2
                )
        ;
$funk$ language sql
        ;

SELECT funky('AA', 'BB' , 'CC', 'DD' );            -- INSERT
SELECT funky('C1_3', 'C2_3' , 'V1_3', 'V2_3' );    -- (null) UPDATE 
SELECT funky('C1_7', 'C2_7' , 'V1_7', 'V2_7777' ); -- (real) UPDATE 

SELECT * FROM my_table ;

結果:

        updated_time        | updated_username | criteria1 | criteria2 | value1 | value2  
----------------------------+------------------+-----------+-----------+--------+---------
 2013-03-13 16:37:55.405267 | _none_           | C1_1      | C2_1      | V1_1   | V2_1
 2013-03-13 16:37:55.405267 | _none_           | C1_2      | C2_2      | V1_2   | V2_2
 2013-03-13 16:37:55.405267 | _none_           | C1_3      | C2_3      | V1_3   | V2_3
 2013-03-13 16:37:55.405267 | _none_           | C1_4      | C2_4      | V1_4   | V2_4
 2013-03-13 16:37:55.405267 | _none_           | C1_5      | C2_5      | V1_5   | V2_5
 2013-03-13 16:37:55.405267 | _none_           | C1_6      | C2_6      | V1_6   | V2_6
 2013-03-13 16:37:55.405267 | _none_           | C1_8      | C2_8      | V1_8   | V2_8
 2013-03-13 16:37:55.405267 | _none_           | C1_9      | C2_9      | V1_9   | V2_9
 2013-03-13 16:37:55.405267 | _none_           | C1_10     | C2_10     | V1_10  | V2_10
 2013-03-13 16:37:55.463651 | postgres         | AA        | BB        | CC     | DD
 2013-03-13 16:37:55.472783 | postgres         | C1_7      | C2_7      | V1_7   | V2_7777
(11 rows)
于 2013-03-13T15:13:47.890 に答える
-1

取引を開始します。選択を使用して、挿入するデータが既に存在するかどうかを確認し、存在する場合は何もせず、そうでない場合は更新し、存在しない場合は挿入します。最後に取引を終了します。

于 2010-08-12T07:17:55.407 に答える