次の方法で共有


MERGE (Transact-SQL)

適用対象:SQL ServerAzure SQL DatabaseAzure SQL Managed InstanceAzure Synapse Analytics (専用 SQL プールのみ)Microsoft Fabric プレビューの SQL データベースMicrosoft Fabric のウェアハウス

MERGE ステートメントは、ソース テーブルとの結合の結果から、ターゲット テーブルに対して挿入、更新、または削除操作を実行します。 たとえば、他のテーブルとの違いに基づいて、あるテーブル内の行を挿入、更新、または削除することにより、2 つのテーブルを同期します。

この記事では、選択した製品バージョンに基づいて、さまざまな構文、引数、解説、アクセス許可、および例を示します。 バージョン ドロップダウン リストから目的の製品バージョンを選択します。

Note

Fabric Data Warehouse では、 MERGE はプレビュー段階です。

Transact-SQL 構文表記規則

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

WITH <common_table_expression>

MERGE ステートメントのスコープ内で定義される一時的な名前付き結果セットまたはビュー (共通テーブル式とも呼ばれます) を指定します。 結果セットは単純なクエリから派生し、 MERGE ステートメントによって参照されます。 詳細については、「WITH common_table_expression (Transact-SQL)」を参照してください。

TOP ( ) [ PERCENT ]

影響を受ける行の数またはパーセンテージを指定します。 expression には、行数または行のパーセンテージを指定できます。 TOP式で参照される行は、順序に合いません。 詳しくは、「TOP (Transact-SQL)」をご覧ください。

TOP句は、ソース テーブル全体とターゲット テーブル全体の結合と、挿入、更新、または削除アクションの対象ではない結合された行が削除された後に適用されます。 TOP句を使用すると、結合された行の数が指定した値に減ります。 これらの操作 (挿入、更新、または削除) は、残りの結合された行に対して、順不同の方法で適用されます。 つまり、 WHEN 句で定義されているアクション間で行が分散される順序はありません。 たとえば、 TOP (10) を指定すると、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は、MERGE ステートメントのWHEN句で指定された挿入、更新、または削除操作のターゲットです。

target_table がビューの場合、これに対するアクションはビューの更新条件を満たす必要があります。 詳細については、「ビューを使用したデータ変更」を参照してください。

target_table にリモート テーブルを指定することはできません。 target_table にルールを定義することはできません。target_table はメモリ最適化テーブルになることはできません。

ヒントは、<merge_hint> として指定できます。

<merge_hint> は Azure Synapse Analytics ではサポートされていません。

[ AS ] table_alias

target_table のテーブルを参照するための代替名です。

<table_sourceの使用>

に基づいて <merge_search_condition> 内のデータ行と照合するデータ ソースを指定します。 この一致の結果は、MERGE ステートメントのWHEN句によって実行するアクションを指示します。 <table_source> には、リモート テーブルを指定することも、リモート テーブルにアクセスする派生テーブルを指定することもできます。

<table_source> には派生テーブルを指定できます。このテーブルでは、複数の行を指定してテーブルを作成するために Transact-SQL テーブル値コンストラクターが使用されます。

<table_source> には派生テーブルを指定できます。このテーブルでは、複数の行を指定してテーブルを作成する SELECT ... UNION ALL を使用します。

[ AS ] table_alias

table_source のテーブルを参照するための代替名です。

この句の構文および引数の詳細については、「FROM (Transact-SQL)」を参照してください。

ON <merge_search_condition>

<table_source>target_table を結合するための一致箇所を特定する条件を指定します。

Caution

照合目的で使用する対象テーブルの列だけを指定することが重要です。 つまり、対象テーブルのうち、ソース テーブルの対応する列と比較する列を指定します。 AND NOT target_table.column_x = valueの指定など、ON句のターゲット テーブルの行をフィルターで除外して、クエリのパフォーマンスを向上させないでください。 この場合、予期しない無効な結果が返される可能性があります。

一致すると、 <merge_matched>

<table_source> ON <merge_search_condition>によって返される行と一致し、追加の検索条件を満たす *target_table のすべての行を、<merge_matched>句に従って更新または削除することを指定します。

MERGE ステートメントには、最大で 2 つのWHEN MATCHED句を含めることができます。 2 つの句を指定する場合は、最初の句に AND<search_condition> 句を伴う必要があります。 特定の行に対して、2 番目の WHEN MATCHED 句は、最初の行が適用されていない場合にのみ適用されます。 2 つの WHEN MATCHED 句がある場合、1 つは UPDATE アクションを指定し、1 つは DELETE アクションを指定する必要があります。 <merge_matched>句でUPDATEを指定し、<merge_search_condition>に基づいて複数の行の<table_source>がtarget_tableの行と一致すると、SQL Server はエラーを返します。 MERGEステートメントは、同じ行を複数回更新したり、同じ行を更新および削除したりすることはできません。

[BY TARGET] に一致しない場合は、 <merge_not_matched>

target_tableの行と一致しないが、追加の検索条件 (存在する場合) を満たす<table_source> ON <merge_search_condition>によって返されるすべての行について、行をtarget_tableに挿入することを指定します。 挿入する値は、<merge_not_matched> 句で指定します。 MERGE ステートメントに含めることができるWHEN NOT MATCHED [ BY TARGET ]句は 1 つだけです。

ソースで一致しない場合は、 <merge_matched>

<table_source> ON <merge_search_condition>によって返される行と一致せず、追加の検索条件を満たす *target_table のすべての行を、<merge_matched>句に従って更新または削除することを指定します。

MERGE ステートメントには、最大で 2 つのWHEN NOT MATCHED BY SOURCE句を含めることができます。 2 つの句を指定する場合は、最初の句に AND<clause_search_condition> 句を伴う必要があります。 特定の行に対して、2 番目の WHEN NOT MATCHED BY SOURCE 句は、最初の行が適用されていない場合にのみ適用されます。 2 つの WHEN NOT MATCHED BY SOURCE 句がある場合、1 つは UPDATE アクションを指定し、1 つは DELETE アクションを指定する必要があります。 <clause_search_condition> では対象テーブルの列のみを参照できます。

<table_source> から返される行がない場合、ソース テーブルの列にアクセスすることはできません。 <merge_matched> 句に指定した更新操作または削除操作でソース テーブルの列を参照していると、エラー 207 (無効な列名) が返されます。 たとえば、ソース テーブルの WHEN NOT MATCHED BY SOURCE THEN UPDATE SET TargetTable.Col1 = SourceTable.Col1 にアクセスできないために、Col1 句によってステートメントが失敗する可能性があります。

AND <clause_search_condition>

任意の有効な検索条件を指定します。 詳細については、 検索条件 (Transact-SQL) を参照してください。

<table_hint_limited>

MERGE ステートメントによって実行される挿入、更新、または削除の各アクションについて、ターゲット テーブルに適用する 1 つ以上のテーブル ヒントを指定します。 WITHキーワードとかっこが必要です。

NOLOCKREADUNCOMMITTED は許可されません。 テーブル ヒントの詳細については、「 テーブル ヒント (Transact-SQL)」を参照してください。

INSERT ステートメントのターゲットであるテーブルに対してTABLOCK ヒントを指定すると、TABLOCKX ヒントを指定する場合と同じ効果があります。 テーブルに対して、排他ロックが取得されます。 FORCESEEK を指定すると、ソース テーブルに結合された対象テーブルの暗黙のインスタンスに対して適用されます。

Caution

WHEN NOT MATCHED [ BY TARGET ] THEN INSERTREADPASTを指定すると、UNIQUE制約に違反するINSERT操作が発生する可能性があります。

INDEX ( index_val [ ,...n ] )

ソース テーブルとの暗黙の結合を実行するための、対象テーブルの 1 つ以上のインデックスの名前または ID を指定します。 詳細については、「テーブル ヒント (Transact-SQL)」を参照してください。

<output_clause>

target_table 内の更新、挿入、または削除される行ごとに 1 行を返します。この場合、特定の順序はありません。 $action は、OUTPUT 句に指定することができます。 $action は、それぞれの行に対して実行されたアクションに従って、次のいずれかの値をその行について返す nvarchar(10) 型の列です: INSERTUPDATE、または DELETEOUTPUT句は、MERGEの影響を受ける行を照会またはカウントするための推奨される方法です。 この句の引数と動作の詳細については、「 OUTPUT 句 (Transact-SQL)」を参照してください。

OPTION ( <query_hint> [ ,...n ] )

オプティマイザー ヒントを使用して、データベース エンジンがステートメントを処理する方法をカスタマイズすることを指定します。 詳細については、「 クエリ ヒント (Transact-SQL)」を参照してください。

<merge_matched>

によって返される行と一致せず、追加の検索条件を満たす<table_source> ON <merge_search_condition>のすべての行に適用される更新または削除アクションを指定します。

UPDATE SET <set_clause>

対象テーブル内で更新する列名または変数名の一覧と、それらの更新に使用する値を指定します。

この句の引数について詳しくは、「UPDATE (Transact-SQL)」をご覧ください。 列と同じ値を変数に設定することはできません。

DELETE

target_table 内の行に一致する行を削除するように指定します。

<merge_not_matched>

対象テーブルに挿入する値を指定します。

(column_list)

対象テーブルのデータを挿入する 1 つ以上の列で構成されるリストを指定します。 列は単一部分の名前として指定する必要があります。指定しない場合は、 MERGE ステートメントが失敗します。 column_list はかっこで囲み、コンマで区切る必要があります。

VALUES ( values_list)

対象テーブルに挿入する値を返す定数、変数、または式を、コンマ区切りのリストで指定します。 式に EXECUTE ステートメントを含めることはできません。

既定値

挿入される行が、各列に対して定義されている既定値で構成されることを指定します。

この句について詳しくは、「INSERT (Transact-SQL)」をご覧ください。

<search_condition>

<merge_search_condition> または <clause_search_condition> を指定する検索条件を指定します。 この句の引数の詳細については、「 検索条件 (Transact-SQL)」を参照してください。

<グラフの検索パターン>

グラフの一致パターンを指定します。 この句の引数の詳細については、「MATCH (Transact-SQL)」を参照してください

Remarks

MERGE ステートメントで説明されている条件付き動作は、2 つのテーブルに一致する特性が複雑に混在している場合に最適です。 たとえば、存在しない場合は行を挿入し、一致する場合は行を更新します。 別のテーブルの行に基づいて 1 つのテーブルを更新する場合は、 INSERTUPDATE、および 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);

3 つの MATCHED 句のうち少なくとも 1 つを指定する必要がありますが、任意の順序で指定できます。 同じ MATCHED 句で変数を複数回更新することはできません。

MERGE ステートメントによってターゲット テーブルに指定された挿入、更新、または削除アクションは、連鎖参照整合性制約を含め、それに定義されている制約によって制限されます。 ターゲット テーブルの一意のインデックスに対して IGNORE_DUP_KEYON されている場合、 MERGE はこの設定を無視します。

MERGE ステートメントには、ステートメント ターミネータとしてセミコロン (;) が必要です。 エラー 10713 は、ターミネータなしで MERGE ステートメントを実行すると発生します。

MERGE後に使用すると、@@ROWCOUNT (Transact-SQL) は、クライアントに挿入、更新、および削除された行の合計数を返します。

MERGE は、データベース互換性レベルが 100 以上に設定されている場合に、完全に予約されたキーワードです。 MERGE ステートメントは、90100の両方のデータベース互換性レベルで使用できますが、データベース互換性レベルが 90 に設定されている場合、キーワードは完全には予約されていません。

Caution

キュー更新レプリケーションを使用する場合は、MERGE ステートメントを使用しないでください。 MERGEとキュー更新トリガーには互換性がありません。 MERGEステートメントをINSERTおよびUPDATEステートメントに置き換えます。

Azure Synapse Analytics に関する考慮事項

Azure Synapse Analytics では、 MERGE コマンドは SQL Server と Azure SQL Database と比較して次の違いがあります。

  • MERGEを使用したディストリビューション キー列の更新は、10.0.17829.0 より前のビルドではサポートされていません。 一時停止または強制的にアップグレードできない場合は、バージョン 10.0.17829.0 まで、ANSI UPDATE FROM ... JOIN ステートメントを回避策として使用してください。
  • MERGE更新は、削除と挿入のペアとして実装されます。 MERGE更新の影響を受ける行数には、削除された行と挿入された行が含まれます。
  • MERGE...WHEN NOT MATCHED INSERT は、 IDENTITY 列を持つテーブルではサポートされていません。
  • テーブル値コンストラクターは、ソース テーブルの USING 句では使用できません。 複数の行を使って派生ソース テーブルを作成するには、SELECT ... UNION ALL を使用します。
  • 各種の分散タイプでのテーブルのサポートについては、次の表で説明しています。
Azure Synapse Analytics での MERGE CLAUSE サポートされている TARGET 配布テーブル サポートされる SOURCE 分散テーブル Comment
WHEN MATCHED すべての分散タイプ すべての分散タイプ
NOT MATCHED BY TARGET HASH すべての分散タイプ 2 つのテーブルを同期するために UPDATE/DELETE FROM...JOIN を使用します。
NOT MATCHED BY SOURCE すべての分散タイプ すべての分散タイプ

Tip

分散ハッシュ キーをMERGEJOIN列として使用し、等価比較のみを実行している場合は、WHEN MATCHED THEN UPDATE SET句の列の一覧から配布キーを省略できます。これは冗長な更新であるためです。

Azure Synapse Analytics では、10.0.17829.0 より前のビルドの MERGE コマンドは、特定の条件下でターゲット テーブルを不整合な状態のままにし、行が間違ったディストリビューションに配置され、後のクエリで間違った結果が返される場合があります。 この問題は、次の 2 つのケースで発生する可能性があります。

Scenario Comment
ケース 1
セカンダリ インデックスまたはUNIQUE制約を含む HASH 分散TARGET テーブルでMERGEを使用する。
- Synapse SQL 10.0.15563.0 以降のバージョンで修正されました。
- SELECT @@VERSION が 10.0.15563.0 より低いバージョンを返す場合は、Synapse SQL プールを手動で一時停止して再開し、この修正プログラムを取得します。
- Synapse SQL プールに修正プログラムが適用されるまでは、セカンダリ インデックスまたはUNIQUE制約を持つ分散TARGET テーブルHASHMERGE コマンドを使用しないでください。
ケース 2
MERGE を使用して、ハッシュ分散テーブルの分散キー列を更新する。
- Synapse SQL 10.0.17829.0 以降のバージョンで修正されました。
- SELECT @@VERSION が 10.0.17829.0 より低いバージョンを返す場合は、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';

MERGEHASH分散テーブルがケース 1 またはケース 2 の影響を受けるかどうかを確認するには、次の手順に従って、テーブルの行が間違ったディストリビューションに配置されているかどうかを調べます。 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

特定のシナリオでは、ターゲット テーブルまたはソース テーブルに 1,024 列がない場合でも、 MERGE ステートメントによってエラー CREATE TABLE failed because column <> in table <> exceeds the maximum of 1024 columns.が発生することがあります。 このシナリオは、以下の条件のいずれかが満たされたときに発生する可能性があります。

  • MERGE内のDELETEUPDATE SET、またはINSERT操作で複数の列が指定されます (WHEN [NOT] MATCHED句に固有ではありません)。
  • JOIN条件の列に非クラスター化インデックス (NCI) があります
  • ターゲット テーブルが分散HASH

このエラーが見つかった場合、推奨される回避策は次のとおりです。

  • JOIN列から非クラスター化インデックス (NCI) を削除するか、NCI を使用せずに列に結合します。 基になるテーブルを後で更新して、 JOIN 列に NCI を含める場合、 MERGE ステートメントは実行時にこのエラーの影響を受ける可能性があります。 詳細については、「DROP INDEX」を参照してください。
  • MERGEの代わりに UPDATEDELETEINSERT ステートメントを使用します。

トリガーの実装

MERGE ステートメントで指定されたすべての挿入、更新、または削除アクションについて、SQL Server はターゲット テーブルで定義されている対応するAFTER トリガーを起動しますが、トリガーを最初または最後に起動するアクションは保証されません。 同じ操作に対して定義された複数のトリガーは、指定した順序に従います。 トリガー起動順序の設定の詳細については、「最初と最後のトリガーの指定」を参照してください。

ターゲット テーブルに、MERGE ステートメントによって実行される挿入、更新、または削除アクションに対して定義された有効な INSTEAD OF トリガーがある場合は、MERGE ステートメントで指定されたすべてのアクションに対して OF トリガーINSTEAD有効にする必要があります。

INSTEAD OF UPDATEまたは OF トリガーがtarget_tableで定義されている場合、更新操作または削除操作は実行されません。 代わりにトリガーが起動され、inserted テーブルと deleted テーブルに適切なデータが設定されます。

target_tableで OF トリガーが定義されている場合、挿入操作は実行されません。 代わりに、テーブルに適切なデータが設定されます。

Note

個別の INSERTUPDATE、および DELETE ステートメントとは異なり、トリガー内の @@ROWCOUNT によって反映される行の数が多くなる可能性があります。 AFTER トリガー内の@@ROWCOUNT (トリガーがキャプチャするデータ変更ステートメントに関係なく) には、MERGEの影響を受ける行の合計数が反映されます。 たとえば、MERGE ステートメントが 1 つの行を挿入し、1 行を更新し、1 行を削除した場合、トリガーがINSERTステートメントに対してのみ宣言されている場合でも、@@ROWCOUNTは任意のAFTERトリガーに対して 3 つになります。

Permissions

ソース テーブル SELECT アクセス許可と、ターゲット テーブルに対する INSERTUPDATE、または DELETE のアクセス許可が必要です。 詳細については、 SELECT (Transact-SQL)INSERT (Transact-SQL)UPDATE (Transact-SQL)DELETE (Transact-SQL) に関する記事の「アクセス許可」セクションを参照してください。

インデックスに関するベスト プラクティス

MERGE ステートメントを使用すると、個々の DML ステートメントを 1 つのステートメントに置き換えることができます。 これにより、操作が単一のステートメント内で実行されてソース テーブルと対象テーブルのデータの処理回数が最小限に抑えられるので、クエリのパフォーマンスが向上します。 ただし、パフォーマンスが向上するかどうかは、インデックスが正しいか、結合が存在するかなど、いくつかの考慮事項によって決まります。

MERGE ステートメントのパフォーマンスを向上させるには、次のインデックス ガイドラインをお勧めします。

  • MERGEのソースとターゲットの間の結合を容易にするインデックスを作成します。
    • ターゲット テーブルへの結合ロジックに対応するキーを持つソース テーブルの結合列に、インデックスを作成します。 可能な場合は、これを一意にする必要があります。
    • また、ターゲット テーブルの結合列にインデックスを作成します。 可能な場合は、これを一意のクラスター化インデックスにする必要があります。
    • これら 2 つのインデックスにより、テーブル内のデータが並べ替えられ、一意性によって比較のパフォーマンスが向上します。 クエリ オプティマイザーで重複行を検索して更新するための追加の検証処理を実行する必要がなく、追加の並べ替え操作が不要になるため、クエリのパフォーマンスが向上します。
  • MERGEステートメントのターゲットとして、任意の形式の列ストア インデックスを持つテーブルは避けてください。 他の UPDATE と同様に、ステージングされた行ストア テーブルを更新し、UPDATEMERGEではなく、バッチ処理されたDELETEINSERTを実行することで、列ストア インデックスのパフォーマンスが向上する可能性があります。

MERGE のコンカレンシーに関する考慮事項

ロックの点では、 MERGE は、個別の連続する INSERTUPDATE、および DELETE ステートメントとは異なります。 MERGE では、 INSERTUPDATE、および DELETE 操作が実行されますが、異なるロック メカニズムが使用されます。 アプリケーションのニーズによっては、個別の INSERTUPDATE、および DELETE ステートメントを記述する方が効率的な場合があります。 大規模な MERGE では、複雑なコンカレンシーの問題が発生したり、高度なトラブルシューティングが必要になる場合があります。 そのため、運用環境にデプロイする前に、 MERGE ステートメントを十分にテストすることを計画してください。

MERGE ステートメントは、次のシナリオでの個別の INSERTUPDATE、および DELETE 操作に適しています (ただし、これらに限定されません)。

  • 大量の行数を含む ETL 操作は、他の同時実行操作が予期されていない期間中に実行されます。 コンカレンシーが大きいと予想される場合は、MERGE ステートメントよりもブロックが少なく、個別のINSERTUPDATE、およびDELETEロジックのパフォーマンスが向上する可能性があります。
  • 小さな行数および長期間実行される可能性が低いトランザクションに関連する複雑な操作。
  • 最適な実行プランを確保するようにインデックスを設計できるユーザー テーブルに関連する複雑な操作。インデックス スキャンまたは (理想的には) インデックス シークを優先して、テーブルのスキャンと参照を回避できます。

コンカレンシーに関するその他の考慮事項:

  • 一意キーが 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 で結合を処理する場合、クエリ オプティマイザーにより、複数の候補の中から最も効率的な結合の処理方法が選択されます。 ソースと対象が同じようなサイズで、前に説明したインデックスのガイドラインがソース テーブルと対象テーブルに適用されている場合は、Merge Join 操作が最も効率的なクエリ プランになります。 これは、両方のテーブルが 1 回だけスキャンされ、データを並べ替える必要がないためです。 ソース テーブルが対象テーブルよりも小さい場合は、Nested Loops 操作をお勧めします。

MERGE ステートメントで OPTION (<query_hint>) 句を指定することで、特定の結合を強制的に使用できます。 この結合の種類ではインデックスが使用されないため、 MERGE ステートメントのクエリ ヒントとしてハッシュ結合を使用しないことをお勧めします。

パラメーター化に関するベスト プラクティス

パラメーターを指定せずに SELECTINSERTUPDATE、または DELETE ステートメントを実行した場合、SQL Server クエリ オプティマイザーは、ステートメントを内部的にパラメーター化することを選択できます。 これは、クエリに含まれるリテラル値がすべてパラメーターに置き換えられることを意味します。 たとえば、ステートメント INSERT dbo.MyTable (Col1, Col2) VALUES (1, 10) は内部で INSERT dbo.MyTable (Col1, Col2) VALUES (@p1, @p2) として実装される可能性があります。 簡易パラメーター化と呼ばれるこの処理によって、新しい SQL ステートメントと既存のコンパイル済みの実行プランとを照合するリレーショナル エンジンの機能が向上します。 クエリをコンパイルおよび再コンパイルする頻度が下がるので、クエリのパフォーマンスが向上する可能性があります。 クエリ オプティマイザーは、単純なパラメーター化プロセスを MERGE ステートメントに適用しません。 したがって、リテラル値を含むMERGEステートメントは、MERGE ステートメントが実行されるたびに新しいプランがコンパイルされるため、個々のINSERTUPDATE、またはDELETEステートメントが実行されないことがあります。

クエリのパフォーマンスを向上させるには、次のパラメーター化のガイドラインに従うことをお勧めします。

  • MERGE ステートメントのON <merge_search_condition>句とWHEN句内のすべてのリテラル値をパラメーター化します。 たとえば、リテラル値を適切な入力パラメーターに置き換えて、 MERGE ステートメントをストアド プロシージャに組み込むことができます。
  • ステートメントをパラメーター化できない場合は、TEMPLATE 型のプラン ガイドを作成し、そのプラン ガイドで PARAMETERIZATION FORCED クエリ ヒントを指定します。 詳細については、「プラン ガイドを使用したクエリのパラメーター化動作の指定」を参照してください。
  • MERGEステートメントがデータベースで頻繁に実行される場合は、データベースの PARAMETERIZATION オプションを FORCED に設定することを検討してください。 このオプションを設定する場合は注意が必要です。 PARAMETERIZATION オプションはデータベース レベルの設定で、データベースに対するすべてのクエリの処理方法に影響します。 詳細については、「強制パラメーター化」を参照してください。
  • プラン ガイドより新しい簡単な代替手段として、クエリ ストア ヒントを使用する同様の方針を検討します。 詳細については、「クエリ ストアのヒント」を参照してください。

TOP 句に関するベスト プラクティス

MERGE ステートメントでは、TOP句は、ソース テーブルとターゲット テーブルが結合された後、および挿入、更新、または削除アクションの対象ではない行が削除された後に影響を受ける行の数または割合を指定します。 TOP句を使用すると、指定した値に結合された行の数がさらに減り、挿入、更新、または削除アクションが順序付けされていない方法で残りの結合行に適用されます。 つまり、 WHEN 句で定義されているアクション間で行が分散される順序はありません。 たとえば、 TOP (10) を指定すると 10 行に影響します。これらの行のうち、7 行が更新され、3 行が挿入されるか、1 行が削除され、5 行が更新され、4 行が挿入される可能性があります。

TOP句を使用して、大きなテーブルに対してデータ操作言語 (DML) 操作をバッチで実行するのが一般的です。 この目的のために MERGE ステートメントで TOP 句を使用する場合は、次の意味を理解することが重要です。

  • I/O のパフォーマンスに影響する可能性があります。

    MERGE ステートメントは、ソース テーブルとターゲット テーブルの両方の完全なテーブル スキャンを実行します。 操作をバッチに分割すると、バッチごとに実行される書き込み操作の数は減少しますが、各バッチでソース テーブルと対象テーブルのフル テーブル スキャンが実行されます。 結果として得られる読み取りアクティビティは、テーブルに対するクエリおよび他の同時実行アクティビティのパフォーマンスに影響を与える可能性があります。

  • 不適切な結果になる可能性があります。

    一連のすべてのバッチが新しい行を対象としていることを確認することが重要です。そうしないと、対象テーブルに重複行が誤って挿入されるなどの不適切な動作が発生するおそれがあります。 このような動作は、対象バッチには存在しないが対象テーブル全体には存在する行がソース テーブルに含まれている場合に発生することがあります。 正しい結果を得るには、次のようにします。

    • ON句を使用して、既存のターゲット行に影響を与えるソース行と、まったく新しいソース行を判断します。
    • WHEN MATCHED句で追加の条件を使用して、ターゲット行が前のバッチによって既に更新されているかどうかを判断します。
    • WHEN MATCHED句で追加の条件を使用し、ロジックSET同じ行を 2 回更新できないことを確認します。

TOP句は、これらの句が適用された後にのみ適用されるため、各実行では、まったく一致しない行が 1 つ挿入されるか、既存の行が更新されます。

一括読み込みに関するベスト プラクティス

MERGE ステートメントを使用すると、OPENROWSET(BULK...)句をテーブル ソースとして指定することで、ソース データ ファイルからターゲット テーブルにデータを効率的に一括読み込みできます。 これにより、ファイル全体が単一のバッチで処理されます。

一括マージ処理のパフォーマンスを向上させるには、次のガイドラインに従うことをお勧めします。

  • 対象テーブルの結合列にクラスター化インデックスを作成します。

  • 一括読み込み MERGE中にターゲット テーブルで一意でない非クラスター化インデックスを無効にし、後で有効にします。 これは、夜間の一括データ操作では一般的で便利な方法です。

  • OPENROWSET(BULK...)句のORDERヒントとUNIQUE ヒントを使用して、ソース データ ファイルの並べ替え方法を指定します。

    既定では、一括操作はデータ ファイルが並べ替えられていないことを前提に実行されます。 そのため、ソース データをターゲット テーブルのクラスター化インデックスに従って並べ替え、クエリ オプティマイザーがより効率的なクエリ プランを生成できるように、 ORDER ヒントを使用して順序を指定することが重要です。 ヒントは実行時に検証されます。データ ストリームが指定されたヒントに適合していない場合は、エラーが発生します。

これらのガイドラインによって結合キーが一意になり、ソース ファイルのデータの並べ替え順が対象テーブルと一致するようになります。 追加の並べ替え操作が不要になり、意味のないデータのコピーが必要なくなるので、クエリのパフォーマンスが向上します。

MERGE パフォーマンスの測定と診断

次の機能は、 MERGE ステートメントのパフォーマンスの測定と診断に役立つ機能です。

  • sys.dm_exec_query_optimizer_info動的管理ビューのマージ stmt カウンターを使用して、MERGE ステートメントのクエリ最適化の数を返します。
  • sys.dm_exec_plan_attributes動的管理ビューの merge_action_type 属性を使用して、MERGE ステートメントの結果として使用されるトリガー実行プランの種類を返します。
  • 拡張イベント セッションを使用して、他のデータ操作言語 (DML) ステートメントの場合と同じ方法で、 MERGE ステートメントのトラブルシューティング データを収集します。 拡張イベントの概要について詳しくは、「クイック スタート: 拡張イベント」および 「SSMS XEvent Profiler の使用」に関するページをご覧ください。

Examples

A. MERGE を使用して、単一のステートメントでテーブルに INSERT 操作と UPDATE 操作を実行する

一般的なシナリオは、一致する行が存在する場合、テーブル内の 1 つまたは複数の列を更新することです。 または、一致する行が存在しない場合は、データを新しい行として挿入します。 通常は、適切な UPDATE ステートメントと INSERT ステートメントを含むストアド プロシージャにパラメーターを渡すことで、どちらのシナリオも実行します。 MERGE ステートメントを使用すると、1 つのステートメントで両方のタスクを実行できます。 次の例は、 INSERT ステートメントと UPDATE ステートメントの両方を含む AdventureWorks2022 データベースのストアド プロシージャを示しています。 その後、1 つの 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 操作を実行する

次の例では、MERGEを使用して、SalesOrderDetail テーブルで処理された注文に基づいて、AdventureWorks2022 サンプル データベースのProductInventory テーブルを毎日更新します。 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 操作を実行する

次の例では、 MERGE を使用して、行を更新または挿入することによって AdventureWorks2022 データベースの 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 ステートメントの結果を別のテーブルに挿入する

次の例では、MERGE ステートメントのOUTPUT句から返されたデータをキャプチャし、そのデータを別のテーブルに挿入します。 MERGE ステートメントは、SalesOrderDetail テーブルで処理された注文に基づいて、AdventureWorks2022 データベースのProductInventory テーブルのQuantity列を更新します。 この例では、更新された行をキャプチャし、在庫変更の追跡に使用する別のテーブルに挿入します。

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