Natural Sort in MySQL

后端 未结 21 1361
南旧
南旧 2020-11-22 02:25

Is there an elegant way to have performant, natural sorting in a MySQL database?

For example if I have this data set:

  • Final Fantasy
  • Final Fant
21条回答
  •  轻奢々
    轻奢々 (楼主)
    2020-11-22 02:58

    Regarding the best response from Richard Toth https://stackoverflow.com/a/12257917/4052357

    Watch out for UTF8 encoded strings that contain 2byte (or more) characters and numbers e.g.

    12 南新宿
    

    Using MySQL's LENGTH() in udf_NaturalSortFormat function will return the byte length of the string and be incorrect, instead use CHAR_LENGTH() which will return the correct character length.

    In my case using LENGTH() caused queries to never complete and result in 100% CPU usage for MySQL

    DROP FUNCTION IF EXISTS `udf_NaturalSortFormat`;
    DELIMITER ;;
    CREATE FUNCTION `udf_NaturalSortFormat` (`instring` varchar(4000), `numberLength` int, `sameOrderChars` char(50)) 
    RETURNS varchar(4000)
    LANGUAGE SQL
    DETERMINISTIC
    NO SQL
    SQL SECURITY INVOKER
    BEGIN
        DECLARE sortString varchar(4000);
        DECLARE numStartIndex int;
        DECLARE numEndIndex int;
        DECLARE padLength int;
        DECLARE totalPadLength int;
        DECLARE i int;
        DECLARE sameOrderCharsLen int;
    
        SET totalPadLength = 0;
        SET instring = TRIM(instring);
        SET sortString = instring;
        SET numStartIndex = udf_FirstNumberPos(instring);
        SET numEndIndex = 0;
        SET i = 1;
        SET sameOrderCharsLen = CHAR_LENGTH(sameOrderChars);
    
        WHILE (i <= sameOrderCharsLen) DO
            SET sortString = REPLACE(sortString, SUBSTRING(sameOrderChars, i, 1), ' ');
            SET i = i + 1;
        END WHILE;
    
        WHILE (numStartIndex <> 0) DO
            SET numStartIndex = numStartIndex + numEndIndex;
            SET numEndIndex = numStartIndex;
    
            WHILE (udf_FirstNumberPos(SUBSTRING(instring, numEndIndex, 1)) = 1) DO
                SET numEndIndex = numEndIndex + 1;
            END WHILE;
    
            SET numEndIndex = numEndIndex - 1;
    
            SET padLength = numberLength - (numEndIndex + 1 - numStartIndex);
    
            IF padLength < 0 THEN
                SET padLength = 0;
            END IF;
    
            SET sortString = INSERT(sortString, numStartIndex + totalPadLength, 0, REPEAT('0', padLength));
    
            SET totalPadLength = totalPadLength + padLength;
            SET numStartIndex = udf_FirstNumberPos(RIGHT(instring, CHAR_LENGTH(instring) - numEndIndex));
        END WHILE;
    
        RETURN sortString;
    END
    ;;
    

    p.s. I would have added this as a comment to the original but I don't have enough reputation (yet)

提交回复
热议问题