MERGE

한 테이블에 있는 값을 두 번째의 다른 테이블이나 하위 쿼리에 있는 값을 기준으로 삽입, 업데이트, 삭제합니다. 두 번째 테이블이 대상 테이블의 새 행(삽입 예정), 수정된 행(업데이트 예정) 또는 표시된 행(삭제 예정)이 포함된 변경 로그인 경우 병합이 유용할 수 있습니다.

이 명령은 다음과 같은 사례를 처리하기 위한 의미 체계를 지원합니다.

  • 일치하는 값(업데이트 및 삭제의 경우).

  • 일치하지 않는 값(삽입의 경우).

참고 항목:

DELETE , UPDATE

구문

MERGE INTO <target_table>
  USING <source>
  ON <join_expr>
  { matchedClause | notMatchedClause } [ ... ]
Copy

여기서:

matchedClause ::=
  WHEN MATCHED
    [ AND <case_predicate> ]
    THEN { UPDATE { ALL BY NAME | SET <col_name> = <expr> [ , <col_name> = <expr> ... ] } | DELETE } [ ... ]
Copy
notMatchedClause ::=
   WHEN NOT MATCHED
     [ AND <case_predicate> ]
     THEN INSERT { ALL BY NAME | [ ( <col_name> [ , ... ] ) ] VALUES ( <expr> [ , ... ] ) }
Copy

매개 변수

target_table

병합할 테이블을 지정합니다.

source

대상 테이블과 조인할 테이블 또는 하위 쿼리를 지정합니다.

join_expr

대상 테이블과 원본을 조인할 식을 지정합니다.

matchedClause (업데이트 또는 삭제의 경우)

WHEN MATCHED ... AND case_predicate

참일 때 일치하는 케이스가 실행되도록 하는 식을 선택적으로 지정합니다.

기본값: 값 없음(일치하는 케이스가 항상 실행됨)

WHEN MATCHED ... THEN { UPDATE { ALL BY NAME | SET ... } | DELETE }

값이 일치할 때 수행할 작업을 지정합니다.

ALL BY NAME

대상 테이블의 모든 열을 소스의 값으로 업데이트합니다. 대상 테이블의 각 열은 소스에서 같은 이름을 가진 열의 값으로 업데이트됩니다.

대상 테이블과 소스에 동일한 수의 열과 모든 열에 대해 동일한 이름이 있어야 합니다. 그러나 열 순서는 대상 테이블과 소스 간에 다를 수 있습니다.

SET col_name = expr [ , col_name = expr ... ]

새 열 값에 해당하는 표현식을 사용하여 대상 테이블에서 지정된 열을 업데이트합니다(대상 및 소스 관계를 모두 참조할 수 있음).

단일 SET 하위 절에서 업데이트할 여러 개의 열을 지정할 수 있습니다.

DELETE

소스와 일치하는 경우 대상 테이블의 행을 삭제합니다.

notMatchedClause (삽입의 경우)

WHEN NOT MATCHED ... AND case_predicate

참일 때 일치하지 않는 케이스가 실행되도록 하는 식을 선택적으로 지정합니다.

기본값: 값 없음(일치하지 않는 케이스가 항상 실행됨)

WHEN NOT MATCHED ... THEN INSERT . { ALL BY NAME | [ ( col_name [ , ... ] ) ] VALUES ( expr [ , ... ] ) }

값이 일치하지 않을 때 수행할 작업을 지정합니다.

ALL BY NAME

소스의 값과 함께 대상 테이블의 모든 열을 삽입합니다. 대상 테이블의 각 열에는 소스에서 같은 이름을 가진 열의 값이 삽입됩니다.

대상 테이블과 소스에 동일한 수의 열과 모든 열에 대해 동일한 이름이 있어야 합니다. 그러나 열 순서는 대상 테이블과 소스 간에 다를 수 있습니다.

( col_name [ , ... ] )

소스의 값과 함께 삽입할 대상 테이블의 하나 이상의 열을 선택적으로 지정합니다.

기본값: 값 없음(대상 테이블의 모든 열이 삽입됨)

VALUES ( expr [ , ... ] )

삽입된 열 값에 대응되는 식을 지정합니다(원본 관계를 참조해야 함).

사용법 노트

  • 단일 MERGE 문은 일치하는 절과 일치하지 않는 절(즉, WHEN MATCHED ...WHEN NOT MATCHED ...)을 여러 개 포함할 수 있습니다.

  • AND 하위 절을 생략(기본 동작)하는 일치하거나 일치하지 않는 절은 문의 해당 절 유형 중에서 마지막`에 와야 합니다(예: :code:`WHEN MATCHED … 절 다음에 WHEN MATCHED AND ... 절은 올 수 없음). 그렇게 하면 연결할 수 없는 경우가 생겨 오류가 반환됩니다.

중복 조인 동작

소스 테이블의 여러 행이 대상 테이블의 단일 행과 일치하는 경우 결과는 결정적이거나 비결정적일 수 있습니다. 이 섹션에서는 이러한 사용 사례에 대한 MERGE 동작을 설명합니다.

UPDATE와 DELETE에 대한 비결정적 결과

병합이 소스 테이블의 여러 행에 대해 대상 테이블의 행을 조인하면, 다음과 같은 조인 조건이 비결정적 결과를 생성합니다(즉, 시스템이 대상 행을 업데이트하거나 삭제하는 데 사용할 소스 값을 결정할 수 없음).

  • 다중 값으로 업데이트될 대상 행이 선택됩니다(예: WHEN MATCHED ... THEN UPDATE).

  • 업데이트될 대상 행과 삭제될 대상 행이 선택됩니다(예: WHEN MATCHED ... THEN UPDATE, WHEN MATCHED ... THEN DELETE).

이 상황에서, 병합 결과는 ERROR_ON_NONDETERMINISTIC_MERGE 세션 매개 변수에 대해 지정된 값에 따라 다릅니다.

  • TRUE(기본값)인 경우, 병합이 오류를 반환합니다.

  • FALSE인 경우 중복 항목 중 한 행을 선택하여 업데이트 또는 삭제를 수행하며, 선택한 행은 정의되지 않습니다.

UPDATE와 DELETE에 대한 결정적 결과

결정적 병합은 항상 오류 없이 완료됩니다. 각 대상 행에 대해 다음 조건 중 *하나 이상*을 충족하는 경우 병합이 결정적입니다.

  • 하나 이상의 원본 행이 WHEN MATCHED ... THEN DELETE 절을 충족하고 다른 어떤 원본 행도 WHEN MATCHED 절을 충족하지 않습니다.

  • 정확히 한 개의 원본 행이 WHEN MATCHED ... THEN UPDATE 절을 충족하고 다른 어떤 원본 행도 WHEN MATCHED 절을 충족하지 않습니다.

이에 따라 MERGE가 의미상으로 UPDATEDELETE 명령과 같아지게 됩니다.

참고

데이터 소스의 여러 행(즉, 소스 테이블 또는 하위 쿼리)이 ON 조건에 따라 대상 테이블과 일치할 때 오류를 방지하려면 소스 절에서 :doc:`/sql-reference/constructs/group-by`를 사용해 각 대상 행이 소스에서 (많아야) 한 개의 행에 대해 조인하도록 하세요.

다음 예에서는 src``가 같은 ``k 값을 가진 여러 행을 포함하는 것으로 가정합니다. 대상 행에서 k 값이 같은 행을 업데이트하는 데 사용되는 값(v)은 명확하지 않습니다. 이 쿼리는 MAX 함수 및 GROUP BY를 사용하여 src 중에서 사용되는 v 값을 명확히 합니다.

MERGE INTO target
  USING (SELECT k, MAX(v) AS v FROM src GROUP BY k) AS b
  ON target.k = b.k
  WHEN MATCHED THEN UPDATE SET target.v = b.v
  WHEN NOT MATCHED THEN INSERT (k, v) VALUES (b.k, b.v);
Copy

INSERT에 대한 결정적 결과

결정적 병합은 항상 오류 없이 완료됩니다.

MERGE 문이 WHEN NOT MATCHED ... THEN INSERT 절을 포함하고 대상에 일치하는 행이 없으며 소스가 중복 값을 포함하는 경우에는 대상이 소스의 사본에 대해 하나의 행 사본을 가져옵니다. 예에 대해서는 소스 복제본으로 병합 수행 섹션을 참조하세요.

다음 예에서는 MERGE 명령을 사용합니다.

값을 업데이트하는 기본 병합 수행

다음 예에서는 소스 테이블의 값을 사용하여 대상 테이블의 값을 업데이트하는 기본 병합을 수행합니다. 두 테이블을 만들고 로드합니다.

CREATE OR REPLACE TABLE merge_example_target (id INTEGER, description VARCHAR);

INSERT INTO merge_example_target (id, description) VALUES
  (10, 'To be updated (this is the old value)');

CREATE OR REPLACE TABLE merge_example_source (id INTEGER, description VARCHAR);

INSERT INTO merge_example_source (id, description) VALUES
  (10, 'To be updated (this is the new value)');
Copy

테이블에 값을 표시합니다.

SELECT * FROM merge_example_target;
Copy
+----+---------------------------------------+
| ID | DESCRIPTION                           |
|----+---------------------------------------|
| 10 | To be updated (this is the old value) |
+----+---------------------------------------+
SELECT * FROM merge_example_source;
Copy
+----+---------------------------------------+
| ID | DESCRIPTION                           |
|----+---------------------------------------|
| 10 | To be updated (this is the new value) |
+----+---------------------------------------+

MERGE 문을 실행합니다.

MERGE INTO merge_example_target
  USING merge_example_source
  ON merge_example_target.id = merge_example_source.id
  WHEN MATCHED THEN
    UPDATE SET merge_example_target.description = merge_example_source.description;
Copy
+------------------------+
| number of rows updated |
|------------------------|
|                      1 |
+------------------------+

대상 테이블에 새 값을 표시합니다(소스 테이블은 변경되지 않음).

SELECT * FROM merge_example_target;
Copy
+----+---------------------------------------+
| ID | DESCRIPTION                           |
|----+---------------------------------------|
| 10 | To be updated (this is the new value) |
+----+---------------------------------------+
SELECT * FROM merge_example_source;
Copy
+----+---------------------------------------+
| ID | DESCRIPTION                           |
|----+---------------------------------------|
| 10 | To be updated (this is the new value) |
+----+---------------------------------------+

여러 작업으로 기본 병합 수행

여러 작업(INSERT, UPDATE및 DELETE)을 혼합하여 기본 병합을 수행합니다.

두 테이블을 만들고 로드합니다.

CREATE OR REPLACE TABLE merge_example_mult_target (
  id INTEGER,
  val INTEGER,
  status VARCHAR);

INSERT INTO merge_example_mult_target (id, val, status) VALUES
  (1, 10, 'Production'),
  (2, 20, 'Alpha'),
  (3, 30, 'Production');

CREATE OR REPLACE TABLE merge_example_mult_source (
  id INTEGER,
  marked VARCHAR,
  isnewstatus INTEGER,
  newval INTEGER,
  newstatus VARCHAR);

INSERT INTO merge_example_mult_source (id, marked, isnewstatus, newval, newstatus) VALUES
  (1, 'Y', 0, 10, 'Production'),
  (2, 'N', 1, 50, 'Beta'),
  (3, 'N', 0, 60, 'Deprecated'),
  (4, 'N', 0, 40, 'Production');
Copy

테이블에 값을 표시합니다.

SELECT * FROM merge_example_mult_target;
Copy
+----+-----+------------+
| ID | VAL | STATUS     |
|----+-----+------------|
|  1 |  10 | Production |
|  2 |  20 | Alpha      |
|  3 |  30 | Production |
+----+-----+------------+
SELECT * FROM merge_example_mult_source;
Copy
+----+--------+-------------+--------+------------+
| ID | MARKED | ISNEWSTATUS | NEWVAL | NEWSTATUS  |
|----+--------+-------------+--------+------------|
|  1 | Y      |           0 |     10 | Production |
|  2 | N      |           1 |     50 | Beta       |
|  3 | N      |           0 |     60 | Deprecated |
|  4 | N      |           0 |     40 | Production |
+----+--------+-------------+--------+------------+

다음 병합 예는 merge_example_mult_target 테이블에 대해 다음 작업을 수행합니다.

  • id``가 동일한 행의 ``marked 열은 ``merge_example_mult_source``에서 ``Y``이므로 ``id``가 ``1``로 설정된 행을 삭제합니다.

  • merge_example_mult_source``에서 동일한 행에 대해 ``isnewstatus``가 ``1``로 설정되어 있으므로 ``id``가 ``2``로 설정된 행의 ``valstatus 값을 ``merge_example_mult_source``에서 ``id``가 동일한 행의 값으로 업데이트합니다.

  • id``가 ``3``으로 설정된 행의 ``val 값을 merge_example_mult_source``의 ``id``가 동일한 행의 값으로 업데이트합니다. ``merge_example_mult_source``에서 ``isnewstatus``가 ``0``으로 설정되어 있으므로 MERGE 문은 ``merge_example_mult_target``의 ``status 값을 업데이트하지 않습니다.

  • 행이 ``merge_example_mult_source``에 존재하고 ``merge_example_mult_target``에 일치하는 행이 없으므로 ``id``가 ``4``로 설정된 행을 삽입합니다.

MERGE INTO merge_example_mult_target
  USING merge_example_mult_source
  ON merge_example_mult_target.id = merge_example_mult_source.id
  WHEN MATCHED AND merge_example_mult_source.marked = 'Y'
    THEN DELETE
  WHEN MATCHED AND merge_example_mult_source.isnewstatus = 1
    THEN UPDATE SET val = merge_example_mult_source.newval, status = merge_example_mult_source.newstatus
  WHEN MATCHED
    THEN UPDATE SET val = merge_example_mult_source.newval
  WHEN NOT MATCHED
    THEN INSERT (id, val, status) VALUES (
      merge_example_mult_source.id,
      merge_example_mult_source.newval,
      merge_example_mult_source.newstatus);
Copy
+-------------------------+------------------------+------------------------+
| number of rows inserted | number of rows updated | number of rows deleted |
|-------------------------+------------------------+------------------------|
|                       1 |                      2 |                      1 |
+-------------------------+------------------------+------------------------+

병합 결과를 보려면 merge_example_mult_target 테이블의 값을 표시합니다.

SELECT * FROM merge_example_mult_target ORDER BY id;
Copy
+----+-----+------------+
| ID | VAL | STATUS     |
|----+-----+------------|
|  2 |  50 | Beta       |
|  3 |  60 | Production |
|  4 |  40 | Production |
+----+-----+------------+

ALL BY NAME을 사용하여 병합 수행

다음 예에서는 소스 테이블의 값을 사용하여 대상 테이블의 값을 삽입 및 업데이트하는 병합을 수행합니다. 이 예에서는 WHEN MATCHED ... THEN ALL BY NAMEWHEN NOT MATCHED ... THEN ALL BY NAME 하위 절을 사용하여 병합이 모든 열에 적용되도록 지정합니다.

열 개수와 열 이름이 같지만, 두 열의 순서가 다른 테이블 2개를 만듭니다.

CREATE OR REPLACE TABLE merge_example_target_all (
  id INTEGER,
  x INTEGER,
  y VARCHAR);

CREATE OR REPLACE TABLE merge_example_source_all (
  id INTEGER,
  y VARCHAR,
  x INTEGER);
Copy

테이블을 로딩합니다.

INSERT INTO merge_example_target_all (id, x, y) VALUES
  (1, 10, 'Skiing'),
  (2, 20, 'Snowboarding');

INSERT INTO merge_example_source_all (id, y, x) VALUES
  (1, 'Skiing', 10),
  (2, 'Snowboarding', 25),
  (3, 'Skating', 30);
Copy

테이블에 값을 표시합니다.

SELECT * FROM merge_example_target_all;
Copy
+----+----+--------------+
| ID |  X | Y            |
|----+----+--------------|
|  1 | 10 | Skiing       |
|  2 | 20 | Snowboarding |
+----+----+--------------+
SELECT * FROM merge_example_source_all;
Copy
+----+--------------+----+
| ID | Y            |  X |
|----+--------------+----|
|  1 | Skiing       | 10 |
|  2 | Snowboarding | 25 |
|  3 | Skating      | 30 |
+----+--------------+----+

MERGE 문을 실행합니다.

MERGE INTO merge_example_target_all
  USING merge_example_source_all
  ON merge_example_target_all.id = merge_example_source_all.id
  WHEN MATCHED THEN
    UPDATE ALL BY NAME
  WHEN NOT MATCHED THEN
    INSERT ALL BY NAME;
Copy
+-------------------------+------------------------+
| number of rows inserted | number of rows updated |
|-------------------------+------------------------|
|                       1 |                      2 |
+-------------------------+------------------------+

대상 테이블에 새 값을 표시합니다.

SELECT *
  FROM merge_example_target_all
  ORDER BY id;
Copy
+----+----+--------------+
| ID |  X | Y            |
|----+----+--------------|
|  1 | 10 | Skiing       |
|  2 | 25 | Snowboarding |
|  3 | 30 | Skating      |
+----+----+--------------+

소스 복제본으로 병합 수행

소스에 중복 값이 있고 대상에는 일치하는 값이 없는 병합을 수행합니다. 소스 레코드의 모든 복사본이 대상에 삽입됩니다. 자세한 내용은 INSERT에 대한 결정적 결과 섹션을 참조하세요.

두 테이블을 모두 자르고 중복이 포함된 새 행을 소스 테이블에 로딩합니다.

TRUNCATE table merge_example_target;

TRUNCATE table merge_example_source;

INSERT INTO merge_example_source (id, description) VALUES
  (50, 'This is a duplicate in the source and has no match in target'),
  (50, 'This is a duplicate in the source and has no match in target');
Copy

merge_example_target``에는 값이 없습니다. ``merge_example_source 테이블의 값을 표시합니다.

SELECT * FROM merge_example_source;
Copy
+----+--------------------------------------------------------------+
| ID | DESCRIPTION                                                  |
|----+--------------------------------------------------------------|
| 50 | This is a duplicate in the source and has no match in target |
| 50 | This is a duplicate in the source and has no match in target |
+----+--------------------------------------------------------------+

MERGE 문을 실행합니다.

MERGE INTO merge_example_target
  USING merge_example_source
  ON merge_example_target.id = merge_example_source.id
  WHEN MATCHED THEN
    UPDATE SET merge_example_target.description = merge_example_source.description
  WHEN NOT MATCHED THEN
    INSERT (id, description) VALUES
      (merge_example_source.id, merge_example_source.description);
Copy
+-------------------------+------------------------+
| number of rows inserted | number of rows updated |
|-------------------------+------------------------|
|                       2 |                      0 |
+-------------------------+------------------------+

대상 테이블에 새 값을 표시합니다.

SELECT * FROM merge_example_target;
Copy
+----+--------------------------------------------------------------+
| ID | DESCRIPTION                                                  |
|----+--------------------------------------------------------------|
| 50 | This is a duplicate in the source and has no match in target |
| 50 | This is a duplicate in the source and has no match in target |
+----+--------------------------------------------------------------+

결정적 결과와 비결정적 결과로 병합 수행

비결정적 및 결정적 결과를 생성하는 조인을 사용해 레코드를 병합합니다.

두 테이블을 만들고 로드합니다.

CREATE OR REPLACE TABLE merge_example_target_orig (k NUMBER, v NUMBER);

INSERT INTO merge_example_target_orig VALUES (0, 10);

CREATE OR REPLACE TABLE merge_example_src (k NUMBER, v NUMBER);

INSERT INTO merge_example_src VALUES (0, 11), (0, 12), (0, 13);
Copy

다음 예에서 병합을 수행하면 여러 업데이트가 서로 충돌합니다. ERROR_ON_NONDETERMINISTIC_MERGE 세션 매개 변수가 true`로 설정되면 MERGE 문이 오류를 반환합니다. 그렇지 않으면 MERGE 문은 ``merge_example_target_clone.v``를 중복 행(행이 정의되지 않음) 하나의 값(예: ``11`, 12 또는 13)으로 업데이트합니다.

CREATE OR REPLACE TABLE merge_example_target_clone
  CLONE merge_example_target_orig;

MERGE INTO  merge_example_target_clone
  USING merge_example_src
  ON merge_example_target_clone.k = merge_example_src.k
  WHEN MATCHED THEN UPDATE SET merge_example_target_clone.v = merge_example_src.v;
Copy

업데이트와 삭제는 서로 충돌합니다. ERROR_ON_NONDETERMINISTIC_MERGE 세션 매개 변수가 true`로 설정되면 MERGE 문이 오류를 반환합니다. 그렇지 않으면 MERGE 문은 행을 삭제하거나 ``merge_example_target_clone.v``를 중복 행(행이 정의되지 않음) 하나의 값(예: ``12` 또는 13)으로 업데이트합니다.

CREATE OR REPLACE TABLE merge_example_target_clone
  CLONE merge_example_target_orig;

MERGE INTO merge_example_target_clone
  USING merge_example_src
  ON merge_example_target_clone.k = merge_example_src.k
  WHEN MATCHED AND merge_example_src.v = 11 THEN DELETE
  WHEN MATCHED THEN UPDATE SET merge_example_target_clone.v = merge_example_src.v;
Copy

여러 삭제는 서로 충돌하지 않습니다. 어떤 절과도 일치하지 않는 조인된 값은 삭제를 방지하지 않습니다(merge_example_src.v = 13). MERGE 문이 성공하며 대상 행이 삭제됩니다.

CREATE OR REPLACE TABLE target CLONE merge_example_target_orig;

MERGE INTO merge_example_target_clone
  USING merge_example_src
  ON merge_example_target_clone.k = merge_example_src.k
  WHEN MATCHED AND merge_example_src.v <= 12 THEN DELETE;
Copy

어떤 절과도 일치하지 않는 조인된 값은 업데이트를 방지하지 않습니다(merge_example_src.v = 12, 13). MERGE 문이 성공하며 대상 행이 ``target.v = 11``로 설정됩니다.

CREATE OR REPLACE TABLE merge_example_target_clone CLONE target_orig;

MERGE INTO merge_example_target_clone
  USING merge_example_src
  ON merge_example_target_clone.k = merge_example_src.k
  WHEN MATCHED AND merge_example_src.v = 11
    THEN UPDATE SET merge_example_target_clone.v = merge_example_src.v;
Copy

소스 절에서 GROUP BY를 사용하여 각 대상 행이 소스의 한 행에 대해 조인되도록 합니다.

CREATE OR REPLACE TABLE merge_example_target_clone CLONE merge_example_target_orig;

MERGE INTO merge_example_target_clone
  USING (SELECT k, MAX(v) AS v FROM merge_example_src GROUP BY k) AS b
  ON merge_example_target_clone.k = b.k
  WHEN MATCHED THEN UPDATE SET merge_example_target_clone.v = b.v
  WHEN NOT MATCHED THEN INSERT (k, v) VALUES (b.k, b.v);
Copy

DATE 값을 기준으로 병합 수행

다음 예에서 members 테이블은 이름, 주소 그리고 현지 체육관에 지급하는 현재의 요금(members.fee)을 저장합니다. signup 테이블은 각 회원의 등록 날짜(signup.date)를 저장합니다. 무료 평가판 만료 후, MERGE 문은 30일 이상 전에 체육관에 가입한 회원에게 기본 요금인 $40를 적용합니다.

MERGE INTO members m
  USING (SELECT id, date
    FROM signup
    WHERE DATEDIFF(day, CURRENT_DATE(), signup.date::DATE) < -30) s
  ON m.id = s.id
  WHEN MATCHED THEN UPDATE SET m.fee = 40;
Copy