SQL: Work arounds for multi-column IN queries in T-SQL

SQL: Work arounds for multi-column IN queries in T-SQL

We all use IN when writing queries:

SELECT ProductID, ProductName, Color, Size 
FROM dbo.Products 
WHERE Size IN ('370ml', '220ml');

A challenge comes up though, when you want to find pairs of values using IN. For example, if I have the following pairs of values:

Size        Color  370ml   Blue 370ml   Red 220ml   Blue

How do I find those when using IN?

Other databases do allow you to have pairs:

SELECT ProductID, ProductName, Color, Size
FROM dbo.Products 
WHERE (Size, Color) 
    IN (('370ml', 'Blue'), ('370ml', 'Red'), ('220ml', 'Blue'));

I wish T-SQL had that option but it doesn’t. It would be particularly useful when those values in the IN clause are coming from a sub-query.

Using CONCAT

One option is to use CONCAT to concatenate the strings. It takes a list of values, ignores NULL values, implicitly casts all values to strings, and concatenates the results.

SELECT ProductID, ProductName, Color, Size 
FROM dbo.Products 
WHERE CONCAT(Size, Color) 
IN 
(
    CONCAT('370ml', 'Blue'), 
    CONCAT('370ml', 'Red'), 
    CONCAT('220ml', 'Blue')
);

That’s the solution that looks closer to what you were trying to achieve, but it does more work than needed.

Using VALUES and a join

The other way is to just create a table expression using row constructors and to then join to it:

SELECT p.ProductID, p.ProductName, p.Color, p.Size 
FROM dbo.Products AS p 
INNER JOIN 
( 
    VALUES('370ml', 'Blue'), ('370ml', 'Red'), ('220ml', 'Blue') 
) AS c(Size, Color) 
ON c.Size = p.Size 
AND c.Color = p.Color;

Using EXISTS

Tiago Rente reminded me in the comments on LinkedIn that of course we could have used EXISTS rather than an INNER JOIN. I actually prefer the EXISTS as it keeps the filtering in a single predicate. It could look like this:

SELECT p.ProductID, p.ProductName, p.Color, p.Size 
FROM dbo.Products 
WHERE EXISTS (SELECT 1 FROM 
                 ( VALUES('370ml', 'Blue'), 
                         ('370ml', 'Red'), 
                         ('220ml', 'Blue') ) AS Selected(Size, Color) 
              WHERE Selected.Size = p.Size 
              AND Selected.Color = p.Color);

Using a CTE for Clarity

Anthony Duguid also mentioned in the LinkedIn comments that he’d like a CTE in there, to allow for a clearer name for the subquery. In fact, I think I’d like the combination of a CTE and EXISTS the best:

WITH SelectedProducts
AS
(
    SELECT Size, Color 
    FROM ( VALUES('370ml', 'Blue'), 
                 ('370ml', 'Red'), 
                 ('220ml', 'Blue') 
         ) AS sp(Size, Color)
)
SELECT p.ProductID, p.ProductName, p.Color, p.Size 
FROM dbo.Products 
WHERE EXISTS (SELECT 1 FROM SelectedProducts AS sp
                       WHERE sp.Size = p.Size 
                       AND sp.Color = p.Color);

2021-01-26