UPDATE ... ON CONFLICT DO NOTHING

From: Alexander Farber <alexander(dot)farber(at)gmail(dot)com>
To: pgsql-general <pgsql-general(at)postgresql(dot)org>
Subject: UPDATE ... ON CONFLICT DO NOTHING
Date: 2017-03-14 11:55:25
Message-ID: CAADeyWh1S6BFPE_GdFNkSgjvthXegtM1P7ktz0bsZ8x8j2CuBw@mail.gmail.com
Views: Raw Message | Whole Thread | Download mbox | Resend email
Thread:
Lists: pgsql-general

Good afternoon,

I am working on a small game, where the mobile app initially sends social
network user data (name, city, gender) to PostgreSQL 9.5 backend and I
store that data in a table:

CREATE TABLE words_social (
sid varchar(255) NOT NULL,

social integer NOT NULL CHECK (0 <= social AND social <= 6), /*
Facebook, Googl+, Twitter, ... */
female integer NOT NULL CHECK (female = 0 OR female = 1),
given varchar(255) NOT NULL CHECK (given ~ '\S'),
family varchar(255),
photo varchar(255) CHECK (photo ~* '^https?://...'),
place varchar(255),
stamp integer NOT NULL,

uid integer NOT NULL REFERENCES words_users ON DELETE CASCADE,
PRIMARY KEY(sid, social)
);

And then I have another larger table holding the rest of user information:

CREATE TABLE words_users (
uid SERIAL PRIMARY KEY,

created timestamptz NOT NULL,
visited timestamptz NOT NULL,
ip inet NOT NULL,
.....
win integer NOT NULL CHECK (win >= 0),
loss integer NOT NULL CHECK (loss >= 0),
draw integer NOT NULL CHECK (draw >= 0),

elo integer NOT NULL CHECK (elo >= 0),
medals integer NOT NULL CHECK (medals >= 0),
coins integer NOT NULL
);

Whenever the mobile app notices, that the user authenticated against
several social networks - I merge his data in my custom function:

CREATE OR REPLACE FUNCTION words_merge_users(
in_users jsonb,
in_ip inet,
OUT out_uid integer /* the user id of the merged user */
) RETURNS RECORD AS
$func$
DECLARE
_user jsonb;
_uids integer[];
-- the variables below are used to temporary save new user stats
_created timestamptz;
_win integer;
_loss integer;
_draw integer;
_elo integer;
_medals integer;
_coins integer;
BEGIN

_uids := (
SELECT ARRAY_AGG(DISTINCT uid)
FROM words_social
JOIN JSONB_ARRAY_ELEMENTS(in_users) x
ON sid = x->>'sid'
AND social = (x->>'social')::int
);
........
-- few users found -> merge their records to a single one
IF CARDINALITY(_uids) > 1 THEN
SELECT
MIN(uid),
MIN(created),
SUM(win),
SUM(loss),
SUM(draw),
AVG(elo),
SUM(medals),
SUM(coins)
INTO STRICT
out_uid, /* this is the new user id */
_created,
_win,
_loss,
_draw,
_elo,
_medals,
_coins
FROM words_users
WHERE uid = ANY(_uids);

-- How to merge words_reviews? Please read below...

UPDATE words_social
SET uid = out_uid
WHERE uid = ANY(_uids);

DELETE FROM words_users
WHERE uid <> out_uid
AND uid = ANY(_uids);

UPDATE words_users SET
visited = CURRENT_TIMESTAMP,
ip = in_ip,
created = _created,
win = _win,
loss = _loss,
draw = _draw,
elo = _elo,
medals = _medals,
coins = _coins
WHERE uid = out_uid;
END IF;
END
$func$ LANGUAGE plpgsql;

This works well, but now I have introduced a table where users can rate
each other ("author" can rate "uid"):

CREATE TABLE words_reviews (
uid integer NOT NULL CHECK (uid <> author) REFERENCES words_users
ON DELETE CASCADE,
author integer NOT NULL REFERENCES words_users(uid) ON DELETE
CASCADE,
nice integer NOT NULL CHECK (nice = 0 OR nice = 1),
review varchar(255),
updated timestamptz NOT NULL,
PRIMARY KEY(uid, author)
);

And now I have a problem, because while merging user data I can not just:

UPDATE words_reviews /* This will produce conflicts...
*/
SET uid = out_uid
WHERE uid = ANY(_uids);

DELETE FROM words_reviews
WHERE uid <> out_uid
AND uid = ANY(_uids);

And same for the authoring part - I can not just:

UPDATE words_reviews /* This will produce conflicts...
*/
SET author = out_uid
WHERE author = ANY(_uids);

DELETE FROM words_reviews
WHERE author <> out_uid
AND author = ANY(_uids);

Because this might result in PRIMARY KEY(uid, author) conflicts in the
words_reviews table.

I wish there would be possibility to add ON CONFLICT DO NOTHING /* and drop
the review */ to the both UPDATE's above, but there is no such thing
described at https://www.postgresql.org/docs/9.5/static/sql-update.html

What would you please recommend in my situation?

I'd like to merge user data including reviews and silently drop any
conflicting review records...

Regards
Alex

Responses

Browse pgsql-general by date

  From Date Subject
Next Message Антон Тарабрин 2017-03-14 12:09:24 Table not cleaning up drom dead tuples
Previous Message Schmid Andreas 2017-03-14 10:00:27 Re: createuser: How to specify a database to connect to