2016-07-12 6 views
1

に一致したとき、私はレコードが存在している場合は、ターゲットの終了日を変更するには、次の条件アップデートと条件がTSQL-マージ

  1. にマージ使用してアップサートを実行することができますストアドプロシージャを書くしようとしている挿入昨日の日すなわち、現在の日に - 1

  2. するレコードが存在しない場合は、新規挿入レコードの

ここで私はSP 0123で使用される表のtblEmployeeですここで

CREATE TABLE tblEmployee 
(
    [EmployeeID] [int] IDENTITY(1,1) NOT NULL, 
    [Name] [varchar](10) NOT NULL, 
    [StartDate] [date] NOT NULL, 
    [EndDate] [date] NOT NULL 
) 
は、入力パラメータとしてUDTTを取り、私のSPで

CREATE PROCEDURE [dbo].[usp_UpsertEmployees] 
@typeEmployee typeEmployee READONLY -- It has same column like tblEmployye except EmployeeID 
AS 
BEGIN 
    SET NOCOUNT ON;  

    MERGE INTO tblEmployee AS TARGET 
    USING @typeEmployee AS SOURCE 
    ON TARGET.Name = SOURCE.Name 

    WHEN MATCHED and TARGET.StartDate < SOURCE.StartDate 
    THEN 

      --First Update Existing Record EndDate to Previous Date as shown below 
      UPDATE 
      set TARGET.EndDate = DATEADD(day, -1, convert(date, SOURCE.StartDate)) 

      -- Now Insert New Record 
      --INSERT VALUES(SOURCE.Name, SOURCE.StartDate, SOURCE.EndDate); 

    WHEN NOT MATCHED by TARGET 
    THEN 
      INSERT VALUES(SOURCE.Name, SOURCE.StartDate, SOURCE.EndDate); 

    SET NOCOUNT OFF;   
END 

私は両方の既存のレコードを更新し、列が誰かが私説明していただけます

に一致したときに新しいレコードを追加実行することができますどのようにTSQLでのMergeの実行フロー、すなわち

WHEN MATCHED --Will this Execute Everytime 

    WHEN NOT MATCHED by TARGET -- Will this Execute Everytime 

    WHEN NOT MATCHED by SOURCE -- Will this Execute Everytime 

上記の3つのconditiマージ中に毎回実行されたりのみマッチング条件はこれがMERGEは、(更新をし、同じ句に挿入)行うことを意味するものではありませんアドバンス

+0

'その後、/ *アップデートを開始し、*/end' ... –

+0

取得コンパイルエラー「不正な構文が近い '開始' ここに挿入します。INSERT、またはUPDATE、DELETE期待。 – Amar

+0

よく、私は私の推測します間違っていました... –

答えて

3

に毎回

感謝を実行するに。これを実現するには、OUTPUT句を使用して、更新されたすべてのレコードのみを取得します。 MERGE/OUTPUTコンボは非常に厄介です。あなたのOUTPUT更新は実際には更新されたTARGETレコードです。したがって、TARGETレコードを一時変数/テーブル変数で開始する必要があります。次に、INSERTを実行するためにSOURCEに戻します。出力結果を直接ソースに戻したり、WHERE内の相関サブクエリとして使用することはできません。

セットアップいくつかのサンプルデータ

以下のコードはいくつかのサンプルデータを設定します。

-- Setup sample data 
DECLARE @typeEmployee TABLE (
    [Name] [varchar](10) NOT NULL, 
    [StartDate] [date] NOT NULL, 
    [EndDate] [date] NOT NULL 
) 
DECLARE @tblEmployee TABLE (
    [EmployeeID] [int] IDENTITY(1,1) NOT NULL, 
    [Name] [varchar](10) NOT NULL, 
    [StartDate] [date] NOT NULL, 
    [EndDate] [date] NOT NULL 
) 
INSERT @tblEmployee VALUES ('Emp A', '1/1/2016', '2/1/2016') 
INSERT @typeEmployee VALUES ('Emp A', '1/5/2016', '2/2/2016'), ('Emp B', '3/1/2016', '4/1/2016') 

更新するストアド・プロシージャ

あなたはそれがターゲットレコードの変更されたレコードを返すようにMERGEの終わりにOUTPUTを使用することができ、かつ$actionを含むことによって、あなたはまた、それかどうかを取得します挿入、更新、削除のいずれかでした。あなたが唯一のTARGETレコードを取り戻すため、あなたがINSERTを行うことができますので、しかし、MERGE/OUTPUTからの結果セットが直接SOURCEテーブルに対して結合することができません

。 SOURCEテーブルの相関サブクエリ内でOUTPUTの結果を使用することはできません。最も簡単なことは、一時テーブルまたはテーブル変数を使用して出力をキャプチャすることです。

-- Logic to do upsert 
DECLARE @Updates TABLE (
    [Name] [varchar](10) NOT NULL, 
    [StartDate] [date] NOT NULL, 
    [EndDate] [date] NOT NULL 
) 

INSERT @Updates 
    SELECT 
     Name, 
     StartDate, 
     EndDate 
    FROM (
     MERGE INTO @tblEmployee AS TARGET 
     USING @typeEmployee AS SOURCE 
      ON TARGET.Name = SOURCE.Name 
     WHEN MATCHED AND TARGET.StartDate < SOURCE.StartDate 
     THEN 
      --First Update Existing Record EndDate to Previous Date as shown below 
      UPDATE SET 
       EndDate = DATEADD(DAY, -1, CONVERT(DATE, SOURCE.StartDate)) 
     WHEN NOT MATCHED BY TARGET -- OR MATCHED AND TARGET.StartDate >= SOURCE.StartDate -- Handle this case? 
     THEN 
      INSERT VALUES(SOURCE.Name, SOURCE.StartDate, SOURCE.EndDate) 
     OUTPUT $action, INSERTED.Name, INSERTED.StartDate, INSERTED.EndDate 
     -- Use the MERGE to return all changed records of target table 
    ) AllChanges (ActionType, Name, StartDate, EndDate) 
    WHERE AllChanges.ActionType = 'UPDATE' -- Only get records that were updated 

今、あなたはMERGEの出力をキャプチャしてのみ更新されますTARGETレコードにフィルタ処理したので、あなたがしてMERGEアップデートの一部であっただけSOURCEレコードをフィルタリングすることにより、あなたの優れたINSERTを行うことができます。

INSERT @tblEmployee 
    SELECT 
     SOURCE.Name, 
     SOURCE.StartDate, 
     SOURCE.EndDate 
    FROM @typeEmployee SOURCE 
    WHERE EXISTS (
     SELECT * 
     FROM @Updates Updates 
     WHERE Updates.Name = SOURCE.Name 
      -- Other join conditions to ensure 1:1 match against SOURCE (start date?) 
    ) 

出力リレー

これは、変更後のサンプルレコードの出力です。意図した目標変更が行われました。マッチしたとき

-- Show output 
SELECT * FROM @tblEmployee 
+0

上記のコードの説明に少し役立つかもしれません – Amar

+0

追加の説明を追加しました。 –