UPSERT
PostgreSQL의 다양한 구현에 대해 읽었 지만이 모든 솔루션은 비교적 오래되었거나 비교적 이국적입니다 ( 예 : 쓰기 가능한 CTE 사용 ).
그리고 나는이 솔루션이 오래되어 잘 권장되는지 또는 거의 모든 제품이 생산 용도에 적합하지 않은 장난감 예제인지 여부를 즉시 알아내는 psql 전문가는 아닙니다.
PostgreSQL에서 UPSERT를 구현하는 가장 안전한 스레드 안전 방법은 무엇입니까?
UPSERT
PostgreSQL의 다양한 구현에 대해 읽었 지만이 모든 솔루션은 비교적 오래되었거나 비교적 이국적입니다 ( 예 : 쓰기 가능한 CTE 사용 ).
그리고 나는이 솔루션이 오래되어 잘 권장되는지 또는 거의 모든 제품이 생산 용도에 적합하지 않은 장난감 예제인지 여부를 즉시 알아내는 psql 전문가는 아닙니다.
PostgreSQL에서 UPSERT를 구현하는 가장 안전한 스레드 안전 방법은 무엇입니까?
답변:
PostgreSQL에는 이제 UPSERT가 있습니다.
비슷한 StackOverflow 질문 에 따른 선호되는 방법 은 현재 다음과 같습니다.
CREATE TABLE db (a INT PRIMARY KEY, b TEXT);
CREATE FUNCTION merge_db(key INT, data TEXT) RETURNS VOID AS
$$
BEGIN
LOOP
-- first try to update the key
UPDATE db SET b = data WHERE a = key;
IF found THEN
RETURN;
END IF;
-- not there, so try to insert the key
-- if someone else inserts the same key concurrently,
-- we could get a unique-key failure
BEGIN
INSERT INTO db(a,b) VALUES (key, data);
RETURN;
EXCEPTION WHEN unique_violation THEN
-- do nothing, and loop to try the UPDATE again
END;
END LOOP;
END;
$$
LANGUAGE plpgsql;
SELECT merge_db(1, 'david');
SELECT merge_db(1, 'dennis');
업데이트 (2015-08-20) :
ON CONFLICT DO UPDATE
(공식 문서)를 사용하여 업 서트를 처리하기위한 공식적인 구현이 있습니다 . 이 글을 쓰는 시점에서이 기능은 현재 PostgreSQL 9.5 Alpha 2에 있으며 Postgres 소스 디렉토리 에서 다운로드 할 수 있습니다 .
다음은 item_id
기본 키 라고 가정하는 예입니다 .
INSERT INTO my_table
(item_id, price)
VALUES
(123456, 10.99)
ON
CONFLICT (item_id)
DO UPDATE SET
price = EXCLUDED.price
원본 게시물 ...
다음은 삽입 또는 업데이트 발생 여부에 대한 가시성을 얻고 자 할 때 달성 한 구현입니다.
정의는 upsert_data
가격과 item_id를 두 번 지정하지 않고 값을 단일 자원으로 통합하는 것입니다. 업데이트에 대해 한 번, 삽입에 대해 다시 한 번.
WITH upsert_data AS (
SELECT
'19.99'::numeric(10,2) AS price,
'abcdefg'::character varying AS item_id
),
update_outcome AS (
UPDATE pricing_tbl
SET price = upsert_data.price
FROM upsert_data
WHERE pricing_tbl.item_id = upsert_data.item_id
RETURNING 'update'::text AS action, item_id
),
insert_outcome AS (
INSERT INTO
pricing_tbl
(price, item_id)
SELECT
upsert_data.price AS price,
upsert_data.item_id AS item_id
FROM upsert_data
WHERE NOT EXISTS (SELECT item_id FROM update_outcome LIMIT 1)
RETURNING 'insert'::text AS action, item_id
)
SELECT * FROM update_outcome UNION ALL SELECT * FROM insert_outcome
의 사용이 마음에 들지 않으면 다음과 같은 upsert_data
대체 구현이 있습니다.
WITH update_outcome AS (
UPDATE pricing_tbl
SET price = '19.99'
WHERE pricing_tbl.item_id = 'abcdefg'
RETURNING 'update'::text AS action, item_id
),
insert_outcome AS (
INSERT INTO
pricing_tbl
(price, item_id)
SELECT
'19.99' AS price,
'abcdefg' AS item_id
WHERE NOT EXISTS (SELECT item_id FROM update_outcome LIMIT 1)
RETURNING 'insert'::text AS action, item_id
)
SELECT * FROM update_outcome UNION ALL SELECT * FROM insert_outcome
삽입 또는 업데이트가 발생했는지 여부를 알려줍니다.
with "update_items" as (
-- Update statement here
update items set price = 3499, name = 'Uncle Bob'
where id = 1 returning *
)
-- Insert statement here
insert into items (price, name)
-- But make sure you put your values like so
select 3499, 'Uncle Bob'
where not exists ( select * from "update_items" );
업데이트가 발생하면 삽입물 0이 표시되고 그렇지 않으면 삽입물 1 또는 오류가 발생합니다.