简体   繁体   中英

Using SQL MERGE with vars instead of a table

take the following table (a very simplified example):

CREATE TABLE [dbo].[tbl_Order_Lines] (
    [LineID] [int] IDENTITY(1, 1) NOT NULL ,
    [OrderID] [int] NOT NULL ,
    [StockCode] [varchar](20) NOT NULL ,
    [Quantity] [smallint] NOT NULL
)

I have a proc that controls insertions into this table, but at one point you come to the infamous "UPSERT" scenario.

Let's assume my procedure has the following vars:

@OrderID INT ,
@StockCode VARCHAR(20) ,
@Quantity SMALLINT

I currently do as follows:

IF ( NOT EXISTS ( SELECT    *
                  FROM      [dbo].[tbl_Order_Lines]
                  WHERE     [OrderID] = @OrderID
                            AND [StockCode] = @StockCode )
   ) 
    INSERT  INTO [dbo].[tbl_Order_Lines]
            ( [OrderID] ,
              [StockCode] ,
              [Quantity] 
            )
    VALUES  ( @OrderID ,
              @StockCode ,
              @Quantity 
            ) 
ELSE 
    UPDATE  [dbo].[tbl_Order_Lines]
    SET     Quantity = @Quantity
    WHERE   [OrderID] = @OrderID
            AND [StockCode] = @StockCode

My intention is to do away with this old method and use the MERGE statement - however i'm struggling to get my head round the MERGE statement, this is what i have so far:

MERGE dbo.tbl_Order_Lines
    USING ( 
    VALUES
        ( @Quantity
        ) ) AS Source ( Quantity )
    ON dbo.tbl_Order_Lines.OrderID = @OrderID AND StockCode = @StockCode
    WHEN MATCHED THEN
        UPDATE SET Quantity = source.Quantity
    WHEN NOT MATCHED THEN
        INSERT  (
                  OrderID ,
                  StockCode ,
                  Quantity 
                )        VALUES
                ( @OrderID ,
                  @StockCode ,
                  Source.Quantity 
                );

My Question(s):

  1. My attempt at this MERGE seems to work - yet it looks VERY messy and confusing - is there a better way of writing this?
  2. How would i modify this MERGE statement to DELETE matching rows (based on OrderID & StockCode ) if @Quantity = 0

You could tighten up the last part by using the special $action keyword.

Case $action 
    When 'INSERT' Then 'OK_ADDED'
    When 'UPDATE' Then 'OK_UPDATED'
    When 'DELETE' Then 'OK_REMOVED'
End

$action

Is available only for the MERGE statement. Specifies a column of type nvarchar(10) in the OUTPUT clause in a MERGE statement that returns one of three values for each row: 'INSERT', 'UPDATE', or 'DELETE', according to the action that was performed on that row.

Output Clause .

OK, this is what i came up with:

MERGE dbo.tbl_Order_Lines
    USING ( VALUES ( @Quantity ) ) AS Source ( Quantity )
    ON dbo.tbl_Order_Lines.OrderID = @OrderID AND StockCode = @StockCode
    WHEN MATCHED AND @Quantity > 0 THEN
        UPDATE SET Quantity = source.Quantity
    WHEN MATCHED AND @Quantity <= 0 THEN
        DELETE
    WHEN NOT MATCHED AND @Quantity > 0 THEN
        INSERT  (
                  OrderID ,
                  StockCode ,
                  Quantity 
                )
            VALUES
                ( @OrderID ,
                  @StockCode ,
                  Source.Quantity 
                )
    OUTPUT
        COALESCE(Inserted.LineID, Deleted.LineID) AS ResultID ,
        CASE WHEN Deleted.LineID IS NULL
                  AND Inserted.LineID IS NOT NULL THEN 'OK_ADDED'
             WHEN Deleted.LineID IS NOT NULL
                  AND Inserted.LineID IS NOT NULL THEN 'OK_UPDATED'
             WHEN Deleted.LineID IS NOT NULL
                  AND Inserted.LineID IS NULL THEN 'OK_REMOVED'
        END AS ResultDesc
        INTO @tbl_LineChanges ( ResultID, ResultDesc );

Would still love to know if there is a tider way of writing this!

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