테이블의 행을 업데이트하거나 존재하지 않는 경우 INSERT하는 방법은 무엇입니까?


83

다음과 같은 카운터 테이블이 있습니다.

CREATE TABLE cache (
    key text PRIMARY KEY,
    generation int
);

카운터 중 하나를 늘리거나 해당 행이 아직 존재하지 않는 경우 0으로 설정하고 싶습니다. 표준 SQL에서 동시성 문제없이이 작업을 수행 할 수있는 방법이 있습니까? 작업은 때로는 트랜잭션의 일부이며 때로는 분리되어 있습니다.

SQL은 가능한 경우 SQLite, PostgreSQL 및 MySQL에서 수정되지 않은 상태로 실행되어야합니다.

검색 결과 동시성 문제가 있거나 데이터베이스에 특정한 몇 가지 아이디어가 나왔습니다.

  • INSERT새 행을 시도 UPDATE하고 오류가있는 경우. 불행히도의 오류 INSERT는 현재 트랜잭션 을 중단합니다.

  • UPDATE행, 수정 된 행이없는 경우 INSERT새 행.

  • MySQL에는 ON DUPLICATE KEY UPDATE절이 있습니다.

편집 : 모든 훌륭한 답변에 감사드립니다. Paul이 옳은 것처럼 보이며이 작업을 수행 할 수있는 단 하나의 휴대용 방법은 없습니다. 그것은 매우 기본적인 작업처럼 들리기 때문에 저에게 매우 놀랍습니다.


6
이러한 모든 RDBMS에서 작동하는 단일 솔루션을 찾을 수는 없습니다. 죄송합니다.
Paul Tomblin


답변:


137

MySQL (및 이후 SQLite)은 REPLACE INTO 구문도 지원합니다.

REPLACE INTO my_table (pk_id, col1) VALUES (5, '123');

이렇게하면 자동으로 기본 키를 식별하고 업데이트 할 일치하는 행을 찾고, 발견되지 않으면 새 행을 삽입합니다.


13
실제로 MySQL의 REPLACE는 항상 삽입을 수행하지만 이미 존재하는 경우 먼저 행을 삭제합니다. dev.mysql.com/doc/refman/4.1/en/replace.html
에반

90
삽입 + 삭제이며 업데이트하지 않음을 이해하는 것이 중요합니다. 그 결과, 바꾸기를 수행 할 때 항상 모든 필드에 대한 데이터를 포함해야한다는 것을 항상 확인해야합니다.
Zoredache

2
@Zoredache 기술적으로는 '삭제 후 삽입'입니다.
Agi Hammerthief 2014 년

2
@Agihammerthief는 새로 삽입 된 행이 삭제 된 행과 동일한 기본 키를 가지지 않는다는 매우 실제적인 차이점이 있습니다. ON DUPLICATE를 사용하면 특별히 변경하지 않는 한 동일한 기본 키가됩니다.
Tim Strijdhorst

32

SQLite는 이미 존재하는 경우 행 교체를 지원 합니다.

INSERT OR REPLACE INTO [...blah...]

이것을 줄여서

REPLACE INTO [...blah...]

이 단축키는 MySQL REPLACE INTO표현식 과 호환되도록 추가되었습니다 .


PRAMARY KEY값에 a를 정의 해야합니다.
DawnSong

24

다음과 같은 작업을 수행합니다.

INSERT INTO cache VALUES (key, generation)
ON DUPLICATE KEY UPDATE (key = key, generation = generation + 1);

코드 또는 SQL에서 생성 값을 0으로 설정하지만 ON DUP ...를 사용하여 값을 증가시킵니다. 어쨌든 구문이라고 생각합니다.


1
이 답변은 정직하게 선택된 답변이어야합니다. 항목에 모든 필드를 제출하지 않는 경우 비파괴 편집입니다.
Jordan

9

ON DUPLICATE KEY UPDATE 절은 다음과 같은 이유로 최상의 솔루션입니다. REPLACE는 DELETE 다음에 INSERT를 수행하므로 아주 약간의 기간 동안 레코드가 제거되어 쿼리가 다시 올 수있을 가능성이 매우 적습니다. REPLACE 쿼리 중에 표시됩니다.

나는 INSERT ... ON DUPLICATE UPDATE ...를 선호합니다.

jmoz의 솔루션이 최고입니다. 괄호 대신 SET 구문을 선호하지만

INSERT INTO cache 
SET key = 'key', generation = 'generation'
ON DUPLICATE KEY 
UPDATE key = 'key', generation = (generation + 1)
;

4
REPLACE는 원자 적이므로 행이 존재하지 않는 기간이 없습니다.
Brilliand


5

PostgreSQL에는 병합 명령이 없으며 실제로 작성하는 것은 사소한 일이 아닙니다. 실제로 작업을 "흥미롭게"만드는 이상한 경우가 있습니다.

가장 좋은 (예 : 가능한 가장 많은 조건에서 작업) 접근 방식은 수동 (merge_db)에 표시된 것과 같은 기능을 사용하는 것 입니다.

기능을 사용하고 싶지 않다면 일반적 으로 다음과 같이 벗어날 수 있습니다.

updated = db.execute(UPDATE ... RETURNING 1)
if (!updated)
  db.execute(INSERT...)

결함 증명이 아니며 결국 실패 할 것임을 기억하십시오 .


4

표준 SQL은이 작업에 대한 MERGE 문을 제공합니다. 모든 DBMS가 MERGE 문을 지원하는 것은 아닙니다.


0

원자 적으로 업데이트하거나 삽입하는 일반적인 방법이없는 경우 (예 : 트랜잭션을 통해) 다른 잠금 체계로 폴백 할 수 있습니다. 0 바이트 파일, 시스템 뮤텍스, 명명 된 파이프 등 ...


0

삽입 트리거를 사용할 수 있습니까? 실패하면 업데이트를 수행하십시오.


명령이 작동 할 때 트리거 (적어도 PostgreSQL에서)가 실행 중입니다. 즉, 기본 명령이 실패 할 때 실행되는 트리거를 가질 수 없습니다.

0

SQL을 작성하는 라이브러리를 사용해도 괜찮다면 Upsert 를 사용할 수 있습니다 (현재 Ruby 및 Python 만 해당).

Pet.upsert({:name => 'Jerry'}, :breed => 'beagle')
Pet.upsert({:name => 'Jerry'}, :color => 'brown')

이는 MySQL, Postgres 및 SQLite3에서 작동합니다.

MySQL 및 Postgres에서 저장 프로 시저 또는 UDF (사용자 정의 함수)를 작성합니다. INSERT OR REPLACESQLite3에서 사용합니다 .

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.