About Me

IT Professional with more than 16 years experience in IT especially in the area of full life-cycle of Web/Desktop Applications Development, Database Development, Software Engineering, Consultancy, Data Management, Data Quality, Data Migrations, Reporting, ERP support, etc.

Friday, April 08, 2011

Table Value Constructors at Work

    Paradoxically, the first new SQL Server 2008 feature I learned, soon after the first CTP was made available, was the use of table value constructors, however I managed all this time without using it. This happened because I was either working on previous SQL Server versions or because other techniques were more appropriate to the problems I had to solve. So, a few days ago, the use of table value constructors came again on the table when I was weighting what’s the best approach to a problem posed by a colleague. Actually, the problem is quite simple – given a pair of values (id, new value) is needed to update a table with the “new value” corresponding to the given “id”. Previously, in similar cases, I preferred to load the data in a staging table because this was allowing me to quickly perform several checks on the set of values (e.g. check for duplicates, identify how many records will be updated). But what do you do when you are working directly in a production or any other type of environment in which you don’t have the possibility of using a staging table?

    For such cases you can either create a dataset on the fly with a set of UNIONS and reuse it in queries or, create an UPDATE statement for each pair of values. You can even automate the creation of UNION and UPDATE statements, however their use doesn’t seem so elegant, especially when you can use table value constructors. The feature seems to be hidden in documentation, as a matter of fact I found it only in the online documentation, the local help having no reference to it. Anyway, excepting a few posts on the same subject I can’t say I have seen any code until now making use of it. What’s interesting to note, before going into details, is that the ANSI SQL 94 (the earliest version of documentation I have) mentions vaguely table value constructors (p. 221), therefore I expect they are part of ANSI standard.

   According to MSDN a table value constructor allows a set of row value expressions to be constructed into a table within a single DML statement. As can be seen from the MSDN examples the list of values can contain a combination of list of values and queries, as long the data type matches and only a single scalar value is provided as a row value expression Another limitation concerns the maximum number of rows constructed, the maximum value being 1000. It’s important to know these limitations, because they might force you use other approaches. Before sketching a solution for this post’s problem, let’s look how a table can be constructed on the fly given a list of pair-values:
-- example table value constructor
SELECT EmployeeId
,
VacationHours

FROM
( VALUES (101, '23')

     , (102, '82')

     , (103, '66')

     , (104, '17')

      , (105, '64')

      , (106, '56')

      , (107, '107')

      , (108, '50')

      , (109, '109')

      , (110, '48')) AS Data(EmployeeId, VacationHours)


    The table thus constructed can be further used in an UPDATE statement, the following example being based on HumanResources.Employee from AdventureWorks database.
-- example table value constructor update
UPDATE HumanResources.Employee
SET
VacationHours = Data.VacationHours

FROM
( VALUES (101, '23')

      , (102, '82')

      , (103, '66')

      , (104, '17')

      , (105, '64')

      , (106, '56')

      , (107, '107')

      , (108, '50')

      , (109, '109')

      , (110, '48')) AS Data(EmployeeId, VacationHours)

WHERE
HumanResources.Employee.EmployeeId = Data.EmployeeId


    A similar construct can be be used to perform a SELECT, UPDATE or DELETE in which multiple columns participate in the JOIN. For example let’s consider a SELECT based on a set of pairs of values from the Sales.SalesOrderHeader table:
-- example table value constructor
SELECT SOH.*
FROM
Sales.SalesOrderHeader SOH
     JOIN ( -- set of pairs

        VALUES(10, 5069)

         , (10, 6070)

         , (10, 7060)

         , (11, 407)

         , (11, 1603)

         , (11, 2737)) AS Data(CustomerId, CurrencyRateID)

      ON SOH.CustomerId = Data.CustomerID
    AND SOH.CurrencyRateID = Data.CurrencyRateID

    As can be seen table value constructors are pretty simple to use, especially when dealing with a small number of values. If they are the best choice, that depends on the problem you are trying to solve and its specific constraints!

No comments: