简体   繁体   中英

UPDATE 2 columns using MERGE having source conditions

SQL SERVER 2014

I need to update two columns in TargetTable with values from SourceTable

SourceTbl

PersonNr |   Block  |   BlockReason |
---------|----------|---------------|
000001   |   1      |   abuse       | 
000001   |   1      |   age         | 
000001   |   0      |   memo        | 
000002   |   1      |   age         | 
000002   |   0      |               | 
000003   |   0      |               | 
000003   |   0      |               | 
000004   |   1      |   behaviour   | 
000005   |   0      |               | 

TargetTable

PersonNr |   Block  |   BlockReason |
---------|----------|---------------|
000001   |   0      |               | 
000001   |   0      |               | 
000002   |   0      |               | 
000002   |   0      |               | 
000004   |   1      |               | 
000005   |   0      |               | 

Result needed:

PersonNr |   Block  |   BlockReason |
---------|----------|---------------|
000001   |   1      |   abuse       | 
000001   |   1      |   abuse       | 
000002   |   1      |   age         | 
000002   |   1      |   age         | 
000004   |   1      |   behaviour   |
000005   |   0      |               | 

It is not relevant which BlockReason Person 1 gets, as far as it's one from a row where Block = '1'.

I've tried this pretty straight-forward update :

UPDATE
    src
SET
    src.Block = '1', 
    src.BlockReason = targ.BlockReason
FROM
    SourceTbl src
INNER JOIN
    TargetTable targ
ON 
    src.PersonNr= targ.PersonNr
WHERE src.Block = '1'

But ended up with faulty result-rows where Block and Reason are updated separately :

PersonNr |   Block  |   BlockReason |
---------|----------|---------------|
000001   |  1       |   memo        | 

Next I've tried :

MERGE INTO TargetTable AS TGT
USING
(
  SELECT Block, BlockReason, PersonNr
  FROM SourceTbl
 GROUP BY Block, BlockReason, PersonNr
) AS SRC
  ON 
    SRC.PersonNr= TGT.PersonNr AND 
    SRC.Block= '1' 
WHEN MATCHED THEN
UPDATE SET TGT.Block= SRC.Block, TGT.BlockReason= SRC.BlockReason;

Got the error

The MERGE statement attempted to UPDATE or DELETE the same row more than once. This happens when a target row matches more than one source row. A MERGE statement cannot UPDATE/DELETE the same row of the target table multiple times. Refine the ON clause to ensure a target row matches at most one source row, or use the GROUP BY clause to group the source rows.

Any help? Hugely appreciated! Truly. Totally.

The problem with your query is that it gives duplicate values and it's trying to update the same record more than once.And the GROUP BY in the subquery doesn't make any sense since you are not using any aggregate function.

Let's take an id(say 1) and check what's going wrong with your query.

src.PersonNr |   src.Block  |   src.BlockReason | tgt.PersonNr |   tgt.Block  |   tgt.BlockReason |
-------------|--------------|-------------------|--------------
    000001   |   1          |   abuse           | 000001       |       0      |                   |         
    000001   |   1          |   age             | 000001       |       0      |                   | 
    000001   |   1          |   abuse           | 000001       |       0      |                   |
    000001   |   1          |   age             |  000001      |       0      |                   |

Your query will give you the above result and try to update targettable 2 times for each record once with abuse and next with age.

You can try the below query:

MERGE INTO TargetTable AS TGT
USING
(
 SELECT Block, BlockReason, PersonNr
 FROM(
       SELECT Block, BlockReason, PersonNr,ROW_NUMBER() OVER (PARTITION BY PersonNr  ORDER BY [YourPrimaryKey]) RN
       FROM SourceTbl ) X
 WHERE X.RN=1
) AS SRC
  ON 
    SRC.PersonNr= TGT.PersonNr AND 
    SRC.Block= '1' 
WHEN MATCHED THEN
UPDATE SET TGT.Block= SRC.Block, TGT.BlockReason= SRC.BlockReason;

You have duplicates in your data. Add another (or more than one) column to the ON clause of the MERGE that will help identify exactly one record or find a way to remove duplicates before merging.

The UPDATE should be something like this:

UPDATE
    targ
SET
    Block = '1', 
    BlockReason = src.BlockReason
FROM
    SourceTbl src
INNER JOIN
    TargetTable targ
ON 
    src.PersonNr= targ.PersonNr
WHERE src.Block = '1'

Since we're only using rows from SourceTbl where Block is 1 , it should not be possible for a row affected by this update to end up with a reason which had a Block of 0 .

There is still the general issue that this is non-deterministic in cases where multiple rows from SourceTbl are joined to one row in TargetTbl , but since you've indicated that determinism isn't required here, it shouldn't result in a problem.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM