How to Sort a Varchar Column Storing Integers with Order By? – Interview Question of the Week #206

Question: How do you sort a varchar column that stores integers in numeric order?

Cooking spoons hang in ascending bowl sizes regardless of their handle colors

Answer: Convert the values to a suitable numeric type in ORDER BY. A varchar column sorts as text, so it does not necessarily follow the numeric order you intended.

A senior developer asked this during a performance health check. Changing the column’s data type was the obvious answer, but it was not possible for that application at the time. We worked with the column they had.

IF OBJECT_ID('tempdb..#SqlaNumbers184397') IS NOT NULL
    THROW 50001, 'The sample table already exists in this session.', 1;
CREATE TABLE #SqlaNumbers184397 (NUMBER varchar(10));
INSERT #SqlaNumbers184397 (NUMBER)
VALUES ('8792'), ('199876'), ('9012'), ('876100'), ('2098763');

SELECT NUMBER FROM #SqlaNumbers184397 ORDER BY NUMBER;
SELECT NUMBER FROM #SqlaNumbers184397 ORDER BY CAST(NUMBER AS int);
SELECT NUMBER FROM #SqlaNumbers184397 ORDER BY NUMBER * 1;

-- SQL Server2012+: keep unconvertible text last rather than failing.
SELECT NUMBER FROM #SqlaNumbers184397
ORDER BY CASE WHEN TRY_CONVERT(bigint, NUMBER) IS NULL THEN 1 ELSE 0 END,
         TRY_CONVERT(bigint, NUMBER), NUMBER;
DROP TABLE #SqlaNumbers184397;

The first SELECT returns 199876, 2098763, 876100, 8792, 9012 under the usual digit ordering. It compares the character strings. The CAST and multiplication examples both return 8792, 9012, 199876, 876100, 2098763.

Two full five-row native results compare text ordering with integer ordering
The same five values sorted first as text, then as integers. The complete SQL above also includes the multiplication and TRY_CONVERT alternatives.

Which expression should you use?

I prefer explicit CAST when all values are valid and fit in int. Multiplying by 1 works because int takes precedence over varchar and SQL Server converts the string, but the conversion is easier to miss when reading the query. Both expressions fail on nonnumeric text or a number outside the int range.

The final SELECT uses TRY_CONVERT, available from SQL Server 2012, and bigint for a wider integer range. Unconvertible values become NULL; the CASE puts those values last instead of silently putting them first. Decide how to handle empty strings, NULLs, decimal text and invalid values according to your application’s rules. This is integer ordering, not a validator for every textual representation of a number.

Applying a conversion can require a sort even when the original text column has an index. For frequent large queries, the lasting answer is a suitable numeric column, validated data, or an appropriately designed persisted computed column and index. The original workaround is useful, but it does not remove the schema issue.

Have you handled this constraint in an existing application? Share the approach that worked for you.

Published by Pinal Dave on SQLAuthority. More of my work at pinaldave.com.


Discover more from SQL Authority with Pinal Dave

Subscribe to get the latest posts sent to your email.

SQL Datatype, SQL Order By, SQL Scripts, SQL Server, SQL String
Previous Post
How to Track Autogrowth of Any Database? – Interview Question of the Week #205
Next Post
When to Use sort_in_tempdb for Rebuilding Indexes? – Interview Question of the Week #207

Related Posts

3 Comments. Leave new

  • Another method: zero pad and then sort:
    ORDER BY RIGHT(‘00000000000’+NUMBER,12)

    The number of zeroes needs to be at least as long as the longest NUMBER. If you need to figure it out at run time:
    SELECT @maxlength=MAX(LEN(NUMBER)) FROM #NUMBERS.
    Then use RIGHT(REPLICATE(‘0’,@maxlength) +NUMBER, @maxlength)

    Reply
  • We often use ORDER BY LEN(Col), Col
    This works fine and avoids the overhead of casting to number, however only works if the number is „plain“ (without leading zeros, without decimals)

    Reply
  • If the field in the source is a string but you expect a number to be contained in the value then I would use TRY_CAST as in the event of a rogue string being present the CAST in the order by would provoke an error.

    Reply

Leave a Reply

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

Fill out this field
Fill out this field
Please enter a valid email address.