적용 대상:SQL 서버
Azure SQL Database
Azure SQL Managed Instance
Azure Synapse Analytics (전용 SQL 풀 전용)
Microsoft Fabric
의 SQL 데이터베이스Microsoft Fabric의 웨어하우스
이 문은 MERGE 원본 테이블과의 조인 결과에서 대상 테이블에 대한 삽입, 업데이트 또는 삭제 작업을 실행합니다. 예를 들어 원본 테이블과의 차이점에 따라 대상 테이블에서 행을 삽입, 업데이트 및 삭제하여 두 테이블을 동기화합니다.
이 문서에서는 선택한 제품 버전에 따라 다른 구문, 인수, 설명, 사용 권한 및 예제를 제공합니다. 버전 드롭다운 목록에서 원하는 제품 버전을 선택합니다.
Note
패브릭 데이터 웨어하우스 MERGE 에서 미리 보기로 제공됩니다.
Syntax
SQL Server 및 Azure SQL Database에 대한 구문:
[ WITH <common_table_expression> [,...n] ]
MERGE
[ TOP ( expression ) [ PERCENT ] ]
[ INTO ] <target_table> [ WITH ( <merge_hint> ) ] [ [ AS ] table_alias ]
USING <table_source> [ [ AS ] table_alias ]
ON <merge_search_condition>
[ WHEN MATCHED [ AND <clause_search_condition> ]
THEN <merge_matched> ] [ ...n ]
[ WHEN NOT MATCHED [ BY TARGET ] [ AND <clause_search_condition> ]
THEN <merge_not_matched> ]
[ WHEN NOT MATCHED BY SOURCE [ AND <clause_search_condition> ]
THEN <merge_matched> ] [ ...n ]
[ <output_clause> ]
[ OPTION ( <query_hint> [ ,...n ] ) ]
;
<target_table> ::=
{
[ database_name . schema_name . | schema_name . ] [ [ AS ] target_table ]
| @variable [ [ AS ] target_table ]
| common_table_expression_name [ [ AS ] target_table ]
}
<merge_hint>::=
{
{ [ <table_hint_limited> [ ,...n ] ]
[ [ , ] { INDEX ( index_val [ ,...n ] ) | INDEX = index_val }]
}
}
<merge_search_condition> ::=
<search_condition>
<merge_matched>::=
{ UPDATE SET <set_clause> | DELETE }
<merge_not_matched>::=
{
INSERT [ ( column_list ) ]
{ VALUES ( values_list )
| DEFAULT VALUES }
}
<clause_search_condition> ::=
<search_condition>
Azure Synapse Analytics, Fabric Data Warehouse의 구문:
[ WITH <common_table_expression> [,...n] ]
MERGE
[ INTO ] <target_table> [ [ AS ] table_alias ]
USING <table_source> [ [ AS ] table_alias ]
ON <merge_search_condition>
[ WHEN MATCHED [ AND <clause_search_condition> ]
THEN <merge_matched> ] [ ...n ]
[ WHEN NOT MATCHED [ BY TARGET ] [ AND <clause_search_condition> ]
THEN <merge_not_matched> ]
[ WHEN NOT MATCHED BY SOURCE [ AND <clause_search_condition> ]
THEN <merge_matched> ] [ ...n ]
[ OPTION ( <query_hint> [ ,...n ] ) ]
; -- The semi-colon is required, or the query will return a syntax error.
<target_table> ::=
{
[ database_name . schema_name . | schema_name . ]
target_table
}
<merge_search_condition> ::=
<search_condition>
<merge_matched>::=
{ UPDATE SET <set_clause> | DELETE }
<merge_not_matched>::=
{
INSERT [ ( column_list ) ]
VALUES ( values_list )
}
<clause_search_condition> ::=
<search_condition>
Arguments
common_table_expression와 함께 <>
문 범위 MERGE 내에 정의된 임시 명명된 결과 집합 또는 뷰(공통 테이블 식이라고도 함)를 지정합니다. 결과 집합은 단순 쿼리에서 파생되며 문에서 MERGE 참조됩니다. 자세한 내용은 WITH common_table_expression (Transact-SQL)을 참조하세요.
TOP ( 표정 ) [퍼센트]
영향을 받는 행의 개수 또는 비율을 지정합니다.
식은 행의 수 또는 비율일 수 있습니다. 식에서 TOP 참조되는 행은 순서대로 정렬되지 않습니다. 자세한 내용은 TOP(Transact-SQL)을 참조하세요.
이 절은 TOP 전체 원본 테이블과 전체 대상 테이블 조인 및 삽입, 업데이트 또는 삭제 작업에 적합하지 않은 조인된 행이 제거된 후에 적용됩니다. 이 절은 TOP 조인된 행 수를 지정된 값으로 더 줄입니다. 이러한 작업(삽입, 업데이트 또는 삭제)은 정렬되지 않은 방식으로 나머지 조인된 행에 적용됩니다. 즉, 절에 정의된 작업 간에 행이 분산되는 순서는 WHEN 없습니다. 예를 들어 (10)을 지정하면 TOP 10개의 행에 영향을 줍니다. 이러한 행 중 7개는 업데이트되고 3개는 삽입되거나 1개는 삭제되고 5개는 업데이트되고 4개는 삽입될 수 있습니다.
원본 테이블에 대한 필터가 없으면 문은 원본 테이블에서 MERGE 테이블 검색 또는 클러스터형 인덱스 검색뿐만 아니라 대상 테이블의 테이블 검색 또는 클러스터형 인덱스 검색을 수행할 수 있습니다. 따라서 여러 일괄 처리를 만들어 큰 테이블을 수정하는 절을 TOP 사용하는 경우에도 I/O 성능이 영향을 받는 경우가 있습니다. 이러한 시나리오에서 연속된 모든 일괄 처리는 새로운 행을 대상으로 해야 합니다.
database_name
target_table이 있는 데이터베이스의 이름입니다.
schema_name
target_table이 속해 있는 스키마의 이름입니다.
target_table
에 따라 <table_source>데이터 행이 <clause_search_condition> 일치하는 테이블 또는 뷰입니다.
target_table 문의 절 WHEN 에 MERGE 지정된 삽입, 업데이트 또는 삭제 작업의 대상입니다.
target_table이 뷰일 경우 이에 대한 모든 동작은 뷰 업데이트 조건을 충족해야 합니다. 자세한 내용은 뷰를 통해 데이터 수정을 참조하세요.
target_table은 원격 테이블일 수 없습니다. target_table 정의된 규칙을 가질 수 없습니다. target_table 메모리 최적화 테이블일 수 없습니다.
힌트는 .로 <merge_hint>지정할 수 있습니다.
<merge_hint> 는 Azure Synapse Analytics에서 지원되지 않습니다.
[ 아스] table_alias
target_table의 테이블을 참조하기 위한 대체 이름입니다.
using <table_source>
에 따라 target_table 데이터 행과 일치하는 데이터 원본을 지정합니다.<merge_search_condition> 이 일치의 결과는 문의 절에서 WHEN 수행할 작업을 지시합니다 MERGE .
<table_source> 는 원격 테이블 또는 원격 테이블에 액세스하는 파생 테이블일 수 있습니다.
<table_source> 는 Transact-SQL 테이블 값 생성자를 사용하여 여러 행을 지정하여 테이블을 생성하는 파생 테이블일 수 있습니다.
<table_source>는 SELECT ... UNION ALL을 사용해 여러 행을 지정하여 테이블을 생성하는 파생 테이블일 수 있습니다.
[ 아스] table_alias
table_source의 테이블을 참조하기 위한 대체 이름입니다.
이 절의 구문 및 인수에 대한 자세한 내용은 FROM(Transact-SQL)을 참조하세요.
온merge_search_condition <>
일치하는 부분을 확인하기 위해 <table_source>와 target_table을 조인하는 조건을 지정합니다.
Caution
일치 용도로 사용되는 대상 테이블에서는 열만 지정하는 것이 중요합니다. 즉, 대상 테이블에서 원본 테이블의 해당 열과 비교할 열을 지정해야 합니다. 절에서 대상 테이블 ON 의 행을 필터링하여 쿼리 성능을 향상시키려고 하지 마세요(예: 지정 AND NOT target_table.column_x = value). 이렇게 하면 예기치 않은 잘못된 결과가 반환됩니다.
일치하면 <merge_matched>
반환된 행과 일치하고 추가 검색 조건을 충족하는 *target_table 모든 행 <table_source> ON <merge_search_condition>이 절에 <merge_matched> 따라 업데이트되거나 삭제되도록 지정합니다.
문에는 MERGE 최대 두 개의 WHEN MATCHED 절이 있을 수 있습니다. 두 절을 지정하는 경우 첫 번째 절에는 절이 함께 AND<search_condition> 있어야 합니다. 지정된 행의 경우 두 번째 절은 첫 번째 WHEN MATCHED 절이 아닌 경우에만 적용됩니다. 두 절 WHEN MATCHED 이 있는 경우 동작을 UPDATE 지정해야 하고 하나는 작업을 지정 DELETE 해야 합니다. 절에 UPDATE 지정되고 둘 이상의 행 <merge_matched> 이 <table_source> 행과 일치하는 경우 <merge_search_condition> SQL Server는 오류를 반환합니다. 문은 MERGE 동일한 행을 두 번 이상 업데이트하거나 동일한 행을 업데이트하고 삭제할 수 없습니다.
[대상별] <일치하지 않으면 merge_not_matched>
행이 반환 된 모든 행 <table_source> ON <merge_search_condition> 에 대해 target_table 삽입되도록 지정합니다. 이 행은 target_table 행과 일치하지 않지만 있는 경우 추가 검색 조건을 충족합니다. 삽입할 값은 절에 <merge_not_matched> 의해 지정됩니다. 문에는 MERGE 절이 하나 WHEN NOT MATCHED [ BY TARGET ] 만 있을 수 있습니다.
원본 <과 일치하지 않으면 merge_matched>
반환된 행과 일치하지 않고 추가 검색 조건을 충족하는 *target_table 모든 행 <table_source> ON <merge_search_condition>이 절에 <merge_matched> 따라 업데이트되거나 삭제되도록 지정합니다.
문에는 MERGE 최대 두 개의 WHEN NOT MATCHED BY SOURCE 절이 있을 수 있습니다. 두 절을 지정하는 경우 첫 번째 절에는 절이 함께 AND<clause_search_condition> 있어야 합니다. 지정된 행의 경우 두 번째 절은 첫 번째 WHEN NOT MATCHED BY SOURCE 절이 아닌 경우에만 적용됩니다. 두 절 WHEN NOT MATCHED BY SOURCE 이 있는 경우 작업을 지정 UPDATE 해야 하고 하나는 작업을 지정 DELETE 해야 합니다. 대상 테이블의 열만 .에서 <clause_search_condition>참조할 수 있습니다.
행이 반환 <table_source>되지 않으면 원본 테이블의 열에 액세스할 수 없습니다. 절에 <merge_matched> 지정된 업데이트 또는 삭제 작업이 원본 테이블의 열을 참조하는 경우 오류 207(잘못된 열 이름)이 반환됩니다. 예를 들어 이 절 WHEN NOT MATCHED BY SOURCE THEN UPDATE SET TargetTable.Col1 = SourceTable.Col1 은 원본 테이블에 액세스할 수 없으므로 문이 실패 Col1 할 수 있습니다.
그리고 <clause_search_condition>
유효한 검색 조건을 지정합니다. 자세한 내용은 검색 조건(Transact-SQL)을 참조하세요.
<table_hint_limited>
문에서 수행한 각 삽입, 업데이트 또는 삭제 작업에 대해 대상 테이블에 적용할 하나 이상의 테이블 힌트를 MERGE 지정합니다.
WITH 키워드와 괄호가 필요합니다.
NOLOCK 허용되지 READUNCOMMITTED 않습니다. 테이블 힌트에 대한 자세한 내용은 테이블 힌트(Transact-SQL)를 참조하세요.
TABLOCK 문의 대상 INSERT 인 테이블에 힌트를 지정하면 힌트를 지정하는 것과 같은 효과가 있습니다TABLOCKX. 두 경우 모두 테이블이 배타적으로 잠깁니다. FORCESEEK를 지정할 경우 원본 테이블과 조인된 대상 테이블의 암시적 인스턴스에 이 힌트가 적용됩니다.
Caution
지정하면 READPASTWHEN NOT MATCHED [ BY TARGET ] THEN INSERT 제약 조건을 위반 INSERT 하는 작업이 발생할 UNIQUE 수 있습니다.
INDEX ( index_val [ ,... n ] )
원본 테이블과의 암시적 조인을 수행하는 대상 테이블에 있는 하나 이상의 인덱스에 대한 이름 또는 ID를 지정합니다. 자세한 내용은 테이블 힌트(Transact-SQL)를 참조하세요.
<output_clause>
업데이트, 삽입 또는 삭제되는 target_table의 모든 행과 일치하는 행을 임의의 순서로 반환합니다.
$action 은 출력 절에 지정할 수 있습니다.
$action는 각 행에 대해 수행된 작업에 따라 각 행INSERTUPDATE에 대한 세 가지 값 중 하나를 반환하는 nvarchar(10)DELETE의 열입니다. 절은 OUTPUT .의 영향을 받는 MERGE행을 쿼리하거나 계산하는 권장 방법입니다. 이 절의 인수 및 동작에 대한 자세한 내용은 OUTPUT 절(Transact-SQL)을 참조하세요.
OPTION ( <query_hint> [ ,... n ] )
데이터베이스 엔진이 문을 처리하는 방식을 사용자 지정하기 위한 최적화 프로그램 힌트를 지정합니다. 자세한 내용은 쿼리 힌트(Transact-SQL)를 참조하세요.
<merge_matched>
반환된 행과 일치하지 않고 추가 검색 조건을 충족하는 모든 target_table 행에 <table_source> ON <merge_search_condition>적용되는 업데이트 또는 삭제 작업을 지정합니다.
업데이트 설정 <set_clause>
대상 테이블에서 업데이트할 열 또는 변수 이름의 목록 및 이 목록을 업데이트하는 데 사용할 값을 지정합니다.
이 절의 인수에 대한 자세한 내용은 UPDATE(Transact-SQL)를 참조하세요. 변수를 열과 동일한 값으로 설정할 수는 없습니다.
DELETE
target_table의 행과 일치하는 행이 삭제되도록 지정합니다.
<merge_not_matched>
대상 테이블에 삽입할 값을 지정합니다.
( column_list )
데이터를 삽입할 대상 테이블에 있는 하나 이상의 열 목록입니다. 열은 단일 부분으로 지정해야 합니다. 그렇지 않으면 MERGE 문이 실패합니다.
column_list는 괄호로 묶고 쉼표로 구분해야 합니다.
VALUES ( values_list)
대상 테이블에 삽입할 값을 반환하는 상수, 변수 또는 식의 쉼표로 구분된 목록입니다. 식에는 문을 포함 EXECUTE 할 수 없습니다.
<search_condition>
지정 <merge_search_condition> 하거나 <clause_search_condition>지정할 검색 조건을 지정합니다. 이 절의 인수에 대한 자세한 내용은 검색 조건(Transact-SQL)을 참조하세요.
<그래프 검색 패턴>
그래프 일치 패턴을 지정합니다. 이 절의 인수에 대한 자세한 내용은 MATCH(Transact-SQL)를 참조하세요.
Remarks
문에 대해 MERGE 설명된 조건부 동작은 두 테이블에 일치하는 특성이 복잡한 경우 가장 잘 작동합니다. 예를 들어, 행이 없는 경우 행을 삽입하고 행이 일치하지 않는 경우 행을 업데이트합니다. 다른 테이블의 행을 기반으로 한 테이블을 단순히 업데이트하는 경우 , INSERT및 문을 사용하여 성능 및 UPDATE 확장성을 DELETE향상시킵니다. 다음은 그 예입니다.
INSERT tbl_A (col, col2)
SELECT col, col2
FROM tbl_B
WHERE NOT EXISTS (SELECT col FROM tbl_A A2 WHERE A2.col = tbl_B.col);
세 MATCHED 절 중 하나 이상을 지정해야 하지만 어떤 순서로든 지정할 수 있습니다. 동일한 MATCHED 절에서 변수를 두 번 이상 업데이트할 수 없습니다.
문에 의해 MERGE 대상 테이블에 지정된 모든 삽입, 업데이트 또는 삭제 작업은 연결된 참조 무결성 제약 조건을 포함하여 정의된 모든 제약 조건에 의해 제한됩니다.
IGNORE_DUP_KEY 대상 테이블 ON 의 고유 인덱스에 대한 경우 MERGE 이 설정을 무시합니다.
문에는 MERGE 문 종결자로 세미콜론(;)이 필요합니다. 종결자 없이 문을 실행할 때 MERGE 오류 10713이 발생합니다.
뒤에 MERGE사용되는 경우 @@ROWCOUNT(Transact-SQL) 는 클라이언트에 삽입, 업데이트 및 삭제된 총 행 수를 반환합니다.
MERGE 는 데이터베이스 호환성 수준이 이상으로 설정된 100 경우 완전히 예약된 키워드입니다. 이 문은 MERGE 데이터베이스 호환성 수준과 90 데이터베이스 호환성 수준 모두에서 100 사용할 수 있지만 데이터베이스 호환성 수준이 설정된 90경우 키워드가 완전히 예약되지는 않습니다.
Caution
MERGE를 사용하는 경우 문을 사용하지 마세요.
MERGE 대기 중 업데이트 트리거 및 대기 중 업데이트 트리거는 호환되지 않습니다.
MERGE 문을 and INSERT 문으로 UPDATE 바꿉니다.
Azure Synapse Analytics 고려 사항
Azure Synapse Analytics MERGE 에서 이 명령은 SQL Server 및 Azure SQL Database에 비해 다음과 같은 차이점이 있습니다.
- 배포 키 열을 업데이트하는 데 사용하는
MERGE것은 10.0.17829.0보다 오래된 빌드에서 지원되지 않습니다. 일시 중지하거나 강제로 업그레이드할 수 없는 경우 버전 10.0.17829.0까지 ANSIUPDATE FROM ... JOIN문을 해결 방법으로 사용합니다. -
MERGE업데이트는 삭제 및 삽입 쌍으로 구현됩니다. 업데이트의MERGE영향을 받는 행 수에는 삭제된 행과 삽입된 행이 포함됩니다. -
MERGE...WHEN NOT MATCHED INSERT는 열이 있는IDENTITY테이블에 대해 지원되지 않습니다. - 원본 테이블의 절에는
USING테이블 값 생성자를 사용할 수 없습니다.SELECT ... UNION ALL을 사용하여 여러 행이 있는 파생 원본 테이블을 만듭니다. - 다음 표에서는 배포 유형이 다른 테이블에 대한 지원을 설명합니다.
| Azure Synapse Analytics의 MERGE CLAUSE | 지원되는 TARGET 배포 테이블 |
지원되는 원본 배포 테이블 | Comment |
|---|---|---|---|
WHEN MATCHED |
모든 배포 유형 | 모든 배포 유형 | |
NOT MATCHED BY TARGET |
HASH |
모든 배포 유형 | 두 테이블을 동기화하는 데 사용합니다 UPDATE/DELETE FROM...JOIN . |
NOT MATCHED BY SOURCE |
모든 배포 유형 | 모든 배포 유형 |
Tip
배포 해시 키를 열 JOIN 로 MERGE 사용하고 같음 비교만 수행하는 경우 중복 업데이트이므로 절의 열 WHEN MATCHED THEN UPDATE SET 목록에서 배포 키를 생략할 수 있습니다.
Azure Synapse Analytics MERGE 에서 10.0.17829.0 이전 빌드의 명령은 특정 조건에서 대상 테이블을 일관되지 않은 상태로 두고 행이 잘못된 배포에 배치되어 나중에 쿼리가 잘못된 결과를 반환하도록 할 수 있습니다. 이 문제는 다음과 같은 두 가지 경우에 발생할 수 있습니다.
| Scenario | Comment |
|---|---|
|
사례 1 보조 인덱스 또는 MERGE 제약 조건이 포함된 HASH 분산 TARGET 테이블에서 사용 UNIQUE |
- Synapse SQL 10.0.15563.0 이상 버전에서 수정되었습니다. - 10.0.15563.0보다 낮은 버전을 반환하는 경우 SELECT @@VERSION 수동으로 Synapse SQL 풀을 일시 중지하고 다시 시작하여 이 수정 사항을 선택합니다.- 수정 사항이 Synapse SQL 풀에 MERGE 적용될 때까지 보조 인덱스 또는 HASH 제약 조건이 있는 분산 TARGET 테이블에서 명령을 사용하지 UNIQUE 마십시오. |
|
사례 2 MERGE를 사용하여 문이 HASH 분산 테이블의 배포 키 열을 업데이트합니다. |
- Synapse SQL 10.0.17829.0 이상 버전에서 수정되었습니다. - 10.0.17829.0보다 낮은 버전을 반환하는 경우 SELECT @@VERSION 수동으로 Synapse SQL 풀을 일시 중지하고 다시 시작하여 이 수정 사항을 선택합니다.- Synapse SQL 풀에 수정 사항이 적용될 때까지 배포 키 열을 업데이트하는 명령을 사용하지 MERGE 마십시오. |
두 시나리오의 업데이트는 이전 MERGE 실행의 영향을 받은 테이블을 복구하지 않습니다. 다음 스크립트를 사용하여 영향을 받는 테이블을 수동으로 식별하고 복구합니다.
데이터베이스의 분산 테이블이 문제가 될 수 있는지 확인 HASH 하려면(앞에서 언급한 경우에 사용되는 경우) 다음 문을 실행합니다.
-- Case 1
SELECT a.name,
c.distribution_policy_desc,
b.type
FROM sys.tables a
INNER JOIN sys.indexes b
ON a.object_id = b.object_id
INNER JOIN sys.pdw_table_distribution_properties c
ON a.object_id = c.object_id
WHERE b.type = 2
AND c.distribution_policy_desc = 'HASH';
-- Subject to Case 2, if distribution key value is updated in MERGE statement
SELECT a.name,
c.distribution_policy_desc
FROM sys.tables a
INNER JOIN sys.pdw_table_distribution_properties c
ON a.object_id = c.object_id
WHERE c.distribution_policy_desc = 'HASH';
분산 테이블 HASH 이 사례 1 또는 사례 2의 영향을 받는지 MERGE 확인하려면 다음 단계에 따라 테이블에 행이 잘못된 분포로 배치되었는지 검사합니다. 반환되는 경우 no need for repair 이 테이블은 영향을 받지 않습니다.
IF object_id('[check_table_1]', 'U') IS NOT NULL
DROP TABLE [check_table_1]
GO
IF object_id('[check_table_2]', 'U') IS NOT NULL
DROP TABLE [check_table_2]
GO
CREATE TABLE [check_table_1]
WITH (DISTRIBUTION = ROUND_ROBIN) AS
SELECT <DISTRIBUTION_COLUMN> AS x
FROM <MERGE_TABLE>
GROUP BY <DISTRIBUTION_COLUMN>;
GO
CREATE TABLE [check_table_2]
WITH (DISTRIBUTION = HASH (x)) AS
SELECT x
FROM [check_table_1];
GO
IF NOT EXISTS (
SELECT TOP 1 *
FROM (
SELECT <DISTRIBUTION_COLUMN> AS x
FROM <MERGE_TABLE>
EXCEPT
SELECT x
FROM [check_table_2]
) AS tmp
)
SELECT 'no need for repair' AS result
ELSE
SELECT 'needs repair' AS result
GO
IF object_id('[check_table_1]', 'U') IS NOT NULL
DROP TABLE [check_table_1]
GO
IF object_id('[check_table_2]', 'U') IS NOT NULL
DROP TABLE [check_table_2]
GO
영향을 받는 테이블을 복구하려면 이 문을 실행하여 이전 테이블의 모든 행을 새 테이블로 복사해야 합니다.
IF object_id('[repair_table_temp]', 'U') IS NOT NULL
DROP TABLE [repair_table_temp];
GO
IF object_id('[repair_table]', 'U') IS NOT NULL
DROP TABLE [repair_table];
GO
CREATE TABLE [repair_table_temp]
WITH (DISTRIBUTION = ROUND_ROBIN) AS
SELECT *
FROM <MERGE_TABLE>;
GO
-- [repair_table] will hold the repaired table generated from <MERGE_TABLE>
CREATE TABLE [repair_table]
WITH (DISTRIBUTION = HASH (<DISTRIBUTION_COLUMN>)) AS
SELECT *
FROM [repair_table_temp];
GO
IF object_id('[repair_table_temp]', 'U') IS NOT NULL
DROP TABLE [repair_table_temp];
GO
Troubleshooting
특정 시나리오에서 문은 MERGE 대상 또는 원본 테이블에 열이 1,024개 없는 경우에도 오류가 CREATE TABLE failed because column <> in table <> exceeds the maximum of 1024 columns.발생할 수 있습니다. 이 시나리오는 다음 조건 중 어느 것이라도 충족될 때 발생할 수 있습니다.
- 여러 열이 (절
DELETE에UPDATE SETINSERT특정되지 않음) 내MERGE의WHEN [NOT] MATCHED또는 연산에 지정됩니다. - 조건의 모든 열에
JOINNCI(비클러스터형 인덱스)가 있습니다. - 대상 테이블이
HASH분산됨
이 오류가 발견되면 제안된 해결 방법은 다음과 같습니다.
트리거 구현
문에 MERGE 지정된 모든 삽입, 업데이트 또는 삭제 작업에 대해 SQL Server는 대상 테이블에 정의된 해당 AFTER 트리거를 실행하지만 트리거를 처음 또는 마지막으로 발생시키는 동작을 보장하지는 않습니다. 동일한 동작에 대해 정의된 트리거는 사용자가 지정하는 순서대로 실행됩니다. 트리거 실행 순서를 설정하는 방법은 시작 및 끝 트리거 지정을 참조하세요.
대상 테이블에 문에 의해 수행된 INSTEAD 삽입, 업데이트 또는 삭제 작업에 대해 정의된 OF 트리거가 있는 경우 문에 MERGE 지정된 모든 작업에 대해 OF 트리거를 사용하도록 설정 INSTEAD 해야 MERGE 합니다.
INSTEAD
UPDATE OF INSTEAD 또는 DELETE OF 트리거가 정의된 경우 업데이트 또는 삭제 작업이 실행되지 않습니다. 대신 트리거가 실행되고 이에 따라 inserted 및 deleted 테이블이 채워집니다.
INSTEAD
INSERT OF 트리거가 정의된 경우 삽입 작업이 수행되지 않습니다. 대신, 이에 따라 테이블이 채워집니다.
Note
별도의 INSERT문 UPDATE및 문과 DELETE 달리 트리거 내부에서 반영하는 @@ROWCOUNT 행 수가 더 많을 수 있습니다.
@@ROWCOUNT 트리거가 캡처하는 데이터 수정 문에 관계없이 내부 트리거는 AFTER 영향을 받는 MERGE총 행 수를 반영합니다. 예를 들어 문이 한 행을 MERGE 삽입하고, 한 행을 업데이트하고, 한 행을 삭제하는 경우 트리거 @@ROWCOUNT 가 문에 AFTER 대해서만 선언된 경우에도 트리거에 대해 INSERT 3이 됩니다.
Permissions
SELECT 원본 테이블 및 INSERTUPDATE대상 테이블에 대한 사용 권한 또는 DELETE 권한이 필요합니다. 자세한 내용은 SELECT(Transact-SQL), INSERT(Transact-SQL), UPDATE(Transact-SQL) 및 DELETE(Transact-SQL) 문서의 사용 권한 섹션을 참조하세요.
인덱스 모범 사례
문을 MERGE 사용하여 개별 DML 문을 단일 문으로 바꿀 수 있습니다. 이렇게 하면 작업이 하나의 문 내에서 수행되므로 원본 및 대상 테이블의 데이터가 처리되는 횟수가 최소화되어 쿼리 성능이 향상됩니다. 단, 성능 향상을 위해서는 인덱스, 조인 및 기타 고려 사항이 올바르게 설정되어야 합니다.
문의 성능을 MERGE 향상시키려면 다음 인덱스 지침을 사용하는 것이 좋습니다.
- 인덱스를 만들어 다음의 원본과 대상 간의 조인을 용이하게 합니다
MERGE.- 대상 테이블에 대한 조인 논리를 포함하는 키가 있는 원본 테이블의 조인 열에 인덱스를 만듭니다. 가능하면 고유해야 합니다.
- 또한 대상 테이블의 조인 열에 인덱스를 만듭니다. 가능하면 고유한 클러스터형 인덱스여야 합니다.
- 이러한 두 인덱스는 테이블의 데이터가 정렬되도록 하고 고유성을 통해 비교 성능을 지원합니다. 쿼리 최적화 프로그램이 중복 행을 찾아 업데이트하기 위해 별도로 유효성 검사를 수행할 필요가 없고 추가 정렬 작업도 필요 없으므로 쿼리 성능이 개선됩니다.
- 어떤 형태의 columnstore 인덱스가 있는 테이블을 문의 대상으로
MERGE사용하지 않도록 합니다. UPDATE와 마찬가지로 스테이징된 rowstore 테이블을 업데이트한 다음, 일괄 처리 및DELETE또는INSERT대신 수행하여 columnstore 인덱스를 사용하여 성능이UPDATEMERGE향상될 수 있습니다.
MERGE의 동시성 고려 사항
잠금 측면에서 불연속, MERGE 연속INSERTUPDATE, 문과 DELETE 다릅니다.
MERGE그러나 다른 잠금 메커니즘을 INSERTUPDATE사용하여 여전히 실행 및 DELETE 작업을 수행합니다. 일부 애플리케이션 요구 사항에 대한 개별 INSERTUPDATE문 및 DELETE 문을 작성하는 것이 더 효율적일 수 있습니다. 대규모 MERGE 로 복잡한 동시성 문제가 발생하거나 고급 문제 해결이 필요할 수 있습니다. 따라서 프로덕션에 배포하기 전에 문을 MERGE 철저히 테스트할 계획입니다.
MERGE문은 다음 시나리오에서 불연속 INSERTUPDATE및 DELETE 작업에 적합한 대체 요소입니다(제한되지 않음).
- 행 개수가 많은 ETL 작업으로 다른 동시 실행 작업이 필요하지 않은* 시간 동안 실행됩니다. 동시성이 많이 필요한 경우 문보다
INSERT차단이 적고 별도의UPDATEDELETE논리와MERGE논리가 더 잘 수행될 수 있습니다. - 행 개수 및 트랜잭션이 적은 복잡한 작업은 오랫동안 실행될 가능성이 작습니다.
- 최적의 실행 계획을 보장하도록 인덱스를 디자인할 수 있는 사용자 테이블을 포함하는 복잡한 작업으로 테이블 검색 및 조회를 방지하고 대신 인덱스 검색을 사용합니다.
기타 동시성 고려 사항은 다음과 같습니다.
- 고유 키가 삽입되고 업데이트
MERGE되어야 하는 일부 시나리오에서는 고유 키 위반을 방지하도록 지정HOLDLOCK합니다.HOLDLOCK는 트랜잭션 격리 수준의 동의어SERIALIZABLE로, 다른 동시 트랜잭션에서 이 트랜잭션이 읽은 데이터를 수정할 수 없습니다.SERIALIZABLE는 가장 안전한 격리 수준이지만 읽기가 진행되는 동안 가상 행이 삽입되거나 업데이트되지 않도록 데이터 범위에 대한 잠금을 유지하는 다른 트랜잭션과의 최소 동시성을 제공합니다. 자세한HOLDLOCK내용은 테이블 힌트 및SET TRANSACTION ISOLATION LEVEL(Transact-SQL)을 참조하세요.
JOIN 모범 사례
문의 성능을 MERGE 향상시키고 올바른 결과를 얻으려면 다음 조인 지침을 사용하는 것이 좋습니다.
- 원본 및 대상 테이블의
ON <merge_search_condition>데이터 일치 조건을 결정하는 절의 검색 조건만 지정합니다. 즉, 대상 테이블에서 원본 테이블의 해당 열과 비교할 열만 지정해야 합니다. - 상수와 같은 다른 값에 대한 비교는 포함하지 않습니다.
원본 또는 대상 테이블에서 행을 필터링하려면 다음 방법 중 하나를 사용합니다.
- 적절한 절에서 행 필터링에 대한 검색 조건을 지정합니다
WHEN. 예를 들어WHEN NOT MATCHED AND S.EmployeeName LIKE 'S%' THEN INSERT.... - 필터링된 행을 반환하는 원본 또는 대상에 대한 뷰를 정의하고 이 뷰를 원본 또는 대상 테이블로 참조합니다. 대상 테이블에 대해 정의된 뷰의 모든 동작은 뷰 업데이트를 위한 조건을 충족해야 합니다. 보기를 사용하여 데이터를 업데이트하는 방법에 대한 자세한 내용은 뷰를 통해 데이터 수정을 참조하세요.
-
WITH <common table expression>절을 사용하여 원본 또는 대상 테이블에서 행을 필터링합니다. 이 메서드는 절에ON추가 검색 조건을 지정하는 것과 유사하며 잘못된 결과를 생성할 수 있습니다. 사용하지 않는 것이 좋으며 사용할 경우 구현 전에 철저히 테스트해야 합니다.
문의 조인 MERGE 작업은 문의 조 SELECT 인과 동일한 방식으로 최적화됩니다. 즉, SQL Server에서 조인을 처리할 때 쿼리 최적화 프로그램은 여러 가지 가능한 방법 중 가장 효율적인 방법을 선택합니다. 원본 및 대상의 크기가 비슷하고 ‘인덱스를 위한 최선의 방법’ 섹션에 설명된 인덱스 지침을 원본 및 대상 테이블에 적용한 경우 병합 조인 연산자가 가장 효율적인 쿼리 계획입니다. 두 테이블 모두 한 번만 검색되고 데이터를 정렬할 필요가 없기 때문입니다. 원본 테이블이 대상 테이블보다 작은 경우 중첩 루프 연산자가 좋습니다.
문에 절을 지정하여 특정 조인을 OPTION (<query_hint>) 강제로 사용할 수 있습니다 MERGE . 이 조인 형식은 인덱스를 사용하지 않으므로 해시 조인을 문에 대한 MERGE 쿼리 힌트로 사용하지 않는 것이 좋습니다.
매개 변수화 모범 사례
SELECT매개 변수 없이 , INSERT, UPDATE또는 DELETE 문이 실행되는 경우 SQL Server 쿼리 최적화 프로그램에서 내부적으로 문을 매개 변수화하도록 선택할 수 있습니다. 즉, 쿼리에 포함된 모든 리터럴 값이 매개 변수로 대체됩니다. 예를 들어 문 INSERT dbo.MyTable (Col1, Col2) VALUES (1, 10)은 내부적으로 .로 INSERT dbo.MyTable (Col1, Col2) VALUES (@p1, @p2)구현될 수 있습니다. 단순 매개 변수화라고 하는 이 프로세스는 관계형 엔진이 새 SQL 문을 이전에 컴파일된 기존 실행 계획과 일치시키는 기능을 향상합니다. 쿼리 컴파일 및 다시 컴파일 빈도가 감소하므로 쿼리 성능이 향상될 수 있습니다. 쿼리 최적화 프로그램은 간단한 매개 변수화 프로세스를 문에 MERGE 적용하지 않습니다. 따라서 문이 MERGE 실행될 때마다 INSERT 새 계획이 컴파일되기 때문에 리터럴 값이 포함된 문이 수행되지 않을 수 있으며 개별 UPDATEDELETEMERGE 또는 문이 수행되지 않을 수 있습니다.
쿼리 성능을 높이려면 다음 매개 변수화 지침을 따르는 것이 좋습니다.
- 절 및 문의 절에 있는
ON <merge_search_condition>WHEN모든 리터럴 값을 매개 변수화합니다MERGE. 예를 들어 리터럴 값을 적절한 입력 매개 변수로 대체하는 저장 프로시저에 문을 통합MERGE할 수 있습니다. - 문을 매개 변수화할 수 없는 경우
TEMPLATE형식의 계획 지침을 만들고PARAMETERIZATION FORCED쿼리 힌트를 이 계획 지침에 지정합니다. 자세한 내용은 계획 지침을 사용하여 쿼리 매개 변수화 동작 지정을 참조하세요. - 데이터베이스에서 문이 자주 실행되는 경우
MERGE데이터베이스의PARAMETERIZATION옵션을 .로 설정하는 것이 좋습니다FORCED. 이 옵션을 설정할 때는 신중해야 합니다.PARAMETERIZATION옵션은 데이터베이스 수준 설정이므로 데이터베이스에 대한 모든 쿼리의 처리 방식에 영향을 미칩니다. 자세한 내용은 강제 매개 변수화를 참조하세요. - 계획 지침보다 새롭고 더 간단한 대안으로 쿼리 저장소 힌트를 사용한 유사한 전략을 사용하는 것이 좋습니다. 자세한 내용은 쿼리 저장소 힌트를 참조하세요.
TOP 절 모범 사례
MERGE 문 TOP 에서 이 절은 원본 테이블과 대상 테이블이 조인된 후, 삽입, 업데이트 또는 삭제 작업을 수행할 자격이 없는 행이 제거된 후 영향을 받는 행의 수 또는 백분율을 지정합니다. 이 절은 TOP 조인된 행 수를 지정된 값으로 더 줄이며 삽입, 업데이트 또는 삭제 작업은 순서가 지정되지 않은 방식으로 나머지 조인된 행에 적용됩니다. 즉, 절에 정의된 작업 간에 행이 분산되는 순서는 WHEN 없습니다. 예를 들어 지정하면 TOP (10) 행 10개에 영향을 줍니다. 이러한 행 중 7개는 업데이트되고 3개는 삽입되거나 1개는 삭제되고 5개는 업데이트되고 4개는 삽입될 수 있습니다.
이 절을 TOP 사용하여 대량 테이블에 대한 DML(데이터 조작 언어) 작업을 일괄 처리로 수행하는 것이 일반적입니다. 이 목적을 위해 문에 절을 TOP 사용하는 MERGE 경우 다음과 같은 의미를 이해하는 것이 중요합니다.
I/O 성능이 영향을 받을 수 있습니다.
이 문은
MERGE원본 테이블과 대상 테이블 모두에 대한 전체 테이블 검사를 수행합니다. 작업을 일괄 처리로 분할하면 일괄 처리당 수행되는 쓰기 작업의 수가 줄어듭니다. 그러나 각 일괄 처리는 원본 및 대상 테이블의 전체 테이블 검색을 수행합니다. 결과 읽기 작업은 테이블의 쿼리 및 기타 동시 작업의 성능에 영향을 줄 수 있습니다.잘못된 결과가 발생할 수 있습니다.
연속된 모든 일괄 처리는 새로운 행을 대상으로 해야 합니다. 그렇지 않은 경우 대상 테이블에 중복 행을 잘못 삽입하는 등의 원하지 않는 동작이 발생할 수 있습니다. 이러한 현상은 대상 일괄 처리에는 없었지만 전체 대상 테이블에는 있었던 행이 원본 테이블에 포함되는 경우 발생할 수 있습니다. 정확한 결과를 보장하려면 다음과 같이 합니다.
- 절을
ON사용하여 기존 대상 행에 영향을 미치는 원본 행과 새로운 원본 행을 결정합니다. - 절의 추가 조건을 사용하여 대상 행이
WHEN MATCHED이전 일괄 처리에 의해 이미 업데이트되었는지 확인합니다. - 절 및
WHEN MATCHED논리에SET추가 조건을 사용하여 동일한 행을 두 번 업데이트할 수 없는지 확인합니다.
- 절을
TOP 절은 이러한 절이 적용된 후에만 적용되므로 각 실행은 일치하지 않는 행을 삽입하거나 기존 행 하나를 업데이트합니다.
대량 로드 모범 사례
이 문은 MERGE 절을 테이블 원본으로 지정하여 원본 데이터 파일에서 대상 테이블로 데이터를 효율적으로 대량 로드하는 OPENROWSET(BULK...) 데 사용할 수 있습니다. 이렇게 하면 전체 파일이 하나의 일괄 처리에서 처리됩니다.
대량 병합 프로세스의 성능을 높이려면 다음 지침을 따르는 것이 좋습니다.
대상 테이블의 조인 열에 클러스터형 인덱스를 만듭니다.
대량 로드
MERGE중에 대상 테이블에서 고유하지 않은 다른 비클러스터형 인덱스를 사용하지 않도록 설정하고 나중에 사용하도록 설정합니다. 이 작업은 일반적이며 야간 대량 데이터 작업에 유용합니다.절의
ORDER및UNIQUE힌트를OPENROWSET(BULK...)사용하여 원본 데이터 파일의 정렬 방법을 지정합니다.기본적으로 대량 작업은 데이터 파일이 정렬되지 않았음을 전제로 합니다. 따라서 원본 데이터는 대상 테이블의 클러스터형 인덱스에 따라 정렬되고
ORDER쿼리 최적화 프로그램에서 보다 효율적인 쿼리 계획을 생성할 수 있도록 순서를 나타내는 데 힌트를 사용하는 것이 중요합니다. 힌트는 런타임에 유효성이 검사됩니다. 데이터 스트림이 지정된 힌트를 따르지 않으면 오류가 발생합니다.
이러한 지침을 통해 조인 키의 고유성을 확보하고 원본 파일의 데이터 정렬 순서가 대상 테이블과 일치하도록 할 수 있습니다. 추가 정렬 작업이 필요 없고 불필요한 데이터 복사가 없으므로 쿼리 성능이 향상됩니다.
MERGE 성능 측정 및 진단
명령문의 MERGE 성능을 측정하고 진단하는 데 도움이 되는 기능은 다음과 같습니다.
-
sys.dm_exec_query_optimizer_info 동적 관리 뷰의 병합 stmt 카운터를 사용하여 문에 대한
MERGE쿼리 최적화 수를 반환합니다. -
merge_action_typesys.dm_exec_plan_attributes 동적 관리 뷰의 특성을 사용하여 문의 결과로 사용되는 트리거 실행 계획의 형식을MERGE반환합니다. - 확장 이벤트 세션을 사용하여 다른 DML(데이터 조작 언어) 문과 동일한 방식으로 문에 대한 문제 해결 데이터를
MERGE수집합니다. 확장 이벤트 개요에 대한 자세한 내용은 빠른 시작: SQL Server의 확장 이벤트 및 SSMS XEvent Profiler 사용을 참조하세요.
Examples
A. MERGE를 사용하여 단일 문의 테이블에 대해 INSERT 및 UPDATE 작업을 수행합니다.
일반적인 시나리오에서는 일치하는 행이 있으면 테이블에서 하나 이상의 열을 업데이트합니다. 또는 일치하는 행이 없으면 새 행으로 데이터를 테이블에 삽입합니다. 일반적으로 적절한 UPDATE 문과 INSERT 문이 포함된 저장 프로시저에 매개 변수를 전달하여 두 시나리오를 모두 수행합니다. 문을 MERGE 사용하면 단일 문에서 두 작업을 모두 수행할 수 있습니다. 다음 예제에서는 문과 문을 모두 포함하는 AdventureWorks2022 데이터베이스의 저장 프로시저를 INSERTUPDATE 보여 줍니다. 그런 다음 단일 MERGE 문을 사용하여 해당 작업을 실행하도록 프로시저를 수정합니다.
CREATE PROCEDURE dbo.InsertUnitMeasure @UnitMeasureCode NCHAR(3), @Name NVARCHAR(25)
AS
BEGIN
SET NOCOUNT ON;
-- Update the row if it exists.
UPDATE Production.UnitMeasure
SET Name = @Name
WHERE UnitMeasureCode = @UnitMeasureCode
-- Insert the row if the UPDATE statement failed.
IF (@@ROWCOUNT = 0)
BEGIN
INSERT INTO Production.UnitMeasure (
UnitMeasureCode,
Name
)
VALUES (@UnitMeasureCode, @Name)
END
END;
GO
-- Test the procedure and return the results.
EXEC InsertUnitMeasure @UnitMeasureCode = 'ABC', @Name = 'Test Value';
SELECT UnitMeasureCode, Name
FROM Production.UnitMeasure
WHERE UnitMeasureCode = 'ABC';
GO
-- Rewrite the procedure to perform the same operations using the
-- MERGE statement.
-- Create a temporary table to hold the updated or inserted values
-- from the OUTPUT clause.
CREATE TABLE #MyTempTable (
ExistingCode NCHAR(3),
ExistingName NVARCHAR(50),
ExistingDate DATETIME,
ActionTaken NVARCHAR(10),
NewCode NCHAR(3),
NewName NVARCHAR(50),
NewDate DATETIME
);
GO
ALTER PROCEDURE dbo.InsertUnitMeasure @UnitMeasureCode NCHAR(3),
@Name NVARCHAR(25)
AS
BEGIN
SET NOCOUNT ON;
MERGE Production.UnitMeasure AS tgt
USING (SELECT @UnitMeasureCode, @Name) AS src(UnitMeasureCode, Name)
ON (tgt.UnitMeasureCode = src.UnitMeasureCode)
WHEN MATCHED
THEN
UPDATE
SET Name = src.Name
WHEN NOT MATCHED
THEN
INSERT (UnitMeasureCode, Name)
VALUES (src.UnitMeasureCode, src.Name)
OUTPUT deleted.*,
$action,
inserted.*
INTO #MyTempTable;
END;
GO
-- Test the procedure and return the results.
EXEC InsertUnitMeasure @UnitMeasureCode = 'ABC', @Name = 'New Test Value';
EXEC InsertUnitMeasure @UnitMeasureCode = 'XYZ', @Name = 'Test Value';
EXEC InsertUnitMeasure @UnitMeasureCode = 'ABC', @Name = 'Another Test Value';
SELECT * FROM #MyTempTable;
-- Cleanup
DELETE FROM Production.UnitMeasure
WHERE UnitMeasureCode IN ('ABC', 'XYZ');
DROP TABLE #MyTempTable;
GO
CREATE PROCEDURE dbo.InsertUnitMeasure @UnitMeasureCode NCHAR(3),
@Name NVARCHAR(25)
AS
BEGIN
SET NOCOUNT ON;
-- Update the row if it exists.
UPDATE Production.UnitMeasure
SET Name = @Name
WHERE UnitMeasureCode = @UnitMeasureCode
-- Insert the row if the UPDATE statement failed.
IF (@@ROWCOUNT = 0)
BEGIN
INSERT INTO Production.UnitMeasure (
UnitMeasureCode,
Name
)
VALUES (@UnitMeasureCode, @Name)
END
END;
GO
-- Test the procedure and return the results.
EXEC InsertUnitMeasure @UnitMeasureCode = 'ABC', @Name = 'Test Value';
SELECT UnitMeasureCode, Name
FROM Production.UnitMeasure
WHERE UnitMeasureCode = 'ABC';
GO
-- Rewrite the procedure to perform the same operations using the
-- MERGE statement.
ALTER PROCEDURE dbo.InsertUnitMeasure @UnitMeasureCode NCHAR(3),
@Name NVARCHAR(25)
AS
BEGIN
SET NOCOUNT ON;
MERGE Production.UnitMeasure AS tgt
USING (
SELECT @UnitMeasureCode,
@Name
) AS src(UnitMeasureCode, Name)
ON (tgt.UnitMeasureCode = src.UnitMeasureCode)
WHEN MATCHED
THEN
UPDATE SET Name = src.Name
WHEN NOT MATCHED
THEN
INSERT (UnitMeasureCode, Name)
VALUES (src.UnitMeasureCode, src.Name);
END;
GO
-- Test the procedure and return the results.
EXEC InsertUnitMeasure @UnitMeasureCode = 'ABC', @Name = 'New Test Value';
EXEC InsertUnitMeasure @UnitMeasureCode = 'XYZ', @Name = 'Test Value';
EXEC InsertUnitMeasure @UnitMeasureCode = 'ABC', @Name = 'Another Test Value';
-- Cleanup
DELETE FROM Production.UnitMeasure
WHERE UnitMeasureCode IN ('ABC', 'XYZ');
GO
B. MERGE를 사용하여 단일 문에서 테이블에 대한 UPDATE 및 DELETE 작업을 수행합니다.
다음 예제에서는 테이블에서 처리되는 주문에 따라 AdventureWorks2022 샘플 데이터베이스의 MERGE 테이블을 매일 업데이트 ProductInventory 하는 데 사용합니다SalesOrderDetail.
Quantity 테이블의 ProductInventory 열은 SalesOrderDetail 테이블에서 매일 제품별로 접수되는 주문의 수를 빼는 방식으로 업데이트됩니다. 제품에 대한 주문 수로 인해 제품의 재고 수준이 0 이하로 떨어지면 해당 제품에 대한 행이 ProductInventory 테이블에서 삭제됩니다.
CREATE PROCEDURE Production.usp_UpdateInventory @OrderDate DATETIME
AS
MERGE Production.ProductInventory AS tgt
USING (
SELECT ProductID,
SUM(OrderQty)
FROM Sales.SalesOrderDetail AS sod
INNER JOIN Sales.SalesOrderHeader AS soh
ON sod.SalesOrderID = soh.SalesOrderID
AND soh.OrderDate = @OrderDate
GROUP BY ProductID
) AS src(ProductID, OrderQty)
ON (tgt.ProductID = src.ProductID)
WHEN MATCHED
AND tgt.Quantity - src.OrderQty <= 0
THEN
DELETE
WHEN MATCHED
THEN
UPDATE
SET tgt.Quantity = tgt.Quantity - src.OrderQty,
tgt.ModifiedDate = GETDATE()
OUTPUT $action,
Inserted.ProductID,
Inserted.Quantity,
Inserted.ModifiedDate,
Deleted.ProductID,
Deleted.Quantity,
Deleted.ModifiedDate;
GO
EXECUTE Production.usp_UpdateInventory '20030501';
CREATE PROCEDURE Production.usp_UpdateInventory @OrderDate DATETIME
AS
MERGE Production.ProductInventory AS tgt
USING (
SELECT ProductID,
SUM(OrderQty)
FROM Sales.SalesOrderDetail AS sod
INNER JOIN Sales.SalesOrderHeader AS soh
ON sod.SalesOrderID = soh.SalesOrderID
AND soh.OrderDate = @OrderDate
GROUP BY ProductID
) AS src(ProductID, OrderQty)
ON (tgt.ProductID = src.ProductID)
WHEN MATCHED
AND tgt.Quantity - src.OrderQty <= 0
THEN
DELETE
WHEN MATCHED
THEN
UPDATE
SET tgt.Quantity = tgt.Quantity - src.OrderQty,
tgt.ModifiedDate = GETDATE();
GO
EXECUTE Production.usp_UpdateInventory '20030501';
C. MERGE를 사용하여 파생 원본 테이블을 사용하여 대상 테이블에서 UPDATE 및 INSERT 작업을 수행합니다.
다음 예제에서는 행을 업데이트하거나 삽입하여 AdventureWorks2022 데이터베이스의 테이블을 수정 MERGE 하는 데 사용합니다SalesReason.
원본 테이블의 NewName 값이 대상 테이블 Name의 SalesReason 열에 있는 값과 일치하는 경우 대상 테이블에서 ReasonType 열이 업데이트됩니다.
NewName 값이 일치하지 않으면 원본 행이 대상 테이블에 삽입됩니다. 원본 테이블은 Transact-SQL 테이블 값 생성자를 사용하여 원본 테이블의 여러 행을 지정하는 파생 테이블입니다. 파생 테이블에서 테이블 값 생성자를 사용하는 방법에 대한 자세한 내용은 테이블 값 생성자(Transact-SQL)를 참조하세요.
이 절은 OUTPUT 문의 결과를 MERGE 쿼리하는 데 유용할 수 있습니다. 자세한 내용은 OUTPUT 절(Transact-SQL)을 참조하세요. 이 예제에서는 절의 결과를 테이블 변수에 OUTPUT 저장하는 방법도 보여줍니다. 그런 다음 삽입 및 업데이트된 행 수를 MERGE 반환하는 간단한 선택 작업을 실행하여 문의 결과를 요약합니다.
-- Create a temporary table variable to hold the output actions.
DECLARE @SummaryOfChanges TABLE (Change VARCHAR(20));
MERGE INTO Sales.SalesReason AS tgt
USING (
VALUES ('Recommendation', 'Other'),
('Review', 'Marketing'),
('Internet', 'Promotion')
) AS src(NewName, NewReasonType)
ON tgt.Name = src.NewName
WHEN MATCHED
THEN
UPDATE
SET ReasonType = src.NewReasonType
WHEN NOT MATCHED BY TARGET
THEN
INSERT (Name, ReasonType)
VALUES (NewName, NewReasonType)
OUTPUT $action
INTO @SummaryOfChanges;
-- Query the results of the table variable.
SELECT Change,
COUNT(*) AS CountPerChange
FROM @SummaryOfChanges
GROUP BY Change;
원본 테이블의 NewName 값이 대상 테이블 Name의 SalesReason 열에 있는 값과 일치하는 경우 대상 테이블에서 ReasonType 열이 업데이트됩니다.
NewName 값이 일치하지 않으면 원본 행이 대상 테이블에 삽입됩니다. 원본 테이블은 SELECT ... UNION ALL을 사용하여 원본 테이블의 여러 행을 지정하는 파생 테이블입니다.
MERGE INTO Sales.SalesReason AS tgt
USING (
SELECT 'Recommendation', 'Other'
UNION ALL
SELECT 'Review', 'Marketing'
UNION ALL
SELECT 'Internet', 'Promotion'
) AS src(NewName, NewReasonType)
ON tgt.Name = src.NewName
WHEN MATCHED
THEN
UPDATE SET ReasonType = src.NewReasonType
WHEN NOT MATCHED BY TARGET
THEN
INSERT (Name, ReasonType)
VALUES (NewName, NewReasonType);
D. MERGE 문의 결과를 다른 테이블에 삽입합니다.
다음 예제에서는 문 절 OUTPUT 에서 반환된 MERGE 데이터를 캡처하고 해당 데이터를 다른 테이블에 삽입합니다. 이 문은 MERGE 테이블에서 처리되는 주문에 따라 AdventureWorks2022 데이터베이스의 Quantity 테이블 열을 ProductInventory 업데이트 SalesOrderDetail 합니다. 이 예에서는 업데이트된 행을 캡처하여 재고 변경 내용을 추적하는 데 사용되는 다른 테이블에 삽입합니다.
CREATE TABLE Production.UpdatedInventory (
ProductID INT NOT NULL,
LocationID INT,
NewQty INT,
PreviousQty INT,
CONSTRAINT PK_Inventory PRIMARY KEY CLUSTERED (
ProductID,
LocationID
)
);
GO
INSERT INTO Production.UpdatedInventory
SELECT ProductID, LocationID, NewQty, PreviousQty
FROM (
MERGE Production.ProductInventory AS pi
USING (
SELECT ProductID, SUM(OrderQty)
FROM Sales.SalesOrderDetail AS sod
INNER JOIN Sales.SalesOrderHeader AS soh
ON sod.SalesOrderID = soh.SalesOrderID
AND soh.OrderDate BETWEEN '20030701'
AND '20030731'
GROUP BY ProductID
) AS src(ProductID, OrderQty)
ON pi.ProductID = src.ProductID
WHEN MATCHED
AND pi.Quantity - src.OrderQty >= 0
THEN
UPDATE SET pi.Quantity = pi.Quantity - src.OrderQty
WHEN MATCHED
AND pi.Quantity - src.OrderQty <= 0
THEN
DELETE
OUTPUT $action,
Inserted.ProductID,
Inserted.LocationID,
Inserted.Quantity AS NewQty,
Deleted.Quantity AS PreviousQty
) AS Changes(Action, ProductID, LocationID, NewQty, PreviousQty)
WHERE Action = 'UPDATE';
GO
E. MERGE를 사용하여 그래프 데이터베이스의 대상 에지 테이블에서 INSERT 또는 UPDATE를 수행합니다.
이 예제에서는 노드 테이블 Person 및 City와 에지 테이블 livesIn을 만듭니다. 에지와 MERGE 에지 사이에 가장자리가 livesIn 아직 없는 경우 에지에서 문을 사용하고 새 행을 PersonCity삽입합니다. 에지가 이미 있는 경우 livesIn 에지에 대해 StreetAddress 특성만 업데이트하면 됩니다.
-- CREATE node and edge tables
CREATE TABLE Person
(
ID INTEGER PRIMARY KEY,
PersonName VARCHAR(100)
)
AS NODE
GO
CREATE TABLE City
(
ID INTEGER PRIMARY KEY,
CityName VARCHAR(100),
StateName VARCHAR(100)
)
AS NODE
GO
CREATE TABLE livesIn
(
StreetAddress VARCHAR(100)
)
AS EDGE
GO
-- INSERT some test data into node and edge tables
INSERT INTO Person VALUES (1, 'Ron'), (2, 'David'), (3, 'Nancy')
GO
INSERT INTO City VALUES (1, 'Redmond', 'Washington'), (2, 'Seattle', 'Washington')
GO
INSERT livesIn SELECT P.$node_id, C.$node_id, c
FROM Person P, City C, (values (1,1, '123 Avenue'), (2,2,'Main Street')) v(a,b,c)
WHERE P.id = a AND C.id = b
GO
-- Use MERGE to update/insert edge data
CREATE OR ALTER PROCEDURE mergeEdge
@PersonId integer,
@CityId integer,
@StreetAddress varchar(100)
AS
BEGIN
MERGE livesIn
USING ((SELECT @PersonId, @CityId, @StreetAddress) AS T (PersonId, CityId, StreetAddress)
JOIN Person ON T.PersonId = Person.ID
JOIN City ON T.CityId = City.ID)
ON MATCH (Person-(livesIn)->City)
WHEN MATCHED THEN
UPDATE SET StreetAddress = @StreetAddress
WHEN NOT MATCHED THEN
INSERT ($from_id, $to_id, StreetAddress)
VALUES (Person.$node_id, City.$node_id, @StreetAddress) ;
END
GO
-- Following will insert a new edge in the livesIn edge table
EXEC mergeEdge 3, 2, '4444th Avenue'
GO
-- Following will update the StreetAddress on the edge that connects Ron to Redmond
EXEC mergeEdge 1, 1, '321 Avenue'
GO
-- Verify that all the address were added/updated correctly
SELECT PersonName, CityName, StreetAddress
FROM Person , City , livesIn
WHERE MATCH(Person-(livesIn)->city)
GO