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
The RETURNING
clause enables you to chain your queries; the second query uses the results from the first. (in this case to avoid re-touching the same rows) (RETURNING is available since postgres 8.4)
Shown here embedded in a a function, but it works for plain SQL, too
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 ;
RESULT:
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)