MERGE INTO 문

MERGE INTO 문 (MERGE INTO Statement)

MERGE INTO 문은 INSERT INTO ... ON CONFLICT의 대안으로, 기본 키가 필요 없고 커스텀 매치 조건을 허용해요. 대상 테이블에 primary key 제약이 없는 경우 upsert(INSERT + UPDATE)에 매우 유용한 대안이에요.

출처: 문서

본문

예시 (Examples)

먼저 간단한 테이블을 만들어 볼게요.

CREATE TABLE people (id INTEGER, name VARCHAR, salary FLOAT);
INSERT INTO people VALUES (1, 'John', 92_000.0), (2, 'Anna', 100_000.0);

가장 간단한 upsert는 USING 절에 전체 행을 사용하는 거예요. 이렇게 하면 매치가 있으면 더 이상 지시 없이 행을 새 행으로 업데이트할 수 있고(WHEN MATCHED THEN UPDATE), 매치가 없으면 행을 테이블에 간단히 삽입할 수 있어요(WHEN NOT MATCHED THEN INSERT).

MERGE INTO people
    USING (
        SELECT
            unnest([3, 1]) AS id,
            unnest(['Sarah', 'John']) AS name,
            unnest([95_000.0, 105_000.0]) AS salary
    ) AS upserts
    ON (upserts.id = people.id)
    WHEN MATCHED THEN UPDATE
    WHEN NOT MATCHED THEN INSERT;

FROM people
ORDER BY id;
id name salary
1 John 105000.0
2 Anna 100000.0
3 Sarah 95000.0

이전 예시에서는 id가 일치하면 전체 행을 업데이트했어요. 하지만 몇몇 키와 변경된 값이 있는 변경 집합(change set) 을 받는 것도 일반적인 패턴이에요. 여기에 SET이 좋은 용도예요. 매치 조건이 소스와 대상에서 같은 이름을 가진 컬럼을 사용한다면 매치 조건에서 USING 키워드를 사용할 수 있어요.

MERGE INTO people
    USING (
        SELECT
            1 AS id, 
            98_000.0 AS salary
    ) AS salary_updates
    USING (id)
    WHEN MATCHED THEN UPDATE SET salary = salary_updates.salary;

FROM people
ORDER BY id;
id name salary
1 John 98000.0
2 Anna 100000.0
3 Sarah 95000.0

또 다른 일반적인 패턴은 삭제할 행의 id만 포함할 수 있는 삭제 집합(delete set) 을 받는 거예요.

MERGE INTO people
    USING (
        SELECT
            1 AS id, 
    ) AS deletes
    USING (id)
    WHEN MATCHED THEN DELETE;

FROM people
ORDER BY id;
id name salary
2 Anna 100000.0
3 Sarah 95000.0

MERGE INTO는 더 복잡한 조건도 지원해요. 예를 들어 주어진 삭제 집합 에 대해 특정 금액 이상의 salary를 가진 행만 제거하도록 결정할 수 있어요.

MERGE INTO people
    USING (
        SELECT
            unnest([3, 2]) AS id, 
    ) AS deletes
    USING (id)
    WHEN MATCHED AND people.salary >= 100_000.0 THEN DELETE;

FROM people
ORDER BY id;
id name salary
3 Sarah 95000.0

필요하다면 DuckDB는 여러 UPDATEDELETE 조건도 지원해요. RETURNING 절은 MERGE 문이 어떤 행에 영향을 주었는지 나타내는 데 사용할 수 있어요.

-- Let's get John back in!
INSERT INTO people VALUES (1, 'John', 105_000.0);

MERGE INTO people
    USING (
        SELECT
            unnest([3, 1]) AS id,
            unnest([89_000.0, 70_000.0]) AS salary
    ) AS upserts
    USING (id)
    WHEN MATCHED AND people.salary < 100_000.0 THEN UPDATE SET salary = upserts.salary
    -- Second update or delete condition
    WHEN MATCHED AND people.salary > 100_000.0 THEN DELETE
    WHEN NOT MATCHED THEN INSERT BY NAME
    RETURNING merge_action, *;
merge_action id name salary
UPDATE 3 Sarah 89000.0
DELETE 1 John 105000.0

경우에 따라 소스가 조건을 충족하지 않을 때 특별히 다른 동작을 수행하고 싶을 수 있어요. 예를 들어 소스에 없는 데이터가 대상에 없어야 한다고 기대한다면:

CREATE TABLE target AS
    SELECT unnest([1,2]) AS id;

MERGE INTO target
    USING (SELECT 1 AS id) source
    USING (id)
    WHEN MATCHED THEN UPDATE
    WHEN NOT MATCHED BY SOURCE THEN DELETE
    RETURNING merge_action, *;
merge_action id
UPDATE 1
DELETE 2

WHEN NOT MATCHED BY TARGET을 지정할 수도 있어요. 하지만 그 동작은 예상대로 WHEN NOT MATCHED와 동일해요. 조건을 지정할 때 기본적으로 대상을 보기 때문이에요.

문법 (Syntax)

더 알아보기 (Learn more)