나는 테이블에 두 개의 열을 가지고 col1
, col2
그들은 모두 고유 인덱스 (col1의 고유 그래서 COL2입니다)입니다.
이 테이블에 삽입하고 ON CONFLICT
구문을 사용 하고 다른 열을 업데이트해야하지만 conflict_target
절 에서 두 열을 모두 사용할 수는 없습니다 .
효과가있다:
INSERT INTO table
...
ON CONFLICT ( col1 )
DO UPDATE
SET
-- update needed columns here
그러나 다음과 같이 여러 열에 대해이를 수행하는 방법 :
...
ON CONFLICT ( col1, col2 )
DO UPDATE
SET
....
답변
샘플 테이블 및 데이터
CREATE TABLE dupes(col1 int primary key, col2 int, col3 text,
CONSTRAINT col2_unique UNIQUE (col2)
);
INSERT INTO dupes values(1,1,'a'),(2,2,'b');
문제 재현
INSERT INTO dupes values(3,2,'c')
ON CONFLICT (col1) DO UPDATE SET col3 = 'c', col2 = 2
이것을 Q1이라고합시다. 결과는
ERROR: duplicate key value violates unique constraint "col2_unique"
DETAIL: Key (col2)=(2) already exists.
무엇 설명서를 말한다
충돌 대상은 고유 인덱스 추론을 수행 할 수 있습니다. 추론을 수행 할 때 하나 이상의 index_column_name 열 및 / 또는 index_expression 식과 선택적 index_predicate로 구성됩니다. 순서에 관계없이 정확히 충돌 대상 지정 열 / 표현식을 포함하는 모든 table_name 고유 인덱스는 중재자 인덱스로 추론 (선택)됩니다. index_predicate가 지정되면 추론을위한 추가 요구 사항으로 중재자 인덱스를 충족해야합니다.
이것은 다음 쿼리가 작동해야한다는 인상을 주지만 실제로는 col1과 col2에 대한 고유 인덱스가 함께 필요하기 때문이 아닙니다. 그러나 이러한 인덱스는 col1 및 col2가 OP의 요구 사항 중 하나 인 개별적으로 고유하다는 것을 보장하지 않습니다.
INSERT INTO dupes values(3,2,'c')
ON CONFLICT (col1,col2) DO UPDATE SET col3 = 'c', col2 = 2
이 쿼리를 Q2라고 부르겠습니다 (이는 구문 오류로 실패 함).
왜?
Postgresql은 두 번째 열에서 충돌이 발생할 때 발생해야하는 일이 잘 정의되어 있지 않기 때문에 이러한 방식으로 작동합니다. 많은 가능성이 있습니다. 예를 들어 위의 Q1 쿼리에서 col1
충돌이있을 때 postgresql을 업데이트해야 col2
합니까? 그러나 그것이 또 다른 갈등으로 이어진다면 col1
? postgresql이 어떻게 처리 할 것으로 예상됩니까?
해결책
해결책은 ON CONFLICT와 구식 UPSERT 를 결합하는 것입니다 .
CREATE OR REPLACE FUNCTION merge_db(key1 INT, key2 INT, data TEXT) RETURNS VOID AS
$$
BEGIN
LOOP
-- first try to update the key
UPDATE dupes SET col3 = data WHERE col1 = key1 and col2 = key2;
IF found THEN
RETURN;
END IF;
-- not there, so try to insert the key
-- if someone else inserts the same key concurrently, or key2
-- already exists in col2,
-- we could get a unique-key failure
BEGIN
INSERT INTO dupes VALUES (key1, key2, data) ON CONFLICT (col1) DO UPDATE SET col3 = data;
RETURN;
EXCEPTION WHEN unique_violation THEN
BEGIN
INSERT INTO dupes VALUES (key1, key2, data) ON CONFLICT (col2) DO UPDATE SET col3 = data;
RETURN;
EXCEPTION WHEN unique_violation THEN
-- Do nothing, and loop to try the UPDATE again.
END;
END;
END LOOP;
END;
$$
LANGUAGE plpgsql;
이 저장된 함수의 논리를 수정하여 원하는 방식으로 열을 정확하게 업데이트해야합니다. 다음과 같이 호출하십시오.
SELECT merge_db(3,2,'c');
SELECT merge_db(1,2,'d');
답변
ON CONFLICT
충돌 감지를 수행하려면 고유 색인 *이 필요합니다. 따라서 두 열에 고유 인덱스를 생성하기 만하면됩니다.
t=# create table t (id integer, a text, b text);
CREATE TABLE
t=# create unique index idx_t_id_a on t (id, a);
CREATE INDEX
t=# insert into t values (1, 'a', 'foo');
INSERT 0 1
t=# insert into t values (1, 'a', 'bar') on conflict (id, a) do update set b = 'bar';
INSERT 0 1
t=# select * from t;
id | a | b
----+---+-----
1 | a | bar
* 고유 인덱스 외에도 제외 제약 조건 을 사용할 수도 있습니다 . 이것들은 고유 한 제약보다 좀 더 일반적입니다. 테이블에 id
및에 대한 열이 있고 valid_time
(및 valid_time
이 tsrange
) 중복을 허용하려고 id
하지만 겹치는 기간에 대해서는 허용 하지 않는다고 가정합니다. 고유 제한 조건은 도움이되지 않습니다,하지만 제외 제약 조건 당신은 말할 수있다 “그들의 경우 새로운 레코드를 제외 id
등호 오래된 id
또한 및 valid_time
중복 그것 valid_time
.”
답변
요즘에는 불가능합니다. ON CONFLICT
구문 의 마지막 버전은 절을 반복하는 것을 허용하지 않으며 CTE 를 사용하는 것도 가능하지 않습니다. 더 많은 충돌 대상을 추가하기 위해 ON CONFLICT에서 INSERT를 breack 할 수 없습니다.
답변
postgres 9.5를 사용하는 경우 EXCLUDED 공간을 사용할 수 있습니다.
PostgreSQL 9.5의 새로운 기능에서 가져온 예 :
INSERT INTO user_logins (username, logins)
VALUES ('Naomi',1),('James',1)
ON CONFLICT (username)
DO UPDATE SET logins = user_logins.logins + EXCLUDED.logins;
답변
- 제약 조건을 만듭니다 (예 : 외부 인덱스).
또는 / 그리고
- 기존 제약 조건 (psq의 \ d)을 살펴보십시오.
- INSERT 절에서 ON CONSTRAINT (constraint_name)을 사용하십시오.
답변
Vlad는 올바른 아이디어를 얻었습니다.
먼저 열에 테이블 고유 제약 조건을 만들어야합니다. col1, col2
그런 다음 그렇게하면 다음을 수행 할 수 있습니다.
INSERT INTO dupes values(3,2,'c')
ON CONFLICT ON CONSTRAINT dupes_pkey
DO UPDATE SET col3 = 'c', col2 = 2
답변
일종의 해키이지만 col1과 col2의 두 값을 새 열인 col3 (둘의 인덱스와 비슷 함)에 연결하여이 문제를 해결하고 그와 비교했습니다. 이것은 col1과 col2를 모두 일치시켜야하는 경우에만 작동합니다.
INSERT INTO table
...
ON CONFLICT ( col3 )
DO UPDATE
SET
-- update needed columns here
여기서 col3은 col1과 col2의 값 연결입니다.