Using MySQL sort varchar column numerically with cast as unsigned when the column can begin or end with letters

后端 未结 2 631
我寻月下人不归
我寻月下人不归 2020-12-06 08:24

I\'ve ran into something I\'m not really sure how to handle here. I\'m building a database to store information on sports cards, and I\'m having a bit of an issue with some

2条回答
  •  一向
    一向 (楼主)
    2020-12-06 08:58

    MariaDB 10 and MySQL 8 support REGEXP_REPLACE. Using that, you can define a custom function:

    DROP FUNCTION IF EXISTS zerofill_numbers;
    CREATE FUNCTION zerofill_numbers(str TEXT, len TINYINT)
        RETURNS text
        NO SQL
        DETERMINISTIC
        RETURN REGEXP_REPLACE(
            REGEXP_REPLACE(str, '(\\d+)', LPAD('\\1', len+2, 0)),
            REPLACE('0*(\\d{$len})', '$len', len),
            '\\1'
        );
    

    Now given the following test data:

    DROP TABLE IF EXISTS `strings`;
    CREATE TABLE IF NOT EXISTS `strings` (`str` text);
    INSERT INTO `strings` (`str`) VALUES
        ('Bowman 52'),
        ('Bowman 54'),
        ('Bowman 147'),
        ('Bowman Chrome Prospects BCP32'),
        ('Bowman Chrome Prospects BCP61'),
        ('Bowman Chrome Prospects BCP97'),
        ('Bowman Chrome Prospects BCP125'),
        ('Topps 1'),
        ('Topps 3'),
        ('Topps 2a'),
        ('Topps 2b'),
        ('v9.9.3'),
        ('v9.10.3'),
        ('v11.3.4'),
        ('v9.9.11'),
        ('v11.3'),
        ('0.9'),
        ('0.11'),
        ('s09'),
        ('s11'),
        ('s0'),
        ('v9.0.1');
    

    We can sort it with:

    SELECT s.str
    FROM strings s
    ORDER BY zerofill_numbers(s.str, 10)
    

    Here is the result:

    0.9
    0.11
    Bowman 52
    Bowman 54
    Bowman 147
    Bowman Chrome Prospects BCP32
    Bowman Chrome Prospects BCP61
    Bowman Chrome Prospects BCP97
    Bowman Chrome Prospects BCP125
    s0
    s09
    s11
    Topps 1
    Topps 2a
    Topps 2b
    Topps 3
    v9.0.1
    v9.9.3
    v9.9.11
    v9.10.3
    v11.3
    v11.3.4
    

    The function will fill any number in in the string with zeros until it has the defined length.

    Note 1: This will not sort decimal numbers correctly (see 0.9 and 0.11). You should also not try to use it for signed numbers.

    Note 2: This function is based on the following answer: https://stackoverflow.com/a/46099386/5563083 - So if you find this answer helpfull, go and upvote the source.

    Note 3: If you don't want to define a custom function, you can use the same method inline:

    SELECT *
    FROM strings
    ORDER BY
      REGEXP_REPLACE(REGEXP_REPLACE(str, '(\\d+)', LPAD('\\1', 10+2, 0)), '0*(\\d{10})', '\\1')
    

提交回复
热议问题