204

I have numbers saved as VARCHAR to a MySQL database. I can not make them INT due to some other depending circumstances.

It is taking them as character not as number while sorting.

In the database, I have values like these:

1 2 3 4 5 6 7 8 9 10...

But when I sort them by this field I get a result like this:

1 10 2 3 4 5 6 7 8 9

How can I make it get results ordered numerically, ascending?

2
  • @Oded You can have some arbitrary data (e.g. settings), which you want to order by its numeric value only in very special cases. Commented Feb 13, 2018 at 6:20
  • very interesting article - is for MSSQL but should be relatively similar for MySQL: essentialsql.com/use-sql-server-to-sort-alphanumeric-values Commented Apr 30, 2019 at 10:56

10 Answers 10

373

If possible you should change the data type of the column to a number if you only store numbers anyway.

If you can't do that then cast your column value to an integer explicitly with

select col from yourtable
order by cast(col as unsigned)

or implicitly for instance with a mathematical operation which forces a conversion to number

select col from yourtable
order by col + 0

BTW MySQL converts strings from left to right. Examples:

string value  |  integer value after conversion
--------------+--------------------------------
'1'           |  1
'ABC'         |  0   /* the string does not contain a number, so the result is 0 */
'123miles'    |  123 
'$123'        |  0   /* the left side of the string does not start with a number */
Sign up to request clarification or add additional context in comments.

10 Comments

order by col * 1 works perfect. What is the magic behind this? Sorry I am not professional so this might be silly question but How *1 makes it change to number?
MySQL automatically converts the string to a number to make the multiplication with 1
The first option is most appropriate one, the second has an extra step, but it works non the less.
and if you have mixed string and number and want them both sorted, use "order by col * 1, col"
@superphonic: The result will be decimal.
|
102

Another way, without using a single cast.

(For people who use JPA 2.0, where no casting is allowed)

select col from yourtable
order by length(col),col

This only works for positive integers

6 Comments

this is a good one, thats worked with column that also contain both int and string
This will not work in all cases. Off the top of head, cases that will not work are mixes of whole numbers with; negative numbers, numbers with leading zeros, numbers with fractions and words and numbers combined.
great solution without any casting and juggling types around, +1
didn't seem to work at the first sight, but it worked! Thank you!
how to specify asc or desc order in this sort?
|
40

Another and simple way

ORDER BY ABS(column_name)

Comments

23

The column I'm sorting with has any combination of alpha and numeric, so I used the suggestions in this post as a starting point and came up with this.

DECLARE @tmp TABLE (ID VARCHAR(50));
INSERT INTO @tmp VALUES ('XYZ300');
INSERT INTO @tmp VALUES ('XYZ1002');
INSERT INTO @tmp VALUES ('106');
INSERT INTO @tmp VALUES ('206');
INSERT INTO @tmp VALUES ('1002');
INSERT INTO @tmp VALUES ('J206');
INSERT INTO @tmp VALUES ('J1002');

SELECT ID, (CASE WHEN ISNUMERIC(ID) = 1 THEN 0 ELSE 1 END) IsNum
FROM @tmp
ORDER BY IsNum, LEN(ID), ID;

Results

ID
------------------------
106
206
1002
J206
J1002
XYZ300
XYZ1002

Hope this helps

1 Comment

You may simply use 1 - ISNUMERIC(ID) instead of (CASE WHEN ISNUMERIC(ID) = 1 THEN 0 ELSE 1 END) to change 0 to 1 and vise versa.
8

This works for me.

select * from tablename
order by cast(columnname as int) asc

2 Comments

can you explain why?
the most simple answer, that does exactly what is needed, cast the string to an int , then order by that int in ascending order, no funky stuff :)
7

Another way to convert.

If you have string field, you can transform it or its numerical part the following way: add leading zeros to make all integer strings having equal length.

ORDER BY CONCAT( REPEAT(  "0", 18 - LENGTH( stringfield ) ) , stringfield ) 

or order by part of a field something like 'tensymbols13', 'tensymbols1222' etc.

ORDER BY CONCAT( REPEAT(  "0", 18 - LENGTH( LEFT( stringfield , 10 ) ) ) , LEFT( stringfield , 10 ) ) 

Comments

5

I was looking also a sorting fields that has letter prefix. Here is what i found out the solution. This might help who is looking for the same solution.

Field Values:

FL01,FL02,FL03,FL04,FL05,...FL100,...FL123456789

select SUBSTRING(field,3,9) as field from table order by SUBSTRING(field,3,10)*1 desc

SUBSTRING(field,3,9) i put 9 because 9 is way enough for me to hold max 9 digits integer values.

So the result will be 123456789 123456788 123456787 ... 100 99 ... 2 1

Comments

5

This will handle negative numbers, fractions, string, everything:

ORDER BY ISNUMERIC(col) DESC, Try_Parse(col AS decimal(10,2)), col;

Comments

-1

If you are using AdonisJS and have mixed IDs such as ABC-202, ABC-201..., you can combine raw queries with Query Builder and implement the solution above (https://stackoverflow.com/a/25061144/4040835) as follows:

const sortField =
  'membership_id'
const sortDirection =
  'asc'
const subquery = UserProfile.query()
  .select(
    'user_profiles.id',
    'user_profiles.user_id',
    'user_profiles.membership_id',
    'user_profiles.first_name',
    'user_profiles.middle_name',
    'user_profiles.last_name',
    'user_profiles.mobile_number',
    'countries.citizenship',
    'states.name as state_of_origin',
    'user_profiles.gender',
    'user_profiles.created_at',
    'user_profiles.updated_at'
  )
  .leftJoin(
    'users',
    'user_profiles.user_id',
    'users.id'
  )
  .leftJoin(
    'countries',
    'user_profiles.nationality',
    'countries.id'
  )
  .leftJoin(
    'states',
    'user_profiles.state_of_origin',
    'states.id'
  )
  .orderByRaw(
    `SUBSTRING(:sortField:,3,15)*1 ${sortDirection}`,
    {
      sortField: sortField,
    }
  )
  .paginate(
    page,
    per_page
  )

NOTES: In this line: SUBSTRING(:sortField:,3,15)*1 ${sortDirection},

  1. '3' stands for the index number of the last non-numerical character before the digits. If your mixed ID is "ABC-123," your index number will be 4.
  2. '15' is used to catch as any number of digits as possible after the hyphen.
  3. '1' performs a mathematical operation on the substring which effectively casts the substring to a number.

Ref 1: You can read more about parameter bindings in raw queries here: https://knexjs.org/#Raw-Bindings Ref 2: Adonis Raw Queries: https://adonisjs.com/docs/4.1/query-builder#_raw_queries

Comments

-5

Alter your field to be INT instead of VARCHAR.

I can not make them INT due to some other depending circumstances.

Then fix the depending circumstances first. Otherwise you are working around the real underlying issue. Using a MySQL CAST is an option, but it's masking your bad schema which should be fixed.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.