mergesybasesap-aseupsertsybase-ase15

sybase merge query with where clause in insert or skip the insert on condition


I have a table with account_number, amount , last_updated_time.

I wrote a merge query as below

MERGE INTO account AS target
USING (SELECT ?,?,?)
AS SRC(account_number, amount , last_updated_time)
ON target.account_number = src.account_number AND src.last_updated_time > target.last_updated_time
WHEN MATCHED THEN
UPDATE SET target.amount=src.amount, target.last_updated_time = src.last_updated_time
WHEN NOT MATCHED THEN
INSERT(account_number, amount , last_updated_time)
VALUES(src.account_number, src.amount , src.last_updated_time)

Above query is failing because of unique constraint exception as account_number is primary key. I believe this violation is coming when any old records having lower timestamp are coming then it's going in insert clause and there it's failing.

How can I modify my merge query so that old or duplicate records are skipped and only new records are inserted ?


Solution

  • ASE's merge statement allows secondary conditionals to be defined in the when [not] matched clause.

    In this case we want to first match on account_number and if matched then we test last_updated_time:

    merge into account as TGT
    using (select ?, ?, ?) AS SRC (account_number, amount, last_updated_time)
    
    on    SRC.account_number    = TGT.account_number
    --and SRC.last_updated_time > TGT.last_updated_time            -- remove from 'on' clause
    
    when  matched
    and   SRC.last_updated_time > TGT.last_updated_time            -- add to 'when matched' clause
    
    then
          update set TGT.amount            = SRC.amount,
                     TGT.last_updated_time = SRC.last_updated_time
    when  not matched
    then
          insert (    account_number,     amount,     last_updated_time)
          values (SRC.account_number, SRC.amount, SRC.last_updated_time)
    

    Taking for a test drive ...

    Create table:

    create table account
    (account_number         int             NOT NULL
    ,amount                 money
    ,last_updated_time      datetime
    ,constraint pk1 primary key (account_number)
    )
    go
    
    select * from account
    go
    
     account_number amount last_updated_time 
     -------------- ------ ----------------- 
    

    NOTE: while OP appears to be using the merge command in a prepared statement, I'm going to use static values for demonstration purposes

    merge insert:

    merge into account as TGT
    using (select 100, $500, '20240520') AS SRC (account_number, amount, last_updated_time)
    on    SRC.account_number    = TGT.account_number
    when  matched
    and   SRC.last_updated_time > TGT.last_updated_time
    then
          update set TGT.amount            = SRC.amount,
                     TGT.last_updated_time = SRC.last_updated_time
    when  not matched
    then
          insert (    account_number,     amount,     last_updated_time)
          values (SRC.account_number, SRC.amount, SRC.last_updated_time)
    go
    
    select * from account
    go
    
     account_number amount last_updated_time   
     -------------- ------ ------------------- 
                100 500.00 May 20 2024 12:00AM
    

    merge update (skipped):

    merge into account as TGT
    using (select 100, $300, '20240520') AS SRC (account_number, amount, last_updated_time)
    on    SRC.account_number    = TGT.account_number
    when  matched
    and   SRC.last_updated_time > TGT.last_updated_time
    then
          update set TGT.amount            = SRC.amount,
                     TGT.last_updated_time = SRC.last_updated_time
    when  not matched
    then
          insert (    account_number,     amount,     last_updated_time)
          values (SRC.account_number, SRC.amount, SRC.last_updated_time)
    go
    
    select * from account
    go
    
     account_number amount last_updated_time   
     -------------- ------ ------------------- 
                100 500.00 May 20 2024 12:00AM     -- no change since the 'when matched'
                                                   -- conditional on 'last_updated_time' fails
    

    merge update:

    merge into account as TGT
    using (select 100, $300, '20240720') AS SRC (account_number, amount, last_updated_time)
    on    SRC.account_number    = TGT.account_number
    when  matched
    and   SRC.last_updated_time > TGT.last_updated_time
    then
          update set TGT.amount            = SRC.amount,
                     TGT.last_updated_time = SRC.last_updated_time
    when  not matched
    then
          insert (    account_number,     amount,     last_updated_time)
          values (SRC.account_number, SRC.amount, SRC.last_updated_time)
    go
    
    select * from account
    go
    
     account_number amount last_updated_time   
     -------------- ------ ------------------- 
                100 300.00 Jul 20 2024 12:00AM     -- update performed since 'when matched'
                                                   -- conditional on 'last_updated_time' succeeds
    

    NOTE: tested with ASE 16.0 SP04 PL04