SQL Server 2008 - Killer features - MERGE and Table Valued Parameters

 

Table valued parameters along with MERGE are in my view the two killer features and the two that I believe will get most uptake when people start to use SQL 2008.

Table valued parameters enable you to send a set of data as a single parameter to your code whether it be adhoc SQL, a stored procedure or function. This is beneficial as it reduces the number of calls that have to be made to the database and so you minimise the overhead associated with each call. Imagine saving 1000 items into a table the historical way was to either, issue 1000 database calls to do the inserts or construct a single batch that consisted of all the insert statements. (This was the behaviour with ADO). Even with the latter approach where you only have 1 round trip to the database you still have the overhead associated with a single insert. I compare it to photo copying a set of pages, you can either copy each page one at a time, by lifting the lid on the copier, putting the paper on the platen and then copying and repeating for each sheet or the quicker way is to put them all in the sheet feeder and let the machine process the whole set of pages in one go.

Table valued parameters really come into their own when you have a distributed application and the speed of the link between application and database isn’t very fast. In the scenario above if the latency in the network results in a call to the db taking 10 ms (irrespective of what you are doing), then it will take 10s to save the data. Converting to a table valued parameter would reduce this down to 1 x 10ms overhead for the database call.

The MERGE statement further enhances table valued parameters by enabling you to pass a single set of data consisting of inserts, updates and deletes and save those to the database in a single statement. This has the benefit in that it simplifies code, you have a single atomic transaction and so the complexities of serialized transactions may be avoided, you get performance benefits from the fact that the index structures are being modified in a single set based approach rather than 3 individual statements performing inserts, updates and deletes. (In one test I did I found using MERGE to do purely INSERTs was quicker than the equivalent insert statement, so it is by no means a compromise).

Using AdventureWorks we have the following demo.

Firstly we need to create a TABLE TYPE for use by our Table Valued Parameter. In this case we have copied the structure of the SalesOrderDetail type.

CREATE TYPE SalesOrderDetail_Type  as TABLE (

    [SalesOrderID]          [int] NOT NULL ,

    [SalesOrderDetailID]    [int] IDENTITY(1,1) NOT NULL,

    [CarrierTrackingNumber] [nvarchar](25) NULL,

    [OrderQty]              [smallint] NOT NULL,

    [ProductID]             [int] NOT NULL,

    [SpecialOfferID]        [int] NOT NULL,

    [UnitPrice]             [money] NOT NULL,

    [UnitPriceDiscount]     [money] NOT NULL,

    [LineTotal]             AS (isnull(([UnitPrice]*((1.0)-[UnitPriceDiscount]))*[OrderQty],(0.0))),

    [rowguid]               [uniqueidentifier] ROWGUIDCOL  NOT NULL,

    [ModifiedDate]          [datetime] NOT NULL, PRIMARY KEY (SalesOrderId,SalesOrderDetailID))

GO

 

Secondly we create our procedure that uses the Table Type. We can then call this procedure from TSQL or client code passing in a set of data.

CREATE PROCEDURE up_OrderDetail_SaveSet

        @ChangedOrderDetail SalesOrderDetail_Type READONLY

 ,@SalesOrderID       int

AS

 

MERGE Sales.SalesOrderDetail OrderDetail

USING @ChangedOrderDetail    Chng        ON Chng.[SalesOrderDetailID] = OrderDetail.[SalesOrderDetailID]

                                        AND Chng.[SalesOrderID]       = OrderDetail.[SalesOrderID]

WHEN MATCHED THEN

    UPDATE SET [SalesOrderID]            = Chng.SalesOrderID           

              ,[CarrierTrackingNumber]   = Chng.CarrierTrackingNumber 

              ,[OrderQty]                = Chng.OrderQty               

              ,[ProductID]               = Chng.ProductID               

              ,[SpecialOfferID]          = Chng.SpecialOfferID           

              ,[UnitPrice]               = Chng.UnitPrice               

              ,[UnitPriceDiscount]       = Chng.UnitPriceDiscount       

              ,[rowguid]                 = Chng.rowguid               

              ,[ModifiedDate]            = Chng.ModifiedDate           

WHEN NOT MATCHED THEN

    INSERT ([SalesOrderID],[CarrierTrackingNumber],[OrderQty],[ProductID],[SpecialOfferID]

           ,[UnitPrice],[UnitPriceDiscount],[rowguid],[ModifiedDate])

     VALUES

           (Chng.SalesOrderID,Chng.CarrierTrackingNumber,Chng.OrderQty,Chng.ProductID,Chng.SpecialOfferID

           ,Chng.UnitPrice,Chng.UnitPriceDiscount,Chng.rowguid,Chng.ModifiedDate);



-
Published 17 June 2008 08:30 by simonsabin

Comments

# Dew Droplet - June 18, 2008 | Alvin Ashcraft's Morning Dew

Pingback from  Dew Droplet - June 18, 2008 | Alvin Ashcraft's Morning Dew

# Weekly Link Post 47 « Rhonda Tipton’s WebLog

Pingback from  Weekly Link Post 47 « Rhonda Tipton’s WebLog