Thursday, February 02, 2012

T-SQL UPSERT Atrocity

How to not do an "upsert" from one table to another.  Yes, this example came from a real stored procedure.  Yes, this stored procedure eventually caused problems.  Some of the table and field names have been changed for this example.

ALTER PROCEDURE [dbo].[UpdateDestinationTable]AS
    BEGIN
        DECLARE
@counter INT
        DECLARE
@recordCount INT
        SET
@counter = 1

      
-- How many records are in the source table?
        
SELECT  @recordCount = COUNT(*)
        
FROM    SOURCE_TABLE

      
-- Let's loop that many times!
        
WHILE @counter <= @recordCount
            
BEGIN
                DECLARE
@pur_WBSnum VARCHAR(15)
                
DECLARE @pur_matnum VARCHAR(40)
                
DECLARE @pur_receiptdt DATETIME
                DECLARE
@pur_receiptid VARCHAR(15)
                
DECLARE @pur_vendorid VARCHAR(10)
                
DECLARE @pur_qtyrecvd DECIMAL(10, 3)
                
DECLARE @pur_sloc VARCHAR(4)
                
DECLARE @pur_plant VARCHAR(4)

      
-- Populate our variables with values from a single record
                
SELECT  @pur_WBSnum = pur_WBSnum,
                        
@pur_matnum = pur_matnum,
                        
@pur_receiptdt = pur_receiptdt,
                        
@pur_receiptid = pur_receiptid,
                        
@pur_vendorid = pur_vendorid,
                        
@pur_qtyrecvd = pur_qtyrecvd,
                        
@pur_sloc = pur_sloc,
                        
@pur_plant = pur_plant
                
FROM    
SOURCETABLE

        
-- pur_id is an identity column in the source table.
        -- There will be a record with a pur_id value that
        -- matches the iteration of the loop we are on... right?
                
WHERE   pur_id = @counter

        
-- Update the corresponding record in the destination table
                
UPDATE  DESTINATION_TABLE
                
SET     VENDOR_ID = @pur_vendorid,
                        
QTY_RECVD = @pur_qtyrecvd,
                        
SLOC = @pur_sloc,
                        
PLANT = @pur_plant
                
WHERE   PROJECT_ID = @pur_WBSnum
                        
AND DAX_ITEMID = @pur_matnum
                        
AND RECEIPT_DT = @pur_receiptdt
                        
AND RECEIPT_ID = @pur_receiptid

        
-- Nothing was updated?
                
IF @@ROWCOUNT = 0

        
-- Then we must need to do an insert instead!
                    
INSERT  INTO DESTINATION_TABLE
                            
(
                              
PROJECT_ID,
                              
DAX_ITEMID,
                              
RECEIPT_DT,
                              
RECEIPT_ID,
                              
PO_ID,
                              
VENDOR_ID,
                              
QTY_RECVD,
                              
INVENT_TRANS_ID,
                              
DATAAREA_ID,
                              
SLOC,
                              
PLANT
                            
)
                    
VALUES  (
                              
@pur_WBSnum,
                              
@pur_matnum,
                              
@pur_receiptdt,
                              
@pur_receiptid,
                              
' ',
                              
@pur_vendorid,
                              
@pur_qtyrecvd,
                              
' ',
                              
' ',
                              
@pur_sloc,
                              
@pur_plant
                            
)

                
-- Re-initialize our variables and increment the counter

                
SET @pur_WBSnum = NULL
                
SET @pur_matnum = NULL
                
SET @pur_receiptdt = ' '
                
SET @pur_receiptid = NULL
                
SET @pur_vendorid = NULL
                
SET @pur_qtyrecvd = 0
                
SET @pur_sloc = NULL
                
SET @pur_plant = NULL
                
SET @counter = @counter + 1

            
END
        
-- We can count on TRUCATE to restart the
        -- identity column seed at 1, right?
        
TRUNCATE TABLE
SOURCE_TABLE

    
END