The Table Value Constructor (aka the VALUES keyword)

June 19, 2021

In this post, we'll look at a helpful but lesser-known feature of T-SQL, the Table Value Constructor.

You've probably seen it used as part of an INSERT Statement, where it is easily identifiable as the VALUES keyword.

However, the VALUES keyword isn't exclusive to an INSERT Statement and can be used in other ways that we'll explore below.

What is the Table Value Constructor?

The TVC is an expression that allows you to define a table row by row (known as row expressions) using the keyword VALUES

The Table Value Constructor has the following layout:

VALUES
(value_1, value_2, value_3, etc)
,(value_1, value_2, value_3, etc)
,(value_1, value_2, value_3, etc)
...etc

The TVC starts with the keyword VALUES, followed by a list of rows that form the returned table.

Each row and its column values go inside parentheses (), with a comma separating the column values. Each additional row follows with a comma and parentheses.

Using the Table Value Constructor

So how do we use the Table Value Constructor? In a couple of ways.

Multi-row Insert Statement

Here is where you'll commonly see the Table Value Constructor. In a Multi-row Insert, the Table Value Constructor defines a table that gets inserted as a whole rather than row by row.

DROP TABLE IF EXISTS #hundred_meter_records

CREATE TABLE #hundred_meter_records (
    duration FLOAT
    ,set_by VARCHAR(30)
    ,set_on DATE
    ,place VARCHAR(50)
)

INSERT INTO #hundred_meter_records (duration, set_by, set_on, place)
VALUES(9.58, 'Usain Bolt', '2009-08-16', 'Berlin, Germany'),(9.69, 'Tyson Gay', '2009-09-09', 'Shanghai, China'),(9.69, 'Yohan Blake', '2012-08-23', 'Lausanne, Switzerland'),(9.72, 'Asafa Powell', '2008-10-02', 'Lausanne, Switzerland'),(9.74, 'Justin Gatlin', '2015-05-15', 'Doha, Qatar')
SELECT * FROM #hundred_meter_records

Using as a derived table

An interesting feature of the TVC is that it also functions as a derived table.

To do this, we wrap the TVC in parenthesis () and include it in the FROM clause of a query. As you'd expect, it is then available to use in the SELECT statement.

SELECT
    olympiad
    ,host_city
FROM
    (
        VALUES
        (2012, 'London')
        ,(2016, 'Rio de Janeiro')
        ,(2020, 'Tokyo')
        ,(2024, 'Paris')
        ,(2028, 'Los Angeles')
    ) as olympics(olympiad, host_city)

One thing to point out with the TVC, it's not possible to name columns as part of the TVC call. So instead, we alias the returned table and assign column names inside the parentheses which follow.

...
    ) as olympics(olympiad, host_city)...

As you might have guessed, this also works in a join.

SELECT
    olympics.olympiad
    ,olympics.host_city
    ,host_country.country_name
FROM
    (
        VALUES
        (2012, 'London')
        ,(2016, 'Rio de Janeiro')
        ,(2020, 'Tokyo')
        ,(2024, 'Paris')
        ,(2028, 'Los Angeles')
    ) as olympics(olympiad, host_city)

    INNER JOIN (        VALUES        ('London', 'England')        ,('Rio de Janeiro', 'Brazil')        ,('Tokyo', 'Japan')    ) as host_country(host_city, country_name)        ON host_country.host_city = olympics.host_city

Because the TVC acts as a derived table, we can do some novel things we'll explore below.

Replace hardcoded tables

A nice use of the Table Value Constructor is as an alternative to code like this.

SELECT
    'USA' as country
    ,59939 as gdp_per_capita
    ,24.08 as percent_world_gdp
UNION ALL
SELECT
    'China'
    ,8612
    ,15.12
UNION ALL
SELECT
    'Japan'
    ,38214
    ,6.02

The goal of this code is to generate a table or result set. We can refactor this code with the Table Value Constructor and get the same result.

SELECT
    *
FROM
    (
        VALUES
        ('USA',59939,24.08)
        ,('China' ,8612 ,15.12)
        ,('Japan' ,38214 ,6.02)
    ) as gdp(country,gdp_per_capita,percent_world_gdp)

Notice how it becomes more precise and less repetitive.

Alternative IN and LIKE operators

Interestingly, it's also possible to use the TVC as an alternative to the IN and LIKE operators because of this derived table functionality.

Here's an example using the regular IN operator.

-- Setup data
DROP TABLE IF EXISTS #high_cities
CREATE TABLE #high_cities (
    city VARCHAR(50)
    ,country VARCHAR(20)
    ,meters_above_sea_level INT
)
INSERT INTO #high_cities(city,country,meters_above_sea_level)
VALUES
('La Paz', 'Bolivia', 3869)
,('Cochabama', 'Bolivia', 2621)
,('Bogata', 'Colombia', 2601)
,('Addis Ababa', 'Ethiopia', 2361)
,('Mexico City', 'Mexico', 2316)
,('Xining', 'China', 2299)

-- Regular IN operator
SELECT 
    *
FROM
    #high_cities
WHERE
    country IN ('Bolivia', 'Colombia')

And here's the same with the TVC. Each item of the IN becomes a row inside a derived table created by the TVC. Then, filtering occurs by joining the derived table onto the target table.

SELECT 
    #high_cities.*
FROM
    #high_cities
        INNER JOIN (
            VALUES
            ('Bolivia')
            ,('Colombia')
        ) as country_filter(country)
            ON country_filter.country = #high_cities.country

Keep in mind that the IN operator is a syntax shortcut for multiple OR conditions in the WHERE clause. Depending on your data, this approach may turn out to be faster than using the IN operator.

Likewise, the TVC can be used to replace multiple LIKE operators.

SELECT
    *
FROM
    #high_cities
WHERE
       city LIKE '%a_a%'
    OR city LIKE 'M%'

As we saw before, we've turned each LIKE condition into a row in a table and let the join do the filtering.

SELECT
    *
FROM
    #high_cities
        INNER JOIN (
            VALUES
            ('%a_a%')
            ,('M%')
        ) as city_search(search_string)
            ON #high_cities.city LIKE city_search.search_string

Transpose Columns into Rows

Lastly, there's a cool feature of the TVC that lets us reference an external column inside the TVC itself. This feature lends itself to succinctly turning columns into rows.

Hat tip to Dwain Camps on sqlservercentral.com for the great explainer on this technique.

In short, we CROSS APPLY a Table Value Constructor where each column is a row in the TVC. The effect of this is we end up with a derived table of column values applied to each associated row.

-- Setup some data
DROP TABLE IF EXISTS #avengers
CREATE TABLE #avengers (
    hero_name VARCHAR(20)
    ,intelligence INT
    ,strength INT
    ,speed INT
)
INSERT INTO #avengers(hero_name,intelligence,strength,speed)
VALUES
('Captain America', 85, 25, 30)
,('Iron Man', 90, 75, 50)
,('Black Panther', 85, 25, 25)

-- Transpose columns into rows
SELECT 
    hero_name
    ,cols_to_rows.measure
    ,cols_to_rows.measure_value
FROM
    #avengers
        CROSS APPLY (
            VALUES            ('intelligence', intelligence)            ,('strength', strength)            ,('speed', speed)        ) as cols_to_rows(measure, measure_value)

Notice that the columns you're transposing will need to be the same or compatible data types for this to work.

Conclusion

As we've seen, the Table Value Constructor has some handy uses outside of the Multi-row Insert. Some of these can make your queries cleaner and faster. This expression is one of my favorite hidden gems in T-SQL.

Further Reading