2012-07-07 2 views
0

에 PostgreSQL을 "merge_db"(일명 upsert) 함수를 변환하는 방법, 여기에 canonical example of merge_db in PostgreSQL입니다 :스트레이트 설명서에서 MySQL을

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'); 

이는 MySQL은 사용자 정의 함수로 표현, 만약 그렇다면 수 있습니다, 방법? MySQL의 표준 인 INSERT...ON DUPLICATE KEY UPDATE에 비해 어떤 이점이 있습니까?

참고 : 특별히 사용자 정의 함수를 찾고 있는데 INSERT...ON DUPLICATE KEY UPDATE이 아닙니다.

답변

4

MySQL 5.5.14에서 테스트되었습니다.

CREATE TABLE db (a INT PRIMARY KEY, b TEXT); 

DELIMITER // 
CREATE PROCEDURE merge_db(k INT, data TEXT) 
BEGIN 
    DECLARE done BOOLEAN; 
    REPEAT 
     BEGIN 
      -- If there is a unique key constraint error then 
      -- someone made a concurrent insert. Reset the sentinel 
      -- and try again. 
      DECLARE ER_DUP_UNIQUE CONDITION FOR 23000; 
      DECLARE CONTINUE HANDLER FOR ER_DUP_UNIQUE BEGIN 
       SET done = FALSE; 
      END; 

      SET done = TRUE; 
      SELECT COUNT(*) INTO @count FROM db WHERE a = k; 
      -- Race condition here. If a concurrent INSERT is made after 
      -- the SELECT but before the INSERT below we'll get a duplicate 
      -- key error. But the handler above will take care of that. 
      IF @count > 0 THEN 
       UPDATE db SET b = data WHERE a = k; 
      ELSE 
       INSERT INTO db (a, b) VALUES (k, data); 
      END IF; 
     END; 
    UNTIL done END REPEAT; 
END// 

DELIMITER ; 

CALL merge_db(1, 'david'); 
CALL merge_db(1, 'dennis'); 

일부의 생각 : 먼저 업데이 트를하고 실제로 변경된 행의 수를 반환하기 때문에 다음 @ROW_COUNT()을 확인할 수 없습니다

  • . 행에 이미 업데이트하려는 값이 있으면이 값은 0이 될 수 있습니다.
  • 또한 @ROW_COUNT()은 복제가 안전하지 않습니다.
  • REPLACE...INTO을 사용할 수 있습니다.
  • InnoDB 또는 트랜잭션 지원이있는 테이블을 사용하는 경우 SELECT...FOR UPDATE (테스트되지 않음)을 사용할 수 있습니다.

INSERT...ON DUPLICATE KEY UPDATE을 사용하는 것보다이 솔루션에 이점이 없습니다.