问题
I've got an UPSERT Operation like this:
INSERT INTO people (SELECT * FROM people_update)
ON CONFLICT (name,surname)
DO UPDATE SET age = EXCLUDED.age , street = EXCLUDED.street , city = EXCLUDED.city , postal = EXCLUDED.postal
WHERE
(people.age,people.street,people.city,people.postal) IS DISTINCT FROM (EXCLUDED.age,EXCLUDED.street,EXCLUDED.city,EXCLUDED.postal)
RETURNING case when xmax::text::int > 0 then 'updated' else 'inserted' end,name,surname,age,street,city,postal;
(name,surname)
is a composite primary key and the 'people_update' table contains additional and changed rows in comparison to the 'people' table.
My question is: Is there a way to get the inserted and updated rows of this query as the returning rows?
Edit:
I halfway solved the problem by adding a RETURNING
clause but I would also like to get the old values within my return values.
回答1:
If you add a boolean updated column to the people
table:
ALTER TABLE people ADD COLUMN updated bool DEFAULT FALSE;
then you could identify updated rows by setting updated = TRUE
in the DO UPDATE SET
clause:
INSERT INTO people (SELECT * FROM people_update)
ON CONFLICT (name,surname)
DO UPDATE SET age = EXCLUDED.age , street = EXCLUDED.street , city = EXCLUDED.city
, postal = EXCLUDED.postal
, updated = TRUE
WHERE
(people.age,people.street,people.city,people.postal) IS DISTINCT FROM
(EXCLUDED.age,EXCLUDED.street,EXCLUDED.city,EXCLUDED.postal)
RETURNING *;
For example,
CREATE TABLE test.people (
name text
, surname text
, age float
, street text
, city text
, postal int
);
CREATE UNIQUE INDEX people_idx on people (name, surname);
ALTER TABLE people ADD COLUMN updated bool;
ALTER TABLE people ADD COLUMN prior_age float;
ALTER TABLE people ADD COLUMN prior_street text;
ALTER TABLE people ADD COLUMN prior_city text;
ALTER TABLE people ADD COLUMN prior_postal int;
INSERT INTO people (name, surname, age, street, city, postal) VALUES
('Sancho', 'Panza', 414, '1 Manchego', 'Barcelona', 01605)
, ('Oliver', 'Twist', 182, '2 Stilton', 'London', 01837)
, ('Quasi', 'Modo', 188, $$3 Rue d'Arcole$$, 'Paris' , 01831 )
;
CREATE TABLE test.people_update (
name text
, surname text
, age float
, street text
, city text
, postal int
);
INSERT INTO people_update (name, surname, age, street, city, postal) VALUES
('Sancho', 'Panza', 4140, '10 Idiazabal', 'Montserrat', 16050)
, ('Quasi', 'Modo', 1880, $$30 Champs Elysée$$ , 'Paris', 18310 )
, ('Pinocchio', 'Geppetto', 1380, '40 Nerbone', 'Florence', 18810)
;
INSERT INTO people (SELECT * FROM people_update)
ON CONFLICT (name,surname)
DO UPDATE SET
updated = TRUE
, prior_age = (CASE WHEN people.age = EXCLUDED.age THEN NULL ELSE people.age END)
, prior_street = (CASE WHEN people.street = EXCLUDED.street THEN NULL ELSE people.street END)
, prior_city = (CASE WHEN people.city = EXCLUDED.city THEN NULL ELSE people.city END)
, prior_postal = (CASE WHEN people.postal = EXCLUDED.postal THEN NULL ELSE people.postal END)
, age = EXCLUDED.age
, street = EXCLUDED.street
, city = EXCLUDED.city
, postal = EXCLUDED.postal
WHERE
(people.age,people.street,people.city,people.postal) IS DISTINCT FROM
(EXCLUDED.age,EXCLUDED.street,EXCLUDED.city,EXCLUDED.postal)
RETURNING *;
yields
| name | surname | age | street | city | postal | updated | prior_age | prior_street | prior_city | prior_postal |
|------------+----------+------+------------------+------------+--------+---------+-----------+----------------+------------+--------------|
| Sancho | Panza | 4140 | 10 Idiazabal | Montserrat | 16050 | t | 414 | 1 Manchego | Barcelona | 1605 |
| Quasi | Modo | 1880 | 30 Champs Elysée | Paris | 18310 | t | 188 | 3 Rue d'Arcole | | 1831 |
| Pinocchio | Geppetto | 1380 | 40 Nerbone | Florence | 18810 | f | | | | |
The updated
column shows the ('Sancho', 'Panza')
and ('Quasi', 'Modo')
lines have been updated, and
('Pinocchio', 'Geppetto')
is a new insert.
来源:https://stackoverflow.com/questions/54925725/how-can-i-get-the-inserted-and-updated-rows-for-an-upsert-operation-in-postgres