191

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 database I have

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

On my page it shows ordered list like this:

1 10 2 3 4 5 6 7 8 9

How can I make it appear ordered by numbers ascending?

Old Pro
  • 24,624
  • 7
  • 58
  • 106
Jamol
  • 3,768
  • 8
  • 45
  • 68
  • @Oded You can have some arbitrary data (e.g. settings), which you want to order by its numeric value only in very special cases. – Glutexo Feb 13 '18 at 06:20
  • very interesting article - is for MSSQL but should be relatively similar for MySQL: https://www.essentialsql.com/use-sql-server-to-sort-alphanumeric-values/ – AquaAlex Apr 30 '19 at 10:56

11 Answers11

357

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 */
juergen d
  • 201,996
  • 37
  • 293
  • 362
  • 8
    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? – Jamol Aug 04 '12 at 12:38
  • 14
    MySQL automatically converts the string to a number to make the multiplication with `1` – juergen d Aug 04 '12 at 12:40
  • 1
    The first option is most appropriate one, the second has an extra step, but it works non the less. – Manatax May 09 '14 at 19:56
  • I would be worried about NaN results when using the second option when any non-integer characters may be present. I would opt for the first option, which clearly seems the best way to handle it anyway. – todd Dec 12 '14 at 20:21
  • 7
    and if you have mixed string and number and want them both sorted, use "order by col * 1, col" – Hayden Thring Feb 23 '15 at 03:42
  • except when the column contains text with numbers, e.g. "part-1", "part-2", "part-10", "part-11", "part-3" – ierdna Mar 30 '16 at 00:40
  • What does the second option do if the string is a decimal, like `1.3` - Does it convert it to a decimal, or does performing a mathematical operation only convert to integers? – superphonic Sep 07 '16 at 14:12
  • 1
    @superphonic: The result will be `decimal`. – juergen d Sep 07 '16 at 15:11
  • 1
    In Oracle SQL you can use `order by to_number(col)` – Stevoisiak Jun 09 '20 at 17:37
  • In Oracle SQL, "order by cast(col as unsigned)" seems doesn't work. Though "order by col + 0" and as Stevoisiak mentioned "order by to_number(col)" works. – vinsinraw Sep 30 '20 at 10:20
94

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

EDIT: only works for positive integers

Velter
  • 2,080
  • 1
  • 16
  • 19
36

Another and simple way

ORDER BY ABS(column_name)

Parth Solanki
  • 3,268
  • 2
  • 22
  • 41
22

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

Losbear
  • 3,255
  • 1
  • 32
  • 28
  • 2
    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. – S.Serpooshan Dec 30 '18 at 07:34
9

It might help who is looking for the same solution.

select * from tablename ORDER BY ABS(column_name)
Samir Lakhani
  • 685
  • 10
  • 19
  • Kind of duplicates this answer https://stackoverflow.com/a/42712796/2518705 posted 3 years earlier – RAM237 Jul 26 '22 at 11:52
8

This works for me.

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

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 ) ) 
shukshin.ivan
  • 11,075
  • 4
  • 53
  • 69
5

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

ORDER BY ISNUMERIC(col) DESC, Try_Parse(col AS decimal(10,2)), col;
slartidan
  • 20,403
  • 15
  • 83
  • 131
Amir
  • 63
  • 2
  • 7
4

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

Hkachhia
  • 4,463
  • 6
  • 41
  • 76
-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

Ndianabasi
  • 508
  • 4
  • 12
-4

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.

Glen
  • 106
  • 8