기존 데이터에 따라 UPDATE 또는 INSERT 쿼리를 실행해야하는 "병합"함수를 만들었습니다. 사용 가능한 대부분의 예제에서와 같이 각 테이블에 대해 upsert-wrapper를 작성하는 대신이 함수는 전체 SQL 문자열을 사용합니다. 두 SQL 문자열 모두 응용 프로그램에서 자동으로 생성됩니다.이 PostgreSQL의 MERGE/UPSERT 기능은 안전합니까?
이-- hypothetical "settings" table, with a primary key of (user_id, setting):
SELECT merge(
$$UPDATE settings SET value = 'x' WHERE user_id = 42 AND setting = 'foo'$$,
$$INSERT INTO settings (user_id, setting, value) VALUES (42, 'foo', 'x')$$
);
여기에 병합() 함수의 전체 코드입니다 :
계획은이 같은 함수를 호출하는 것입니다,
CREATE OR REPLACE FUNCTION merge (update_sql TEXT, insert_sql TEXT) RETURNS TEXT AS
$func$
DECLARE
max_iterations INTEGER := 10;
i INTEGER := 0;
num_updated INTEGER;
BEGIN
-- usually returns before re-entering the loop
LOOP
-- first try the update
EXECUTE update_sql;
GET DIAGNOSTICS num_updated = ROW_COUNT;
IF num_updated > 0 THEN
RETURN 'UPDATE';
END IF;
-- nothing was updated: try the insert, watching out for concurrent inserts
BEGIN
EXECUTE insert_sql;
RETURN 'INSERT';
EXCEPTION WHEN unique_violation THEN
-- nop; just loop and try again from the top
END;
-- emergency brake
i := i + 1;
IF i >= max_iterations THEN
RAISE EXCEPTION 'merge(): tried looping % times, giving up now.', i;
EXIT;
END IF;
END LOOP;
END;
$func$
LANGUAGE plpgsql;
내 테스트에서 충분히 잘 작동하는 것처럼 보이지만 특히이 함수를 사용하지 않고도 발행 될 수있는 동시 UPDATE/INSERT/DELETE 쿼리와 관련하여 중대한 사항을 놓치지 않았다면 확실하지 않습니다. 나는 중요한 것을 간과 했는가? 나는이 기능을 위해 상담 자원 중
은 다음과 같습니다- UPDATE/INSERT example 40.2 in the PostgreSQL manual
- Why is UPSERT so complicated?
- SO: Insert, on duplicate update (postgresql)
(편집 : 목표 중 하나가 목표 테이블을 잠금 방지하는 것이 었습니다.)
필자는 쓰기 가능한 CTE를 고려해 보았지만 링크 된 블로그에 언급 된 것처럼 동시 삽입이있는 경쟁 조건이 있습니다. 'merge()'함수의 포인트는 가능한 한 "항상 성공하고, 결국"성공하는 것이 었습니다. 필자는 SQL 문자열을 함수에 전달하는 것은 그리 우아하지 않지만, 필자는 필적 할만한 해결책을 찾지 못했다. 그리고 b) INSERT/UPDATE 쿼리가 자동 생성되어 효과적으로 API 뒤에 추악함을 숨긴다. – Zilk