postgresql을 사용하여“insert ignore”및“replication key update”(sql merge)를 에뮬레이트하는 방법은 무엇입니까?


140

일부 SQL Server에는 INSERT기본 / 고유 키 제약 조건을 위반할 경우 건너 뛰는 기능 이 있습니다. 예를 들어, MySQL은 INSERT IGNORE입니다.

PostgreSQL 을 에뮬레이트 INSERT IGNORE하고 ON DUPLICATE KEY UPDATE사용 하는 가장 좋은 방법은 무엇입니까 ?




6
9.5에서, 그것은 기본적으로 가능합니다 : stackoverflow.com/a/34639631/4418
warren

MySQL 에뮬레이션 : ON DUPLICATE KEY UPDATEPgSQL 9.5에서는 PgSQL에 ON CLAUSE상응하는 제약 조건 이름을 제공해야하지만 MySQL 은이 를 정의 할 필요없이 제약 조건을 캡처 할 수 있기 때문에 여전히 다소 불가능 합니다. 이렇게하면 쿼리를 다시 쓰지 않고이 기능을 "에뮬레이션"할 수 없습니다.
NeverEndingQueue

답변:


35

업데이트를 시도하십시오. 존재하지 않는 행을 수정하지 않으면 삽입을 수행하십시오. 분명히, 당신은 거래 안에서 이것을합니다.

클라이언트 측에 여분의 코드를 넣지 않으려는 경우 함수로 래핑 할 수 있습니다. 또한 그 생각에서 매우 드문 경쟁 조건에 대한 루프가 필요합니다.

설명서에 이에 대한 예가 있습니다. http://www.postgresql.org/docs/9.3/static/plpgsql-control-structures.html , 맨 아래 오른쪽에 예 40-2.

일반적으로 가장 쉬운 방법입니다. 규칙을 사용하여 마술을 할 수는 있지만 훨씬 더 혼란 스러울 것입니다. 나는 그 기능에 대한 랩핑 접근법을 언젠가 추천합니다.

이것은 단일 행 또는 소수의 행 값에 적용됩니다. 하위 쿼리와 같이 많은 양의 행을 처리하는 경우 INSERT 및 UPDATE에 대한 쿼리를 두 개의 쿼리로 나누는 것이 가장 좋습니다 (물론 적절한 조인 / 하위 선택-기본을 작성할 필요가 없음) 두 번 필터)


4
"대량의 행을 처리하는 경우"가 바로 제 경우입니다. 행을 대량 업데이트 / 삽입하고 싶습니다 .mysql을 사용하면 반복없이 하나의 쿼리 로이 작업을 수행 할 수 있습니다. 이제 postgresql에서도 이것이 가능한지 궁금합니다. 하나의 쿼리를 사용하여 OR 삽입을 대량 업데이트하십시오. "INSERT 용 쿼리와 UPDATE 용 쿼리를 두 개의 쿼리로 나누는 것이 가장 좋습니다."하지만 중복 키에 오류가 발생하지 않는 삽입을 어떻게 수행 할 수 있습니까? (즉, "INSERT IGNORE")
gpilotino

4
Magnus는 "트랜잭션 시작; select * from test where false로 임시 테이블 temporary_table 생성; 'data_file.csv'에서 temporary_table 복사; 테이블 테스트 잠금; 시험 세트 data = temporary_table.data from temporary_table에서 test.id = temporary_table.id; test select * from temporary_table에 삽입하십시오. 여기서 id는 (테스트에서 id를 선택하십시오) "
Tometzky

25
업데이트 : PostgreSQL 9.5에서는 이제 간단합니다 INSERT ... ON CONFLICT DO NOTHING;. answer stackoverflow.com/a/34639631/2091700 도 참조하십시오 .
Alphaaa

중요한 것은 SQL 표준 MERGE은 첫 번째 를 취하지 않는 한 동시성 안전 upsert 가 아닙니다LOCK TABLE . 사람들은 그런 식으로 사용하지만 잘못되었습니다.
Craig Ringer

1
v9.5에서는 이제 '기본'기능이므로 @Alphaaa의 의견을 확인하십시오 (답을 알리는 의견 만 광고하십시오)
Camilo Delvasto

178

PostgreSQL 9.5에서는 MySQL이 몇 년 동안 사용해 것처럼 기본 기능입니다 .

INSERT ... ON 충돌하지 않음 / 업데이트 ( "UPSERT")

9.5는 "UPSERT"작업을 지원합니다. INSERT는 ON CONFLICT DO UPDATE / IGNORE 절을 승인하도록 확장되었습니다. 이 조항은 복제 위반이 발생할 경우 취할 대체 조치를 지정합니다.

...

새로운 구문의 추가 예 :

INSERT INTO user_logins (username, logins)
VALUES ('Naomi',1),('James',1) 
ON CONFLICT (username)
DO UPDATE SET logins = user_logins.logins + EXCLUDED.logins;

100

편집 : 워렌의 대답 을 놓친 경우 PG9.5에 기본적으로 적용됩니다. 업그레이드 시간!


Bill Karwin의 답변을 바탕으로 규칙 기반 접근 방식이 어떻게 보이는지 설명합니다 (동일한 DB의 다른 스키마에서 전송하고 다중 열 기본 키 사용).

CREATE RULE "my_table_on_duplicate_ignore" AS ON INSERT TO "my_table"
  WHERE EXISTS(SELECT 1 FROM my_table 
                WHERE (pk_col_1, pk_col_2)=(NEW.pk_col_1, NEW.pk_col_2))
  DO INSTEAD NOTHING;
INSERT INTO my_table SELECT * FROM another_schema.my_table WHERE some_cond;
DROP RULE "my_table_on_duplicate_ignore" ON "my_table";

참고 : INSERT규칙은 규칙이 삭제 될 때까지 모든 작업에 적용 되므로 특별하지는 않습니다.


@sema ? another_schema.my_table의 제약 조건에 따라 중복 이 포함되어 있다면 의미 my_table합니까?
EoghanM

2
@EoghanM postgresql 9.3에서 규칙을 테스트했지만 INSERT INTO "my_table"(a, b), (a, b)와 같은 여러 행 삽입 문으로 중복을 삽입 할 수 있습니다. (그 행 (a, b)이 "my_table"에 아직 존재하지 않는다고 가정).
sema

@sema, gotcha-삽입 할 모든 데이터에 대해 시작시 규칙이 실행되고 각 행이 삽입 된 후 다시 실행되지 않아야 함을 의미해야합니다. 한 가지 방법은 데이터를 다른 임시 테이블에 먼저 삽입하여 제약이없는 다음 수행하는 것입니다.INSERT INTO "my_table" SELECT DISTINCT ON (pk_col_1, pk_col_2) * FROM the_tmp_table;
EoghanM

@EoghanM 또 다른 접근 방식은 중복 구속 조건을 일시적으로 완화하고 삽입시 중복을 허용하지만 이후에 중복을 제거하는 것입니다.DELETE FROM my_table WHERE ctid IN (SELECT ctid FROM (SELECT ctid,ROW_NUMBER() OVER (PARTITION BY pk_col_1,pk_col_2) AS rn FROM my_table) AS dups WHERE dups.rn > 1);
sema

@ sema에 설명 된 문제가 있습니다. 삽입 (a, b), (a, b)를 수행하면 오류가 발생합니다. 이 경우에도 오류를 억제하는 방법이 있습니까?
Diogo Melo

35

Postgres 9.5 이상을 사용하는 사용자에게는 새로운 ON CONFLICT DO NOTHING 구문이 작동해야합니다.

INSERT INTO target_table (field_one, field_two, field_three ) 
SELECT field_one, field_two, field_three
FROM source_table
ON CONFLICT (field_one) DO NOTHING;

이전 버전을 사용하는 사람들에게는이 올바른 조인이 대신 작동합니다.

INSERT INTO target_table (field_one, field_two, field_three )
SELECT source_table.field_one, source_table.field_two, source_table.field_three
FROM source_table 
LEFT JOIN target_table ON source_table.field_one = target_table.field_one
WHERE target_table.field_one IS NULL;

동시 환경에서 큰 삽입 작업을 수행 할 때는 두 번째 방법이 작동하지 않습니다. 당신은을받을 Unique violation: 7 ERROR: duplicate key value violates unique constrainttarget_table다른 행이 삽입 한 동안 이 쿼리가 실행되고 있던 자신의 키, 참으로, 서로 중복되는 경우. 잠금 target_table이 도움이 될 것이라 생각 하지만 동시성은 분명히 고통받을 것입니다.
G. Kashtanov

1
ON CONFLICT (field_one) DO NOTHING답의 가장 중요한 부분입니다.
Abel Callejo

24

인서트 무시 로직 을 얻으려면 다음과 같이 할 수 있습니다. 리터럴 값의 select 문에서 삽입하는 것이 가장 효과적이라는 것을 알았으므로 NOT EXISTS 절을 사용하여 중복 키를 마스킹 할 수 있습니다. 중복 논리에 대한 업데이트를 얻으려면 pl / pgsql 루프가 필요할 것으로 생각됩니다.

INSERT INTO manager.vin_manufacturer
(SELECT * FROM( VALUES
  ('935',' Citroën Brazil','Citroën'),
  ('ABC', 'Toyota', 'Toyota'),
  ('ZOM',' OM','OM')
  ) as tmp (vin_manufacturer_id, manufacturer_desc, make_desc)
  WHERE NOT EXISTS (
    --ignore anything that has already been inserted
    SELECT 1 FROM manager.vin_manufacturer m where m.vin_manufacturer_id = tmp.vin_manufacturer_id)
)

tmp에 중복 행이 포함되어 있으면 어떻게됩니까?
Henley Chiu

항상 별개의 키워드로 선택할 수 있습니다.
Keyo

5
참고로, 다른 거래는 다른 거래에서 새로 추가 된 데이터를 볼 수 없기 때문에 "WHERE NOT EXISTS"트릭은 여러 거래에서 작동하지 않습니다.
Dave Johansen 2016 년

21
INSERT INTO mytable(col1,col2) 
    SELECT 'val1','val2' 
    WHERE NOT EXISTS (SELECT 1 FROM mytable WHERE col1='val1')

여러 거래가 모두 같은 일을하려고하는 영향은 무엇입니까? 존재하지 않는 곳과 다른 트랜잭션을 실행하는 삽입 사이에 행을 삽입 할 수 있습니까? 그리고 Postgres가이를 막을 수 있다면 postgres가 모든 트랜잭션에서 동기화 지점을 도입하지 않습니까?
Καrτhικ

새로 추가 된 데이터는 다른 트랜잭션에 표시되지 않으므로 여러 트랜잭션에서 작동하지 않습니다.
Dave Johansen 2016 년

12

PostgreSQL은 rule 이라는 스키마 객체를 지원하는 것 같습니다 .

http://www.postgresql.org/docs/current/static/rules-update.html

ON INSERT주어진 테이블에 대한 규칙 을 만들어 NOTHING주어진 기본 키 값 으로 행이 존재 UPDATE하는 INSERT경우 행을 만들거나 주어진 기본 키 값 으로 행이 존재 하는 경우 행을 대신 할 수 있습니다.

나는 이것을 직접 시도하지 않았으므로 경험에서 말하거나 예를 제시 할 수 없습니다.


1
내가 잘 이해하면이 규칙은 명령문이 호출 될 때마다 실행되는 트리거입니다. 하나의 쿼리에만 규칙을 적용하려면 어떻게해야합니까? 규칙을 작성하고 즉시 삭제해야합니까? (경쟁 조건은 어떻습니까?)
gpilotino 2018 년

3
예, 같은 질문이 있습니다. 규칙 메커니즘은 PostgreSQL에서 MySQL의 INSERT IGNORE 또는 ON DUPLICATE KEY UPDATE에 가장 가까운 것입니다. "중복 키 업데이트시 postgresql"에 대해 Google에서 규칙을 임시로뿐만 아니라 INSERT에도 적용하더라도 규칙 메커니즘을 권장하는 다른 사람들을 찾을 수 있습니다.
Bill Karwin

4
PostgreSQL은 트랜잭션 DDL을 지원합니다. 즉, 규칙을 생성하고 단일 트랜잭션 내에 규칙을 삭제하면 규칙이 해당 트랜잭션 외부에 표시되지 않으므로 외부에서 아무런 영향을 미치지 않습니다.
cdhowie 2016 년

6

@hanmari가 그의 의견에서 언급했듯이. postgres 테이블에 삽입 할 때 on 충돌 (..)은 중복 데이터를 삽입하지 않는 데 사용할 수있는 최상의 코드는 없습니다. :

query = "INSERT INTO db_table_name(column_name)
         VALUES(%s) ON CONFLICT (column_name) DO NOTHING;"

ON CONFLICT 코드 행을 사용하면 insert 문에서 여전히 데이터 행을 삽입 할 수 있습니다. 쿼리 및 값 코드는 Excel에서 postgres db 테이블로 삽입 된 날짜의 예입니다. ID 필드가 고유한지 확인하는 데 사용하는 postgres 테이블에 제약 조건이 추가되었습니다. 동일한 데이터 행에서 삭제를 실행하는 대신 1에서 시작하는 ID 열의 번호를 다시 매기는 SQL 코드 행을 추가합니다. 예 :

q = 'ALTER id_column serial RESTART WITH 1'

내 데이터에 ID 필드가 있으면 이것을 기본 ID / 직렬 ID로 사용하지 않고 ID 열을 만들고 일련 번호로 설정합니다. 이 정보가 모든 사람에게 도움이되기를 바랍니다. * 소프트웨어 개발 / 코딩에 대학 학위가 없습니다. 코딩에서 아는 모든 것, 나는 스스로 공부합니다.


복합 고유 인덱스에서는 작동하지 않습니다!
Nulik

4

이 솔루션은 규칙 사용을 피합니다.

BEGIN
   INSERT INTO tableA (unique_column,c2,c3) VALUES (1,2,3);
EXCEPTION 
   WHEN unique_violation THEN
     UPDATE tableA SET c2 = 2, c3 = 3 WHERE unique_column = 1;
END;

그러나 성능 단점이 있습니다 ( PostgreSQL.org 참조 ).

EXCEPTION 절을 포함하는 블록은 블록이없는 블록보다 들어오고 나가는 데 훨씬 더 비쌉니다. 따라서 필요없이 EXCEPTION을 사용하지 마십시오.


1

대량으로 삽입하기 전에 항상 행을 삭제할 수 있습니다. 존재하지 않는 행을 삭제해도 오류가 발생하지 않으므로 안전하게 건너 뜁니다.


2
이 방법은 내가 ... 그것을 권하고 싶지 않다, 이상한 경쟁 조건에 매우 경향이있을 것이다
스티븐 Schlansker

1
+1 쉽고 일반적입니다. 주의해서 사용하면 실제로 간단한 해결책이 될 수 있습니다.
Wouter van Nifterick

1
기존 데이터가 삽입 후 변경되었지만 중복 키가 아닌 경우에도 작동하지 않으며 업데이트를 유지하려고합니다. 프로덕션, QA, 개발 및 테스트 시스템에서 실행되는 db 업데이트와 같이 약간 다른 시스템에 대해 작성된 SQL 스크립트가있는 시나리오입니다.
한노 피 에츠

1
DEFERRABLE INITIALLY DEFERRED플래그 를 사용하여 외래 키를 만들면 문제가 될 수 없습니다 .
temoto

-1

데이터 가져 오기 스크립트의 경우 "IF NOT EXISTS"를 바꾸려면 약간 어색한 공식이 있습니다.

DO
$do$
BEGIN
PERFORM id
FROM whatever_table;

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