4 Ways To Define Lookup Values In A Query

Published on: 2018-06-05

Watch this week’s episode on YouTube.

Occasionally you may need to create a lookup table of values for a single query.  Building a permanent table of these values is a good option if you need to reuse the values frequently, but for one-off, ad-hoc queries you may want to hard-code the lookup table of values directly in your query.

We can hard-code these values using several different techniques.  Below are the techniques I’ve seen used before, going in order from my least to most favorite.

Table Variables

Table variables get a bad rap in SQL Server because they don’t provide accurate row estimates to the query optimizer.  This can cause SQL Server to come up with some really terrible execution plans that will kill your query’s performance.

However, if your use case is a single small lookup table of less than 100 records, table variables might actually be a viable option.  They are quick and easy to setup and can be added to the top of your query.

With that said, I don’t think I’ve ever used a table variable in this type of scenario (or any scenario really).  I know some people love using them and I think that’s fine as long as you are keeping track of your query performance.  For me though, there are so many better options available…

Temporary Tables

Temp tables are the answer to many of the table variable’s shortcomings.

Temp tables can perform well with larger amounts of data because they can be indexed and can have statistics generated on them.  Both of these features typically help SQL Server generate better execution plans.

There is some overhead in coding a temp table though: just like a table variable, a temp table needs to be created and inserted into before being able to use it in your query.  While normally not a huge deal, this is not something I want to have to do in those instances where I want to define some lookup values quickly…

SELECT with UNION ALL

The next option is hard-coding values in SELECT statements and then joining them together with UNION ALLs.

This is probably the most common technique I see, and for good reason: the syntax is straight forward and doesn’t require any special setup; perfect for the one-time use ad-hoc scenario.

Its format also makes it easy to use the ALT + highlight shortcut to quickly create a derived table lookup from the results of another query or values copied from elsewhere.

I do like this method a lot, but there is one method that I like slightly more…

The VALUES() Constructor

You’ve probably use the VALUES constructor in an INSERT INTO statement, but did you know you can also use it in a FROM clause?

This syntax is similar to our SELECT + UNION ALL technique above, except we are swapping in single quotes and parentheses for SELECTs and UNION ALLs.  I find this slightly easier to write, if only because it requires typing out fewer characters and feels more programmatic.

One Last Tip: CTE

This isn’t really an additional technique, but something related that I use often and feels appropriate to mention.

If using either the SELECT + UNION ALL or VALUES techniques, you can put those lookup queries into a common table expression for easier referencing.

This doesn’t give any performance advantage, but it does help keep your code clean by putting your lookup logic right at the top of your file.  This becomes particularly useful when using those hard-coded lookup values as parameters, allowing all changes to your query to be made right at the top of your file during subsequent runs.

Thanks for reading. You might also enjoy following me on Twitter.

Want to learn even more SQL?

Sign up for my newsletter to receive weekly SQL tips!

6 thoughts on “4 Ways To Define Lookup Values In A Query”

  1. Something on tangent with lookup values, should you use an enumeration (numbering) for the lookup table. I always am split between using the values as direct values or via an int field. I’ve seen people do it by int column as gospel, but I encountered a situation recently where the rows needed to be stated as either ‘annual’ or ‘monthly’. It was done via a table i.e. period(id, type, desc), but I thought it would have been better to just have the table as period(type,desc) with the PK on the type. This way it can be directly visualised within the parent table row as opposed to seeing an 1 or 2 and performing a join.
    What are your thoughts?

    1. Hi Tack, thanks for the comment. In the scenario you describe, I think it’s fine to not include an id column if your “type” values are all unique. Using just type/description would be following a key/value dictionary pattern which is very common.

      Once tables become wider though, I generally like using an identity id column for a clustering key because that clustering key is going to be copied into all of your nonclustered indexes. Having an identity column as a clustering key will save significant space versus some potentially large column or multi-column clustering key.

    1. Yes! How could I forget. It’s a little wordy for me, but could work well for more complex structures:

      DECLARE @Colors NVARCHAR(MAX) = N'{"Colors" : [{"ColorName": "FireBrick", "HexCode": "#B22222"},{"ColorName": "HotPink", "HexCode":"#FF69B4"}]}';

      SELECT * FROM OPENJSON(@Colors, '$.Colors')
      WITH (
      ColorName varchar(20) '$.ColorName',
      HexCode varchar(7) '$.HexCode'
      )

  2. Table variable could be indexed too! Here is an example and you can see that the optimizer seeks the index.
    DECLARE @T TABLE
    (
    ColorName varchar(500),
    HexCode int
    ,unique(ColorName,HexCode)
    );
    insert into @T select name,object_id from sys.objects
    SELECT * FROM @T
    WHERE ColorName like ‘sys%’

    The main difference of temporary vs variable tables is that temporaries are affected by transactions, variables not. Temporary table can be altered after creation. The scope of the variable table is local to the batch/function/procedure.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.