2013-09-22 1 views
4

기존 데이터에 따라 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 쿼리와 관련하여 중대한 사항을 놓치지 않았다면 확실하지 않습니다. 나는 중요한 것을 간과 했는가? 나는이 기능을 위해 상담 자원 중

은 다음과 같습니다

(편집 : 목표 중 하나가 목표 테이블을 잠금 방지하는 것이 었습니다.)

답변

1

귀하의 질문에 대한 답변 stion은 응용 프로그램이 데이터베이스에 액세스하는 방식의 컨텍스트에 따라 다릅니다. depesz's post you cited by yourself에서 잘 설명 된대로이를 해결할 수있는 여러 가지 방법이 있습니다. 또한 쓰기 가능한 CTE 사용을 고려할 수도 있습니다 (here 참조). 또한 [질문] Insert, on duplicate update in PostgreSQL?에는 의사 결정 프로세스에 대한 흥미로운 토론이 있습니다.

+0

필자는 쓰기 가능한 CTE를 고려해 보았지만 링크 된 블로그에 언급 된 것처럼 동시 삽입이있는 경쟁 조건이 있습니다. 'merge()'함수의 포인트는 가능한 한 "항상 성공하고, 결국"성공하는 것이 었습니다. 필자는 SQL 문자열을 함수에 전달하는 것은 그리 우아하지 않지만, 필자는 필적 할만한 해결책을 찾지 못했다. 그리고 b) INSERT/UPDATE 쿼리가 자동 생성되어 효과적으로 API 뒤에 추악함을 숨긴다. – Zilk