92

I want to sort the following data items in the order they are presented below (numbers 1-12):

1
2
3
4
5
6
7
8
9
10
11
12

However, my query - using order by xxxxx asc sorts by the first digit above all else:

1
10
11
12
2
3
4
5
6
7
8
9

Any tricks to make it sort more properly?

Further, in the interest of full disclosure, this could be a mix of letters and numbers (although right now it is not), e.g.:

A1
534G
G46A
100B
100A
100JE

etc....

Thanks!

update: people asking for query

select * from table order by name asc
Joshua Pinter
  • 45,245
  • 23
  • 243
  • 245
Shackrock
  • 4,601
  • 10
  • 48
  • 74
  • Related: https://stackoverflow.com/questions/48600059/using-mysql-sort-varchar-column-numerically-with-cast-as-unsigned-when-the-colum – Paul Spiegel Feb 18 '20 at 15:18

20 Answers20

153

People use different tricks to do this. I Googled and find out some results each follow different tricks. Have a look at them:

Edit:

I have just added the code of each link for future visitors.

Alpha Numeric Sorting in MySQL

Given input

1A 1a 10A 9B 21C 1C 1D

Expected output

1A 1C 1D 1a 9B 10A 21C

Query

Bin Way
===================================
SELECT 
tbl_column, 
BIN(tbl_column) AS binray_not_needed_column
FROM db_table
ORDER BY binray_not_needed_column ASC , tbl_column ASC

-----------------------

Cast Way
===================================
SELECT 
tbl_column, 
CAST(tbl_column as SIGNED) AS casted_column
FROM db_table
ORDER BY casted_column ASC , tbl_column ASC

Natural Sorting in MySQL

Given input

Table: sorting_test
 -------------------------- -------------
| alphanumeric VARCHAR(75) | integer INT |
 -------------------------- -------------
| test1                    | 1           |
| test12                   | 2           |
| test13                   | 3           |
| test2                    | 4           |
| test3                    | 5           |
 -------------------------- -------------

Expected Output

 -------------------------- -------------
| alphanumeric VARCHAR(75) | integer INT |
 -------------------------- -------------
| test1                    | 1           |
| test2                    | 4           |
| test3                    | 5           |
| test12                   | 2           |
| test13                   | 3           |
 -------------------------- -------------

Query

SELECT alphanumeric, integer
       FROM sorting_test
       ORDER BY LENGTH(alphanumeric), alphanumeric  

Sorting of numeric values mixed with alphanumeric values

Given input

2a, 12, 5b, 5a, 10, 11, 1, 4b

Expected Output

1, 2a, 4b, 5a, 5b, 10, 11, 12

Query

SELECT version
FROM version_sorting
ORDER BY CAST(version AS UNSIGNED), version;
starball
  • 20,030
  • 7
  • 43
  • 238
Jomoos
  • 12,823
  • 10
  • 55
  • 92
  • 13
    It would be great to include more complete information in this post itself. – showdev Sep 21 '15 at 16:46
  • 3
    @showdev I have included it,hope it might be helpful :) – affaz Feb 12 '17 at 04:44
  • None of these worked for me :/ Any recommendations for a list like this? https://pastebin.com/d4kXq6HS Ideal output is: https://pastebin.com/kJ4Zc3XY – err Aug 29 '17 at 20:58
  • 1
    what if the input is A1, A10, B1, C11, D8 ? none of the trick works – Kira Katou Mar 24 '19 at 16:34
  • I've just posted a real, general-purpose nat-sort function for MySQL that should correctly handle all of the examples, and much much more besides. See my answer to "Natural Sort in MySQL" here: https://stackoverflow.com/a/58154535/999120 – Doin Sep 29 '19 at 11:22
  • `CAST()` and `BIN()` return incorrect result if I have **1A, Goofy, 1a, Final Fantasy** it will be **Final Fantasy, Goofy, 1A, 1a** instead of **1A, 1a, Final Fantasy, Goofy** – vee May 17 '21 at 03:04
18

Just do this:

SELECT * FROM table ORDER BY column `name`+0 ASC

Appending the +0 will mean that:

0, 10, 11, 2, 3, 4

becomes :

0, 2, 3, 4, 10, 11

AO_
  • 2,573
  • 3
  • 30
  • 31
  • 2
    THIS IS VERY DANGEROUS! On my query it worked fine, I upvoted the answer BUT when I refreshed, it didn't work! Then I go ahead and refresh the query 100 times, randomly it works and doesn't work for the SAME query! Don't rely on this! My table has a number at the end and here is my query: SELECT TABLE_NAME FROM information_schema.TABLES WHERE TABLE_SCHEMA = 'my_database' AND TABLE_NAME LIKE '%my_table%' ORDER BY TABLE_NAME+0 DESC LIMIT 1 – Tarik Aug 25 '16 at 15:46
  • 5
    @Tarik It's probably because you are using `information_schema` which are just estimated values, they are not fully aggregated. – AO_ Sep 06 '16 at 09:53
  • 1
    @Andrew Odendaal answer works everytime for me with ORDER BY 'name'+0 ASC – IlludiumPu36 Sep 18 '19 at 04:37
17

I know this post is closed but I think my way could help some people. So there it is :

My dataset is very similar but is a bit more complex. It has numbers, alphanumeric data :

1
2
Chair 
3
0
4
5
-
Table
10
13
19
Windows
99
102
Dog

I would like to have the '-' symbol at first, then the numbers, then the text.

So I go like this :

SELECT name, (name = '-') boolDash, (name = '0') boolZero, (name+0 > 0) boolNum 
FROM table 
ORDER BY boolDash DESC, boolZero DESC, boolNum DESC, (name+0), name

The result should be something :

-
0    
1
2
3
4
5
10
13
99
102
Chair
Dog
Table
Windows

The whole idea is doing some simple check into the SELECT and sorting with the result.

antoine
  • 244
  • 2
  • 4
  • I couldn't get this to work by putting `(name = '-') boolDash` in the select statement. But I got it working by putting `name = '-'` directly in the order by statement. – Yep_It's_Me Jan 09 '17 at 06:51
17

I hate this, but this will work

order by lpad(name, 10, 0)  <-- assuming maximum string length is 10
                            <-- you can adjust to a bigger length if you want to
ajreal
  • 46,720
  • 11
  • 89
  • 119
11

This works for type of data: Data1, Data2, Data3 ......,Data21. Means "Data" String is common in all rows.

For ORDER BY ASC it will sort perfectly, For ORDER BY DESC not suitable.

SELECT * FROM table_name ORDER BY LENGTH(column_name), column_name ASC;
Shiv Buyya
  • 3,770
  • 2
  • 30
  • 25
5

I had some good results with

SELECT alphanumeric, integer FROM sorting_test ORDER BY CAST(alphanumeric AS UNSIGNED), alphanumeric ASC
Blouarf
  • 632
  • 9
  • 17
3

If you need to sort an alpha-numeric column that does not have any standard format whatsoever

SELECT * FROM table ORDER BY (name = '0') DESC, (name+0 > 0) DESC, name+0 ASC, name ASC

You can adapt this solution to include support for non-alphanumeric characters if desired using additional logic.

wibeasley
  • 5,000
  • 3
  • 34
  • 62
Weston Ganger
  • 6,324
  • 4
  • 41
  • 39
3

This type of question has been asked previously.

The type of sorting you are talking about is called "Natural Sorting". The data on which you want to do sort is alphanumeric. It would be better to create a new column for sorting.

For further help check natural-sort-in-mysql

Community
  • 1
  • 1
2

This should sort alphanumeric field like: 1/ Number only, order by 1,2,3,4,5,6,7,8,9,10,11 etc... 2/ Then field with text like: 1foo, 2bar, aaa11aa, aaa22aa, b5452 etc...

SELECT  MyField
FROM MyTable
order by 
    IF( MyField REGEXP '^-?[0-9]+$' = 0, 
    9999999999 ,  
    CAST(MyField AS DECIMAL) 
    ), MyField

The query check if the data is a number, if not put it to 9999999999 , then order first on this column, then order on data with text

Good luck!

Dimgold
  • 2,748
  • 5
  • 26
  • 49
2

Instead of trying to write some function and slow down the SELECT query, I thought of another way of doing this...

Create an extra field in your database that holds the result from the following Class and when you insert a new row, run the field value that will be naturally sorted through this class and save its result in the extra field. Then instead of sorting by your original field, sort by the extra field.

String nsFieldVal = new NaturalSortString(getFieldValue(), 4).toString()

The above means:
- Create a NaturalSortString for the String returned from getFieldValue()
- Allow up to 4 bytes to store each character or number (4 bytes = ffff = 65535)

| field(32)  |  nsfield(161)                            |   
  a1            300610001

String sortString = new NaturalSortString(getString(), 4).toString()

import StringUtils;

/**
 * Creates a string that allows natural sorting in a SQL database
 * eg, 0 1 1a 2 3 3a 10 100 a a1 a1a1 b
 */
public class NaturalSortString {

    private String inStr;
    private int byteSize;
    private StringBuilder out = new StringBuilder();

    /**
     * A byte stores the hex value (0 to f) of a letter or number.
     * Since a letter is two bytes, the minimum byteSize is 2.
     *
     * 2 bytes = 00 - ff  (max number is 255)
     * 3 bytes = 000 - fff (max number is 4095)
     * 4 bytes = 0000 - ffff (max number is 65535)
     *
     * For example:
     * dog123 = 64,6F,67,7B and thus byteSize >= 2.      
     * dog280 = 64,6F,67,118 and thus byteSize >= 3.
     *
     * For example:
     * The String, "There are 1000000 spots on a dalmatian" would require a byteSize that can 
     * store the number '1000000' which in hex is 'f4240' and thus the byteSize must be at least 5
     *
     * The dbColumn size to store the NaturalSortString is calculated as:
     * > originalStringColumnSize x byteSize + 1
     * The extra '1' is a marker for String type - Letter, Number, Symbol
     * Thus, if the originalStringColumn is varchar(32) and the byteSize is 5:
     * > NaturalSortStringColumnSize = 32 x 5 + 1 = varchar(161)
     *
     * The byteSize must be the same for all NaturalSortStrings created in the same table.
     * If you need to change the byteSize (for instance, to accommodate larger numbers), you will
     * need to recalculate the NaturalSortString for each existing row using the new byteSize.
     *
     * @param str        String to create a natural sort string from
     * @param byteSize   Per character storage byte size (minimum 2)
     * @throws Exception See the error description thrown
     */
    public NaturalSortString(String str, int byteSize) throws Exception {
        if (str == null || str.isEmpty()) return;
        this.inStr = str;
        this.byteSize = Math.max(2, byteSize);  // minimum of 2 bytes to hold a character
        setStringType();
        iterateString();
    }

    private void setStringType() {
        char firstchar = inStr.toLowerCase().subSequence(0, 1).charAt(0);
        if (Character.isLetter(firstchar))     // letters third
            out.append(3);
        else if (Character.isDigit(firstchar)) // numbers second
            out.append(2);
        else                                   // non-alphanumeric first
            out.append(1);
    }

    private void iterateString() throws Exception {
        StringBuilder n = new StringBuilder();
        for (char c : inStr.toLowerCase().toCharArray()) { // lowercase for CASE INSENSITIVE sorting
            if (Character.isDigit(c)) {
                // group numbers
                n.append(c);
                continue;
            }
            if (n.length() > 0) {
                addInteger(n.toString());
                n = new StringBuilder();
            }
            addCharacter(c);
        }
        if (n.length() > 0) {
            addInteger(n.toString());
        }
    }

    private void addInteger(String s) throws Exception {
        int i = Integer.parseInt(s);
        if (i >= (Math.pow(16, byteSize)))
            throw new Exception("naturalsort_bytesize_exceeded");
        out.append(StringUtils.padLeft(Integer.toHexString(i), byteSize));
    }

    private void addCharacter(char c) {
        //TODO: Add rest of accented characters
        if (c >= 224 && c <= 229) // set accented a to a
            c = 'a';
        else if (c >= 232 && c <= 235) // set accented e to e
            c = 'e';
        else if (c >= 236 && c <= 239) // set accented i to i
            c = 'i';
        else if (c >= 242 && c <= 246) // set accented o to o
            c = 'o';
        else if (c >= 249 && c <= 252) // set accented u to u
            c = 'u';
        else if (c >= 253 && c <= 255) // set accented y to y
            c = 'y';

        out.append(StringUtils.padLeft(Integer.toHexString(c), byteSize));
    }

    @Override
    public String toString() {
        return out.toString();
    }
}

For completeness, below is the StringUtils.padLeft method:

public static String padLeft(String s, int n) {
    if (n - s.length() == 0) return s;
    return String.format("%0" + (n - s.length()) + "d%s", 0, s);
}

The result should come out like the following

-1
-a
0
1
1.0
1.01
1.1.1
1a
1b
9
10
10a
10ab
11
12
12abcd
100
a
a1a1
a1a2
a-1
a-2
áviacion
b
c1
c2
c12
c100
d
d1.1.1
e
Christian
  • 3,708
  • 3
  • 39
  • 60
  • 1
    It's true that the best approach to nat-sort in MySQL is to sort on a separate sort key. However, your solution (a) Requires code external to MySQL to create these keys, (b) Disregards collation rules when sorting the text parts, (c) Is *hugely* inefficient in terms of the sort key representations, and (d) Offers **absolutely no advantage** compared to simply left-padding all numbers in the string up to a fixed character length (as several other solutions on the https://stackoverflow.com/q/153633/999120 page do). Sorry, but this answer is just horrible: downvoted. – Doin Sep 29 '19 at 09:52
2

MySQL ORDER BY Sorting alphanumeric on correct order

example:

SELECT `alphanumericCol` FROM `tableName` ORDER BY 
  SUBSTR(`alphanumericCol` FROM 1 FOR 1), 
  LPAD(lower(`alphanumericCol`), 10,0) ASC

output:

1
2
11
21
100
101
102
104
S-104A
S-105
S-107
S-111
1

This is from tutorials point

SELECT * FROM yourTableName ORDER BY
SUBSTR(yourColumnName FROM 1 FOR 2),
CAST(SUBSTR(yourColumnName FROM 2) AS UNSIGNED);

it is slightly different from another answer of this thread

For reference, this is the original link https://www.tutorialspoint.com/mysql-order-by-string-with-numbers

Another point regarding UNSIGNED is written here https://electrictoolbox.com/mysql-order-string-as-int/

While this has REGEX too https://www.sitepoint.com/community/t/how-to-sort-text-with-numbers-with-sql/346088/9

Robert
  • 490
  • 1
  • 5
  • 17
0
SELECT length(actual_project_name),actual_project_name,
SUBSTRING_INDEX(actual_project_name,'-',1) as aaaaaa,
SUBSTRING_INDEX(actual_project_name, '-', -1) as actual_project_number,
concat(SUBSTRING_INDEX(actual_project_name,'-',1),SUBSTRING_INDEX(actual_project_name, '-', -1)) as a
FROM ctts.test22 
order by 
SUBSTRING_INDEX(actual_project_name,'-',1) asc,cast(SUBSTRING_INDEX(actual_project_name, '-', -1) as unsigned) asc
ChrisMM
  • 8,448
  • 13
  • 29
  • 48
  • 1
    When we use LENGTH(column_name), column_name ASC based on the length function values are sorting. if length varry then sorting is not working. Use above query. – user7646370 Jan 07 '20 at 09:39
0

This is a simple example.

SELECT HEX(some_col) h        
FROM some_table 
ORDER BY h
Infomaster
  • 793
  • 7
  • 8
0

order by len(xxxxx),xxxxx

Eg:

SELECT * from customer order by len(xxxxx),xxxxx

  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Cristik Apr 09 '22 at 14:50
  • This does not provide an answer to the question. Once you have sufficient [reputation](https://stackoverflow.com/help/whats-reputation) you will be able to [comment on any post](https://stackoverflow.com/help/privileges/comment); instead, [provide answers that don't require clarification from the asker](https://meta.stackexchange.com/questions/214173/why-do-i-need-50-reputation-to-comment-what-can-i-do-instead). - [From Review](/review/late-answers/31496596) – Atish Agrawal Apr 12 '22 at 05:13
-1
SELECT 
s.id, s.name,  LENGTH(s.name) len, ASCII(s.name) ASCCCI
FROM table_name s
ORDER BY ASCCCI,len,NAME ASC;
TRiG
  • 10,148
  • 7
  • 57
  • 107
Hosain Ahmed
  • 115
  • 6
-1

Try this For ORDER BY DESC

SELECT * FROM testdata ORDER BY LENGHT(name) DESC, name DESC
Arun J
  • 687
  • 4
  • 14
  • 27
-1

Assuming varchar field containing number, decimal, alphanumeric and string, for example :

Let's suppose Column Name is "RandomValues" and Table name is "SortingTest" 

A1
120
2.23
3
0
2
Apple
Zebra
Banana 
23
86.Akjf9
Abtuo332
66.9
22
ABC

SELECT * FROM SortingTest order by IF( RandomValues REGEXP '^-?[0-9,.]+$' = 0, 9999999999 , CAST(RandomValues AS DECIMAL) ), RandomValues

Above query will do sorting on number & decimal values first and after that all alphanumeric values got sorted. 
vinod
  • 2,850
  • 1
  • 18
  • 23
-1

This will always put the values starting with a number first:

ORDER BY my_column REGEXP '^[0-9]' DESC, length(my_column  + 0), my_column ";

Works as follows:

  • Step1 - Is first char a digit? 1 if true, 0 if false, so order by this DESC
  • Step2 - How many digits is the number? Order by this ASC
  • Step3 - Order by the field itself

Input:

  ('100'),
  ('1'),
  ('10'),
  ('0'),
  ('2'),
  ('2a'),
  ('12sdfa'),
  ('12 sdfa'),
  ('Bar nah');

Output:

0
1
2
2a
10
12 sdfa
12sdfa
100
Bar nah
run_the_race
  • 1,344
  • 2
  • 36
  • 62
-1

Really problematic for my scenario...

select * from table order by lpad(column, 20, 0)

My column is a varchar, but has numeric input (1, 2, 3...) , mixed numeric (1A, 1B, 1C) and too string data (INT, SHIP)

MCunha98
  • 81
  • 3
  • 12