2017-09-06 7 views
1

はのは、私は複合主キーを持つテーブルがあるとしましょう:ソースで部分的に一致するターゲットでDELETEを指定したMERGE?

create table FooBar (
    fooId int not null, 
    barId int not null, 
    status varchar(20) not null, 
    lastModified datetime not null, 
    constraint PK_FooBar primary key (fooId, barId) 
); 

は、今私は多分、このような何か特定のfooIdのためのいくつかの表形式のデータを持っている:

1, 1, 'ACTIVE' 
1, 2, 'INACTIVE' 

を...と私がしたいですこの表形式のデータを信頼できるとして扱い、fooId 1のみをとし、の一致しないレコードをすべてfooIdに削除しますが、すべてのレコードはすなわち、ではなく、1のみである。

1, 1, 'ACTIVE', ... (some date, not typing it out) 
2, 1, 'ACTIVE', ... 
1, 3, 'INACTIVE', ... 
2, 2, 'INACTIVE' 

私は上記の2つのデータセットでMERGE文を実行したい、とFooBarに設定された結果のデータは、次のようになります。

たとえば、のはFooBar表は、現在このデータを持っているとしましょう:

1, 1, 'ACTIVE', ... 
2, 1, 'ACTIVE', ... 
1, 2, 'INACTIVE', ... 
2, 2, 'INACTIVE', ... 

私は行1, 3, 'INACTIVE'を削除したいでしょう、そして新しい修正タイムスタンプを更新する1, 1, 'ACTIVE'行、および1, 2, 'INACTIVE'行が挿入されるように。また、fooIdの2のレコードを変更しないことをお勧めします。

これは単一のステートメントで実行できますか?もしそうなら、どうですか?

+0

通常、マージはターゲットに存在しないソースからの行を挿入し、ソースとターゲットの両方に存在する行を更新し、ソースと一致しない行を削除することもできます(これを調整してfoodId = 1にすることもできます)おそらくあなたはより良い答えを得るために、ソース、ターゲット、期待される結果のサンプルデータを提供するべきです。たぶん[このスレッド](https://stackoverflow.com/questions/7331725/how-to-delete-from-source-using-merge-command-in-sql-server-2008)が役立ちます。 –

答えて

2

あなたのターゲットとソースのcommon table expressionsを使用することができ、そしてそれらのCTE以内にあなたは、あなたが仕事をしたい行を定義するには、フィルタを適用することができます。

-- t = Target = Destination Table = Mirror from Source 
-- s = Source = New Data source to merge into Target table 

declare @FooId int = 1; 

;with t as (
    select fooId, barId, [status], lastModified 
    from dbo.FooBar f 
    where f.fooId = @FooId 
) 
, s as (
    select fooId, barId, [status], lastModified=sysutcdatetime() 
    from src 
    where src.fooId = @FooId 
) 
merge into t with (holdlock) -- holdlock hint for race conditions 
using s 
    on (s.FooId = t.FooId and s.barId = t.barId) 

    /* If the records matches, update status and lastModified. */ 
    when matched 
     then update set t.[status] = s.[status], t.lastModified = s.lastModified 

    /* If not matched in table, insert the record */ 
    when not matched by target 
     then insert (fooId, barId, [status], lastModified) 
     values (s.fooId, s.barId, s.[status], s.lastModified) 

    /* If not matched by source, delete the record*/ 
    when not matched by source 
     then delete 
output $action, inserted.*, deleted.*; 

rextesterデモ:http://rextester.com/KRAI9699

リターン:

+---------+-------+-------+----------+---------------------+-------+-------+----------+---------------------+ 
| $action | fooId | barId | status | lastModified  | fooId | barId | status | lastModified  | 
+---------+-------+-------+----------+---------------------+-------+-------+----------+---------------------+ 
| INSERT | 1  | 2  | INACTIVE | 2017-09-06 16:21:21 | NULL | NULL | NULL  | NULL    | 
| UPDATE | 1  | 1  | ACTIVE | 2017-09-06 16:21:21 | 1  | 1  | ACTIVE | 2017-09-06 16:21:21 | 
| DELETE | NULL | NULL | NULL  | NULL    | 1  | 3  | INACTIVE | 2017-09-06 16:21:21 | 
+---------+-------+-------+----------+---------------------+-------+-------+----------+---------------------+ 

merge参照:

+0

これは、ターゲットテーブルの 'fooId' 2のレコードを削除しませんか? –

+0

@JeremyHolovacsいいえ、それはできません。 - デモをチェックしてください。 't'の共通テーブル式では' f.fooId = @ FooId'は 'fooId = 1'の行に制限します。これらの行だけが 'merge'操作の一部です。 – SqlZim

+0

...言葉があまりにも冷たいです。私はCTEのようなものを使うことはできないと思いました。 –

関連する問題