Sắp xếp tự nhiên trong MySQL


80

Có cách nào thanh lịch để sắp xếp tự nhiên, hiệu quả trong cơ sở dữ liệu MySQL không?

Ví dụ: nếu tôi có tập dữ liệu này:

  • Final Fantasy
  • Final Fantasy 4
  • Final Fantasy 10
  • Final Fantasy 12
  • Final Fantasy 12: Chuỗi Promathia
  • Cuộc phiêu lưu trong tưởng tượng cuối cùng
  • Nguồn gốc của Final Fantasy
  • Final Fantasy Tactics

Bất kỳ giải pháp thanh lịch nào khác ngoài việc tách tên trò chơi thành các thành phần của chúng

  • Tiêu đề : "Final Fantasy"
  • Số : "12"
  • Phụ đề : "Chuỗi Promathia"

để đảm bảo rằng chúng xuất hiện theo đúng thứ tự? (10 sau 4, không phải trước 2).

Làm như vậy là một khó khăn trong ** bởi vì thỉnh thoảng có một trò chơi khác phá vỡ cơ chế phân tích tiêu đề trò chơi đó (ví dụ: "Warhammer 40.000", "James Bond 007")


27
Chains of Promathia có liên quan đến 11.
Flame,


Câu trả lời:


20

Tôi nghĩ đây là lý do tại sao rất nhiều thứ được sắp xếp theo ngày phát hành.

Một giải pháp có thể là tạo một cột khác trong bảng của bạn cho "SortKey". Đây có thể là phiên bản tiêu đề đã được khử trùng phù hợp với mẫu bạn tạo để dễ dàng phân loại hoặc truy cập.


Tôi vừa viết một lớp cho chính xác là stackoverflow.com/a/47522040/935122
Christian

2
Đây chắc chắn là cách tiếp cận đúng, nhưng bản thân nó khó có câu trả lời!
Doin

90

Đây là một giải pháp nhanh chóng:

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

48
Thật tuyệt nếu mọi thứ đều là "Final Fantasy", nhưng nó lại đặt "Goofy" lên trước bộ FF.
fortboise

4
Giải pháp này không hoạt động mọi lúc. Đôi khi nó bị vỡ. Bạn chứ không nên sử dụng cái này: stackoverflow.com/a/12257917/384864
Borut Tomazin

6
Chồng chất kludge khi kludge: SELECT alphanumeric, integer FROM sorting_test ORDER BY SOUNDEX(alphanumeric), LENGTH(alphanumeric), alphanumeric. Nếu điều này hoàn toàn hiệu quả, đó là vì SOUNDEX loại bỏ các số một cách thuận tiện, do đó đảm bảo rằng ví dụ đó apple1có trước z1.
offby1

giải pháp tuyệt vời, cảm ơn, mặc dù tôi đã phải chuyển đổi alphanmuric, length(alphanumeric)để tránh "Goofy" trước "Final Fantasy"
Asped 29/10/14

1
Đề xuất @ offby1 chỉ hoạt động nếu văn bản được viết 100% bằng tiếng Anh vì SOUNDEX()được thiết kế để chỉ hoạt động chính xác trên các từ tiếng Anh.
Raymond Nijland

56

Chỉ tìm thấy cái này:

SELECT names FROM your_table ORDER BY games + 0 ASC

Có phải sắp xếp tự nhiên khi các số ở phía trước, cũng có thể hoạt động ở giữa.


2
Tôi đã không thử nó, nhưng tôi thực sự nghi ngờ nó. Lý do nó hoạt động với số ở phía trước là vì gamesđược sử dụng như trong ngữ cảnh số và do đó được chuyển đổi thành một số trước khi so sánh. Nếu ở giữa, nó sẽ luôn chuyển thành 0 và việc sắp xếp sẽ trở thành giả ngẫu nhiên.
manixrock

1
Đây không phải là sự sắp xếp tự nhiên. Thay vào đó hãy nhìn vào giải pháp làm việc này: stackoverflow.com/a/12257917/384864
Borut Tomazin

@fedir Điều này cũng hoạt động tốt cho tôi. Tôi thậm chí không hoàn toàn chắc chắn chính xác lý do tại sao nó hoạt động. Bất kỳ cơ hội nào về một markletp giải thích?
BizNuge

Chỉ cần có một cuộc điều tra nhanh về điều này và tôi hiểu nó. Tôi thậm chí còn không nhận ra MySQL sẽ thực hiện kiểu ép kiểu này chỉ bằng cách sử dụng một toán tử toán học trên một chuỗi! Điều thú vị là nó chỉ trả về zer0 trong trường hợp không có số nguyên nào ở phía trước chuỗi để "ép kiểu". Cám ơn vì cái này! ---> CHỌN ĐỊA CHỈ, (ĐỊA CHỈ * 1) dưới dạng _cast TỪ cơ sở TẠI ĐÓ POSTCODE THÍCH ĐƠN HÀNG 'NE1%' THEO ĐỊA CHỈ * 1 ASC, GIỚI HẠN ĐỊA CHỈ 100000;
BizNuge

1
Điều này thực sự không hoạt động khi các số ở giữa, chẳng hạn như "Final Fantasy 100" hoặc "Final Fantasy 2". "Final Fantasy 100" sẽ hiển thị đầu tiên. Tuy nhiên, nó hoạt động khi số nguyên đầu tiên là "100 Final Fantasy"
dwenaus

51

Chức năng tương tự như được đăng bởi @plalx, ​​nhưng được viết lại thành MySQL:

DROP FUNCTION IF EXISTS `udf_FirstNumberPos`;
DELIMITER ;;
CREATE FUNCTION `udf_FirstNumberPos` (`instring` varchar(4000)) 
RETURNS int
LANGUAGE SQL
DETERMINISTIC
NO SQL
SQL SECURITY INVOKER
BEGIN
    DECLARE position int;
    DECLARE tmp_position int;
    SET position = 5000;
    SET tmp_position = LOCATE('0', instring); IF (tmp_position > 0 AND tmp_position < position) THEN SET position = tmp_position; END IF; 
    SET tmp_position = LOCATE('1', instring); IF (tmp_position > 0 AND tmp_position < position) THEN SET position = tmp_position; END IF;
    SET tmp_position = LOCATE('2', instring); IF (tmp_position > 0 AND tmp_position < position) THEN SET position = tmp_position; END IF;
    SET tmp_position = LOCATE('3', instring); IF (tmp_position > 0 AND tmp_position < position) THEN SET position = tmp_position; END IF;
    SET tmp_position = LOCATE('4', instring); IF (tmp_position > 0 AND tmp_position < position) THEN SET position = tmp_position; END IF;
    SET tmp_position = LOCATE('5', instring); IF (tmp_position > 0 AND tmp_position < position) THEN SET position = tmp_position; END IF;
    SET tmp_position = LOCATE('6', instring); IF (tmp_position > 0 AND tmp_position < position) THEN SET position = tmp_position; END IF;
    SET tmp_position = LOCATE('7', instring); IF (tmp_position > 0 AND tmp_position < position) THEN SET position = tmp_position; END IF;
    SET tmp_position = LOCATE('8', instring); IF (tmp_position > 0 AND tmp_position < position) THEN SET position = tmp_position; END IF;
    SET tmp_position = LOCATE('9', instring); IF (tmp_position > 0 AND tmp_position < position) THEN SET position = tmp_position; END IF;

    IF (position = 5000) THEN RETURN 0; END IF;
    RETURN position;
END
;;

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
;;

Sử dụng:

SELECT name FROM products ORDER BY udf_NaturalSortFormat(name, 10, ".")

4
Đây là giải pháp duy nhất thực sự hiệu quả. Tôi cũng đã thử nghiệm mã drupals nhưng đôi khi nó không thành công. Cảm ơn anh bạn!
Borut Tomazin

Có ai sử dụng cái này trên các bảng lớn hơn 10 triệu không?
Mark Steudel

2
@MarkSteudel Chúng tôi sử dụng một hàm tương tự như hàm này (mặc dù không chính xác) để sắp xếp tự nhiên trên một số bảng, bảng lớn nhất trong số đó là ~ 5 triệu hàng. Tuy nhiên, chúng tôi không gọi nó trực tiếp trong các truy vấn của mình mà sử dụng nó để đặt giá trị của một nat_namecột. Chúng tôi sử dụng trình kích hoạt để chạy chức năng mỗi khi một hàng được cập nhật. Cách tiếp cận này cho phép bạn sắp xếp tự nhiên mà không có chi phí hiệu suất thực tế với chi phí của một cột bổ sung.
Jacob

công trình này, phân loại số trước chữ cái, và có thể được thực hiện trong Drupal sử dụng hook_views_query_alter, sử dụng một cái gì đó tương tự như nàyif ($query->orderby[0]["field"] === "node_field_data.title") { $orderBySql = " udf_NaturalSortFormat(node_field_data.title, 10, '.') "; $query->orderby = []; $query->addOrderBy(NULL, $orderBySql, $query->orderby[0]["direction"], 'title_natural'); array_unshift($query->orderby, end($query->orderby)); }
realgt

16

Tôi đã viết hàm này cho MSSQL 2000 một thời gian trước:

/**
 * Returns a string formatted for natural sorting. This function is very useful when having to sort alpha-numeric strings.
 *
 * @author Alexandre Potvin Latreille (plalx)
 * @param {nvarchar(4000)} string The formatted string.
 * @param {int} numberLength The length each number should have (including padding). This should be the length of the longest number. Defaults to 10.
 * @param {char(50)} sameOrderChars A list of characters that should have the same order. Ex: '.-/'. Defaults to empty string.
 *
 * @return {nvarchar(4000)} A string for natural sorting.
 * Example of use: 
 * 
 *      SELECT Name FROM TableA ORDER BY Name
 *  TableA (unordered)              TableA (ordered)
 *  ------------                    ------------
 *  ID  Name                    ID  Name
 *  1.  A1.                 1.  A1-1.       
 *  2.  A1-1.                   2.  A1.
 *  3.  R1      -->         3.  R1
 *  4.  R11                 4.  R11
 *  5.  R2                  5.  R2
 *
 *  
 *  As we can see, humans would expect A1., A1-1., R1, R2, R11 but that's not how SQL is sorting it.
 *  We can use this function to fix this.
 *
 *      SELECT Name FROM TableA ORDER BY dbo.udf_NaturalSortFormat(Name, default, '.-')
 *  TableA (unordered)              TableA (ordered)
 *  ------------                    ------------
 *  ID  Name                    ID  Name
 *  1.  A1.                 1.  A1.     
 *  2.  A1-1.                   2.  A1-1.
 *  3.  R1      -->         3.  R1
 *  4.  R11                 4.  R2
 *  5.  R2                  5.  R11
 */
CREATE FUNCTION dbo.udf_NaturalSortFormat(
    @string nvarchar(4000),
    @numberLength int = 10,
    @sameOrderChars char(50) = ''
)
RETURNS varchar(4000)
AS
BEGIN
    DECLARE @sortString varchar(4000),
        @numStartIndex int,
        @numEndIndex int,
        @padLength int,
        @totalPadLength int,
        @i int,
        @sameOrderCharsLen int;

    SELECT 
        @totalPadLength = 0,
        @string = RTRIM(LTRIM(@string)),
        @sortString = @string,
        @numStartIndex = PATINDEX('%[0-9]%', @string),
        @numEndIndex = 0,
        @i = 1,
        @sameOrderCharsLen = LEN(@sameOrderChars);

    -- Replace all char that has to have the same order by a space.
    WHILE (@i <= @sameOrderCharsLen)
    BEGIN
        SET @sortString = REPLACE(@sortString, SUBSTRING(@sameOrderChars, @i, 1), ' ');
        SET @i = @i + 1;
    END

    -- Pad numbers with zeros.
    WHILE (@numStartIndex <> 0)
    BEGIN
        SET @numStartIndex = @numStartIndex + @numEndIndex;
        SET @numEndIndex = @numStartIndex;

        WHILE(PATINDEX('[0-9]', SUBSTRING(@string, @numEndIndex, 1)) = 1)
        BEGIN
            SET @numEndIndex = @numEndIndex + 1;
        END

        SET @numEndIndex = @numEndIndex - 1;

        SET @padLength = @numberLength - (@numEndIndex + 1 - @numStartIndex);

        IF @padLength < 0
        BEGIN
            SET @padLength = 0;
        END

        SET @sortString = STUFF(
            @sortString,
            @numStartIndex + @totalPadLength,
            0,
            REPLICATE('0', @padLength)
        );

        SET @totalPadLength = @totalPadLength + @padLength;
        SET @numStartIndex = PATINDEX('%[0-9]%', RIGHT(@string, LEN(@string) - @numEndIndex));
    END

    RETURN @sortString;
END

GO

@MarkSteudel Bạn sẽ phải thử và tự mình kiểm tra. Tệ hơn nữa, bạn luôn có thể lưu vào bộ nhớ cache các giá trị đã định dạng. Đó có lẽ là những gì tôi sẽ làm đối với các bảng lớn vì bạn cũng có thể lập chỉ mục trường.
plalx

15

MySQL không cho phép loại "sắp xếp tự nhiên" này, vì vậy có vẻ như cách tốt nhất để có được những gì bạn đang theo đuổi là tách dữ liệu của bạn được thiết lập như bạn đã mô tả ở trên (trường id riêng biệt, v.v.) hoặc không thành công đó, thực hiện sắp xếp dựa trên phần tử không phải là tiêu đề, phần tử được lập chỉ mục trong db của bạn (ngày tháng, id được chèn trong db, v.v.).

Việc để db thực hiện việc sắp xếp cho bạn hầu như luôn luôn nhanh hơn việc đọc các tập dữ liệu lớn sang ngôn ngữ lập trình bạn chọn và sắp xếp nó ở đó, vì vậy nếu bạn có bất kỳ quyền kiểm soát nào trên lược đồ db ở đây, thì hãy xem thêm các trường được sắp xếp dễ dàng như được mô tả ở trên, nó sẽ giúp bạn tiết kiệm rất nhiều rắc rối và bảo trì về lâu dài.

Các yêu cầu thêm "sắp xếp tự nhiên" thỉnh thoảng xuất hiện trên các diễn đàn thảo luậnlỗi MySQL , và nhiều giải pháp xoay quanh việc loại bỏ các phần cụ thể của dữ liệu của bạn và truyền chúng cho một phần của truy vấn, ví dụ:ORDER BY

SELECT * FROM table ORDER BY CAST(mid(name, 6, LENGTH(c) -5) AS unsigned) 

Loại giải pháp này có thể được tạo ra để hoạt động trên ví dụ Final Fantasy của bạn ở trên, nhưng không đặc biệt linh hoạt và không có khả năng mở rộng rõ ràng cho một tập dữ liệu bao gồm, chẳng hạn như "Warhammer 40.000" và "James Bond 007" .


9

Vì vậy, mặc dù tôi biết rằng bạn đã tìm được câu trả lời thỏa đáng, nhưng tôi đã phải vật lộn với vấn đề này trong một thời gian và trước đây chúng tôi đã xác định rằng nó không thể được thực hiện một cách hợp lý trong SQL và chúng tôi sẽ phải sử dụng javascript trên JSON mảng.

Đây là cách tôi giải quyết nó chỉ bằng cách sử dụng SQL. Hy vọng rằng điều này sẽ hữu ích cho những người khác:

Tôi đã có dữ liệu như:

Cảnh 1
Cảnh 1A
Cảnh 1B
Cảnh 2A
Cảnh 3
...
Cảnh 101
Cảnh XXA1
Cảnh XXA2

Tôi thực sự đã không "cast" mọi thứ mặc dù tôi cho rằng điều đó cũng có thể hiệu quả.

Đầu tiên tôi thay thế các phần không thay đổi trong dữ liệu, trong trường hợp này là "Cảnh", sau đó thực hiện LPAD để sắp xếp mọi thứ. Điều này dường như cho phép các chuỗi alpha sắp xếp đúng cách cũng như các chuỗi được đánh số.

ORDER BYMệnh đề của tôi có dạng như sau:

ORDER BY LPAD(REPLACE(`table`.`column`,'Scene ',''),10,'0')

Rõ ràng là điều này không giúp được gì cho vấn đề ban đầu vốn không thống nhất - nhưng tôi tưởng tượng điều này có thể sẽ hiệu quả với nhiều vấn đề liên quan khác, vì vậy hãy đặt nó ra khỏi đó.


Các LPAD()gợi ý was very helpful. Tôi có các từ và số để sắp xếp, LPADtôi có thể sắp xếp các số một cách tự nhiên. Và sử dụng CONCATtôi bỏ qua các số không phải. Truy vấn của tôi trông như thế này (bí danh là cột để sắp xếp): IF(CONCAT("",alias*1)=alias, LPAD(alias,5,"0"), alias) ASC;👍
Kai Noack

6
  1. Thêm Khóa sắp xếp (Xếp hạng) trong bảng của bạn. ORDER BY rank

  2. Sử dụng cột "Ngày phát hành". ORDER BY release_date

  3. Khi trích xuất dữ liệu từ SQL, hãy đặt đối tượng của bạn thực hiện việc sắp xếp, ví dụ: nếu trích xuất vào một Tập hợp, hãy đặt nó thành một TreeSet và làm cho mô hình dữ liệu của bạn triển khai Có thể so sánh và thực hiện thuật toán sắp xếp tự nhiên ở đây (sắp xếp chèn sẽ đủ nếu bạn đang sử dụng một ngôn ngữ không có bộ sưu tập) vì bạn sẽ đọc từng hàng từ SQL khi bạn tạo mô hình của mình và chèn nó vào bộ sưu tập)


5

Về câu trả lời tốt nhất từ ​​Richard Toth https://stackoverflow.com/a/12257917/4052357

Chú ý các chuỗi được mã hóa UTF8 có chứa 2byte (hoặc nhiều hơn) ký tự và số, ví dụ:

12 南新宿

Sử dụng hàm LENGTH()trong của MySQL udf_NaturalSortFormatsẽ trả về độ dài byte của chuỗi và không chính xác, thay vào đó hãy sử dụng hàm CHAR_LENGTH()này sẽ trả về độ dài ký tự chính xác.

Trong trường hợp của tôi, sử dụng LENGTH()các truy vấn gây ra không bao giờ hoàn thành và dẫn đến việc sử dụng 100% CPU cho 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
;;

ps Tôi có thể đã thêm điều này như một nhận xét cho bản gốc nhưng tôi không có đủ danh tiếng (chưa)


4

Thêm trường cho "khóa sắp xếp" có tất cả các chuỗi chữ số không được đệm vào một độ dài cố định và sau đó sắp xếp trên trường đó.

Nếu bạn có thể có chuỗi chữ số dài, một phương pháp khác là thêm trước số chữ số (độ rộng cố định, không đệm) vào mỗi chuỗi chữ số. Ví dụ: nếu bạn không có nhiều hơn 99 chữ số liên tiếp, thì đối với "Super Blast 10 Ultra", phím sắp xếp sẽ là "Super Blast 0210 Ultra".


4

Để đặt hàng:
0
1
2
10
23
101
205
1000
a
aac
b
casdsadsa
css

Sử dụng truy vấn này:

LỰA CHỌN 
    tên cột dọc 
TỪ 
    tên_bảng 
ĐẶT BỞI
    column_name REGEXP '^ \ d * [^ \ da-z & \. \' \ - \ "\! \ @ \ # \ $ \% \% \ ^ \ * \ (\) \; \: \\, \? \ / \ ~ \ `\ | \ _ \ -] 'DESC, 
    column_name + 0, 
    tên cột dọc;

Thật không may phá vỡ này xuống nếu bạn thêm giá trị trong như a1, a2, a11, vv ...
random_user_name

4

Nếu bạn không muốn phát minh lại bánh xe hoặc đau đầu với nhiều mã không hoạt động, chỉ cần sử dụng Drupal Natural Sort ... Chỉ cần chạy SQL được nén (MySQL hoặc Postgre), vậy là xong. Khi thực hiện một truy vấn, chỉ cần đặt hàng bằng cách sử dụng:

... ORDER BY natsort_canon(column_name, 'natural')

Cảm ơn vì điều này, tôi đã thử tất cả các loại giải pháp (ha ha xem tôi đã làm gì ở đó?) Nhưng không có giải pháp nào thực sự hiệu quả với tất cả dữ liệu tôi có. Chức năng drupal hoạt động như một sự quyến rũ. Cảm ơn vì đăng.
Ben Hitchcock

điều này hoạt động nhưng sắp xếp các số ở cuối (AZ rồi đến 0-9)
realgt

4

Một tùy chọn khác là thực hiện sắp xếp trong bộ nhớ sau khi lấy dữ liệu từ mysql. Mặc dù nó không phải là lựa chọn tốt nhất từ ​​quan điểm hiệu suất, nhưng nếu bạn không sắp xếp các danh sách khổng lồ, bạn sẽ ổn.

Nếu bạn xem bài đăng của Jeff, bạn có thể tìm thấy rất nhiều thuật toán cho ngôn ngữ mà bạn có thể đang làm việc. Sắp xếp cho con người: Thứ tự sắp xếp tự nhiên


2

Bạn cũng có thể tạo "cột sắp xếp" theo cách động:

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

Bằng cách đó, bạn có thể tạo các nhóm để sắp xếp.

Trong truy vấn của mình, tôi muốn có dấu '-' trước mọi thứ, sau đó là các con số, rồi đến văn bản. Điều này có thể dẫn đến một cái gì đó như:

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

Bằng cách đó, bạn không phải duy trì cột sắp xếp theo đúng thứ tự khi thêm dữ liệu. Bạn cũng có thể thay đổi thứ tự sắp xếp của mình tùy thuộc vào những gì bạn cần.


Tôi không biết nó sẽ biểu diễn như thế nào. Tôi đang sử dụng nó mọi lúc mà không có bất kỳ sự bất tiện nào. Cơ sở dữ liệu của tôi không lớn tho.
antoine

1

Nếu bạn đang sử dụng PHP, bạn có thể thực hiện sắp xếp tự nhiên trong php.

$keys = array();
$values = array();
foreach ($results as $index => $row) {
   $key = $row['name'].'__'.$index; // Add the index to create an unique key.
   $keys[] = $key;
   $values[$key] = $row; 
}
natsort($keys);
$sortedValues = array(); 
foreach($keys as $index) {
  $sortedValues[] = $values[$index]; 
}

Tôi hy vọng MySQL sẽ triển khai sắp xếp tự nhiên trong một phiên bản tương lai, nhưng yêu cầu tính năng (# 1588) đã mở từ năm 2003, Vì vậy, tôi sẽ không nín thở.


Về mặt lý thuyết thì điều đó là có thể, nhưng trước tiên tôi cần đọc tất cả các bản ghi cơ sở dữ liệu vào máy chủ web của mình.
BlaM

Ngoài ra, hãy xem xét: usort($mydata, function ($item1, $item2) { return strnatcmp($item1['key'], $item2['key']); });(Tôi có một mảng kết hợp và sắp xếp theo khóa.) Tham khảo: stackoverflow.com/q/12426825/1066234
Kai Noack

1

Phiên bản đơn giản không phải udf của phản hồi tốt nhất của @ plaix / Richard Toth / Luke Hoggett, chỉ hoạt động cho số nguyên đầu tiên trong trường, là

SELECT name,
LEAST(
    IFNULL(NULLIF(LOCATE('0', name), 0), ~0),
    IFNULL(NULLIF(LOCATE('1', name), 0), ~0),
    IFNULL(NULLIF(LOCATE('2', name), 0), ~0),
    IFNULL(NULLIF(LOCATE('3', name), 0), ~0),
    IFNULL(NULLIF(LOCATE('4', name), 0), ~0),
    IFNULL(NULLIF(LOCATE('5', name), 0), ~0),
    IFNULL(NULLIF(LOCATE('6', name), 0), ~0),
    IFNULL(NULLIF(LOCATE('7', name), 0), ~0),
    IFNULL(NULLIF(LOCATE('8', name), 0), ~0),
    IFNULL(NULLIF(LOCATE('9', name), 0), ~0)
) AS first_int
FROM table
ORDER BY IF(first_int = ~0, name, CONCAT(
    SUBSTR(name, 1, first_int - 1),
    LPAD(CAST(SUBSTR(name, first_int) AS UNSIGNED), LENGTH(~0), '0'),
    SUBSTR(name, first_int + LENGTH(CAST(SUBSTR(name, first_int) AS UNSIGNED)))
)) ASC

1

Tôi đã thử một số giải pháp nhưng thực sự nó rất đơn giản:

SELECT test_column FROM test_table ORDER BY LENGTH(test_column) DESC, test_column DESC

/* 
Result 
--------
value_1
value_2
value_3
value_4
value_5
value_6
value_7
value_8
value_9
value_10
value_11
value_12
value_13
value_14
value_15
...
*/

1
Hoạt động rất tốt để sắp xếp các số theo định dạng 23-4244. Cảm ơn :)
Pyton

1
chỉ hoạt động với dữ liệu thử nghiệm này vì các chuỗi trước số đều giống nhau. Hãy thử gắn bó với một giá trị z_99ở đó và nó sẽ được đưa lên hàng đầu nhưng sẽ zđến sau v.
Samuel Neff,

@SamuelNeff vui lòng xem SQL: ORDER BY LENGTH (test_column) DESC, test_column DESC nên có, vì nó sẽ sắp xếp theo độ dài của cột trước. Này hoạt động tốt sắp xếp một nhóm tiền tố của bảng mà nếu không bạn sẽ không thể để sắp xếp với chỉ "test_column DESC"
Tarik

1

Rất nhiều câu trả lời khác mà tôi thấy ở đây (và trong các câu hỏi trùng lặp) về cơ bản chỉ hoạt động đối với dữ liệu được định dạng rất cụ thể, ví dụ: một chuỗi hoàn toàn là số hoặc có tiền tố chữ cái có độ dài cố định. Điều này sẽ không hoạt động trong trường hợp chung.

Đúng là không thực sự có bất kỳ cách nào để triển khai 100% nat-sort chung trong MySQL, bởi vì để thực hiện nó, điều bạn thực sự cần là một hàm so sánh đã sửa đổi , chuyển đổi giữa sắp xếp từ vựng của chuỗi và sắp xếp số nếu / khi nó gặp phải một số. Mã như vậy có thể triển khai bất kỳ thuật toán nào bạn có thể mong muốn để nhận ra và so sánh các phần số trong hai chuỗi. Tuy nhiên, thật không may, hàm so sánh trong MySQL nằm trong mã của nó và người dùng không thể thay đổi được.

Điều này dẫn đến một sự tấn công nào đó, trong đó bạn cố gắng tạo một khóa sắp xếp cho chuỗi của mình, trong đó các phần số được định dạng lại sao cho loại từ vựng chuẩn thực sự sắp xếp chúng theo cách bạn muốn .

Đối với các số nguyên thuần túy có đến một số chữ số tối đa, giải pháp rõ ràng là chỉ cần đệm trái chúng bằng các số không để chúng đều có chiều rộng cố định. Đây là cách tiếp cận được thực hiện bởi plugin Drupal và các giải pháp của @plalx / @RichardToth. (@Christian có một giải pháp khác và phức tạp hơn nhiều, nhưng nó không mang lại lợi ích nào mà tôi có thể thấy).

Như @tye đã chỉ ra, bạn có thể cải thiện điều này bằng cách thêm độ dài chữ số cố định cho mỗi số, thay vì chỉ cần đệm bên trái nó. Mặc dù vậy, còn rất nhiều điều bạn có thể cải thiện, ngay cả khi có những hạn chế về cơ bản là một vụ hack khó xử. Tuy nhiên, dường như không có bất kỳ giải pháp được xây dựng sẵn nào ngoài đó!

Ví dụ, những gì về:

  • Dấu cộng và dấu trừ? +10 so với 10 so với -10
  • Số thập phân? 8,2, 8,5, 1,006, 0,75
  • Số không hàng đầu? 020, 030, 00000922
  • Ngàn ngăn cách? "1.001 con chó đốm" so với "1001 con chó đốm"
  • Số phiên bản? MariaDB v10.3.18 so với MariaDB v10.3.3
  • Con số rất dài? 103.768.276.592.092.364.859.236.487.687.870.234.598,55

Mở rộng trên phương thức của @ tye, tôi đã tạo một hàm được lưu trữ NatSortKey () khá nhỏ gọn sẽ chuyển đổi một chuỗi tùy ý thành khóa nat-sort và xử lý tất cả các trường hợp trên, hiệu quả hợp lý và bảo toàn tổng số sắp xếp- thứ tự (không có hai chuỗi khác nhau có khóa sắp xếp so sánh bằng nhau). Tham số thứ hai có thể được sử dụng để giới hạn số lượng số được xử lý trong mỗi chuỗi (chẳng hạn như 10 số đầu tiên), có thể được sử dụng để đảm bảo đầu ra phù hợp với độ dài nhất định.

LƯU Ý: Chuỗi khóa sắp xếp được tạo với một giá trị nhất định của tham số thứ 2 này chỉ nên được sắp xếp so với các chuỗi khác được tạo với cùng giá trị cho tham số, nếu không chúng có thể không được sắp xếp chính xác!

Bạn có thể sử dụng nó trực tiếp khi đặt hàng, ví dụ:

SELECT myString FROM myTable ORDER BY NatSortKey(myString,0);  ### 0 means process all numbers - resulting sort key might be quite long for certain inputs

Nhưng để sắp xếp hiệu quả các bảng lớn, tốt hơn nên lưu trữ trước khóa sắp xếp trong một cột khác (có thể có chỉ mục trên đó):

INSERT INTO myTable (myString,myStringNSK) VALUES (@theStringValue,NatSortKey(@theStringValue,10)), ...
...
SELECT myString FROM myTable ORDER BY myStringNSK;

[Tốt nhất, bạn nên làm cho điều này xảy ra tự động bằng cách tạo cột khóa dưới dạng cột được lưu trữ được tính toán, sử dụng một cái gì đó như:

CREATE TABLE myTable (
...
myString varchar(100),
myStringNSK varchar(150) AS (NatSortKey(myString,10)) STORED,
...
KEY (myStringNSK),
...);

Nhưng hiện tại cả MySQL và MariaDB đều không cho phép các hàm được lưu trữ trong các cột được tính toán , vì vậy rất tiếc là bạn chưa thể thực hiện điều này .]


Chức năng của tôi chỉ ảnh hưởng đến việc sắp xếp các số . Nếu bạn muốn thực hiện những việc chuẩn hóa sắp xếp khác, chẳng hạn như xóa tất cả các dấu câu hoặc cắt bớt khoảng trắng khỏi mỗi đầu hoặc thay thế các chuỗi nhiều khoảng trắng bằng các khoảng trắng, bạn có thể mở rộng hàm hoặc có thể thực hiện trước hoặc sau NatSortKey()là áp dụng cho dữ liệu của bạn. (Tôi khuyên bạn nên sử dụng REGEXP_REPLACE()cho mục đích này).

Tôi cho rằng nó cũng có phần thiên về Anh ngữ '.' cho dấu thập phân và ',' cho dấu phân tách hàng nghìn, nhưng nó phải đủ dễ dàng để sửa đổi nếu bạn muốn đảo ngược hoặc nếu bạn muốn chuyển đổi dưới dạng tham số.

Nó có thể được cải thiện hơn nữa theo những cách khác; ví dụ, nó hiện sắp xếp các số âm theo giá trị tuyệt đối, vì vậy -1 đứng trước -2, thay vì ngược lại. Cũng không có cách nào để chỉ định thứ tự sắp xếp DESC cho các số trong khi vẫn giữ lại sắp xếp từ điển ASC cho văn bản. Cả hai vấn đề này có thể được khắc phục với một chút công việc; Tôi sẽ cập nhật mã nếu / khi tôi nhận được thời gian.

Có rất nhiều chi tiết khác cần lưu ý - bao gồm một số phụ thuộc quan trọng vào chaset và collation mà bạn đang sử dụng - nhưng tôi đã đặt tất cả chúng vào một khối nhận xét trong mã SQL. Vui lòng đọc kỹ điều này trước khi sử dụng chức năng cho chính mình!

Vì vậy, đây là mã. Nếu bạn tìm thấy lỗi hoặc có cải tiến mà tôi chưa đề cập, vui lòng cho tôi biết trong phần bình luận!


delimiter $$
CREATE DEFINER=CURRENT_USER FUNCTION NatSortKey (s varchar(100), n int) RETURNS varchar(350) DETERMINISTIC
BEGIN
/****
  Converts numbers in the input string s into a format such that sorting results in a nat-sort.
  Numbers of up to 359 digits (before the decimal point, if one is present) are supported.  Sort results are undefined if the input string contains numbers longer than this.
  For n>0, only the first n numbers in the input string will be converted for nat-sort (so strings that differ only after the first n numbers will not nat-sort amongst themselves).
  Total sort-ordering is preserved, i.e. if s1!=s2, then NatSortKey(s1,n)!=NatSortKey(s2,n), for any given n.
  Numbers may contain ',' as a thousands separator, and '.' as a decimal point.  To reverse these (as appropriate for some European locales), the code would require modification.
  Numbers preceded by '+' sort with numbers not preceded with either a '+' or '-' sign.
  Negative numbers (preceded with '-') sort before positive numbers, but are sorted in order of ascending absolute value (so -7 sorts BEFORE -1001).
  Numbers with leading zeros sort after the same number with no (or fewer) leading zeros.
  Decimal-part-only numbers (like .75) are recognised, provided the decimal point is not immediately preceded by either another '.', or by a letter-type character.
  Numbers with thousand separators sort after the same number without them.
  Thousand separators are only recognised in numbers with no leading zeros that don't immediately follow a ',', and when they format the number correctly.
  (When not recognised as a thousand separator, a ',' will instead be treated as separating two distinct numbers).
  Version-number-like sequences consisting of 3 or more numbers separated by '.' are treated as distinct entities, and each component number will be nat-sorted.
  The entire entity will sort after any number beginning with the first component (so e.g. 10.2.1 sorts after both 10 and 10.995, but before 11)
  Note that The first number component in an entity like this is also permitted to contain thousand separators.

  To achieve this, numbers within the input string are prefixed and suffixed according to the following format:
  - The number is prefixed by a 2-digit base-36 number representing its length, excluding leading zeros.  If there is a decimal point, this length only includes the integer part of the number.
  - A 3-character suffix is appended after the number (after the decimals if present).
    - The first character is a space, or a '+' sign if the number was preceded by '+'.  Any preceding '+' sign is also removed from the front of the number.
    - This is followed by a 2-digit base-36 number that encodes the number of leading zeros and whether the number was expressed in comma-separated form (e.g. 1,000,000.25 vs 1000000.25)
    - The value of this 2-digit number is: (number of leading zeros)*2 + (1 if comma-separated, 0 otherwise)
  - For version number sequences, each component number has the prefix in front of it, and the separating dots are removed.
    Then there is a single suffix that consists of a ' ' or '+' character, followed by a pair base-36 digits for each number component in the sequence.

  e.g. here is how some simple sample strings get converted:
  'Foo055' --> 'Foo0255 02'
  'Absolute zero is around -273 centigrade' --> 'Absolute zero is around -03273 00 centigrade'
  'The $1,000,000 prize' --> 'The $071000000 01 prize'
  '+99.74 degrees' --> '0299.74+00 degrees'
  'I have 0 apples' --> 'I have 00 02 apples'
  '.5 is the same value as 0000.5000' --> '00.5 00 is the same value as 00.5000 08'
  'MariaDB v10.3.0018' --> 'MariaDB v02100130218 000004'

  The restriction to numbers of up to 359 digits comes from the fact that the first character of the base-36 prefix MUST be a decimal digit, and so the highest permitted prefix value is '9Z' or 359 decimal.
  The code could be modified to handle longer numbers by increasing the size of (both) the prefix and suffix.
  A higher base could also be used (by replacing CONV() with a custom function), provided that the collation you are using sorts the "digits" of the base in the correct order, starting with 0123456789.
  However, while the maximum number length may be increased this way, note that the technique this function uses is NOT applicable where strings may contain numbers of unlimited length.

  The function definition does not specify the charset or collation to be used for string-type parameters or variables:  The default database charset & collation at the time the function is defined will be used.
  This is to make the function code more portable.  However, there are some important restrictions:

  - Collation is important here only when comparing (or storing) the output value from this function, but it MUST order the characters " +0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" in that order for the natural sort to work.
    This is true for most collations, but not all of them, e.g. in Lithuanian 'Y' comes before 'J' (according to Wikipedia).
    To adapt the function to work with such collations, replace CONV() in the function code with a custom function that emits "digits" above 9 that are characters ordered according to the collation in use.

  - For efficiency, the function code uses LENGTH() rather than CHAR_LENGTH() to measure the length of strings that consist only of digits 0-9, '.', and ',' characters.
    This works for any single-byte charset, as well as any charset that maps standard ASCII characters to single bytes (such as utf8 or utf8mb4).
    If using a charset that maps these characters to multiple bytes (such as, e.g. utf16 or utf32), you MUST replace all instances of LENGTH() in the function definition with CHAR_LENGTH()

  Length of the output:

  Each number converted adds 5 characters (2 prefix + 3 suffix) to the length of the string. n is the maximum count of numbers to convert;
  This parameter is provided as a means to limit the maximum output length (to input length + 5*n).
  If you do not require the total-ordering property, you could edit the code to use suffixes of 1 character (space or plus) only; this would reduce the maximum output length for any given n.
  Since a string of length L has at most ((L+1) DIV 2) individual numbers in it (every 2nd character a digit), for n<=0 the maximum output length is (inputlength + 5*((inputlength+1) DIV 2))
  So for the current input length of 100, the maximum output length is 350.
  If changing the input length, the output length must be modified according to the above formula.  The DECLARE statements for x,y,r, and suf must also be modified, as the code comments indicate.
****/
  DECLARE x,y varchar(100);            # need to be same length as input s
  DECLARE r varchar(350) DEFAULT '';   # return value:  needs to be same length as return type
  DECLARE suf varchar(101);   # suffix for a number or version string. Must be (((inputlength+1) DIV 2)*2 + 1) chars to support version strings (e.g. '1.2.33.5'), though it's usually just 3 chars. (Max version string e.g. 1.2. ... .5 has ((length of input + 1) DIV 2) numeric components)
  DECLARE i,j,k int UNSIGNED;
  IF n<=0 THEN SET n := -1; END IF;   # n<=0 means "process all numbers"
  LOOP
    SET i := REGEXP_INSTR(s,'\\d');   # find position of next digit
    IF i=0 OR n=0 THEN RETURN CONCAT(r,s); END IF;   # no more numbers to process -> we're done
    SET n := n-1, suf := ' ';
    IF i>1 THEN
      IF SUBSTRING(s,i-1,1)='.' AND (i=2 OR SUBSTRING(s,i-2,1) RLIKE '[^.\\p{L}\\p{N}\\p{M}\\x{608}\\x{200C}\\x{200D}\\x{2100}-\\x{214F}\\x{24B6}-\\x{24E9}\\x{1F130}-\\x{1F149}\\x{1F150}-\\x{1F169}\\x{1F170}-\\x{1F189}]') AND (SUBSTRING(s,i) NOT RLIKE '^\\d++\\.\\d') THEN SET i:=i-1; END IF;   # Allow decimal number (but not version string) to begin with a '.', provided preceding char is neither another '.', nor a member of the unicode character classes: "Alphabetic", "Letter", "Block=Letterlike Symbols" "Number", "Mark", "Join_Control"
      IF i>1 AND SUBSTRING(s,i-1,1)='+' THEN SET suf := '+', j := i-1; ELSE SET j := i; END IF;   # move any preceding '+' into the suffix, so equal numbers with and without preceding "+" signs sort together
      SET r := CONCAT(r,SUBSTRING(s,1,j-1)); SET s = SUBSTRING(s,i);   # add everything before the number to r and strip it from the start of s; preceding '+' is dropped (not included in either r or s)
    END IF;
    SET x := REGEXP_SUBSTR(s,IF(SUBSTRING(s,1,1) IN ('0','.') OR (SUBSTRING(r,-1)=',' AND suf=' '),'^\\d*+(?:\\.\\d++)*','^(?:[1-9]\\d{0,2}(?:,\\d{3}(?!\\d))++|\\d++)(?:\\.\\d++)*+'));   # capture the number + following decimals (including multiple consecutive '.<digits>' sequences)
    SET s := SUBSTRING(s,LENGTH(x)+1);   # NOTE: LENGTH() can be safely used instead of CHAR_LENGTH() here & below PROVIDED we're using a charset that represents digits, ',' and '.' characters using single bytes (e.g. latin1, utf8)
    SET i := INSTR(x,'.');
    IF i=0 THEN SET y := ''; ELSE SET y := SUBSTRING(x,i); SET x := SUBSTRING(x,1,i-1); END IF;   # move any following decimals into y
    SET i := LENGTH(x);
    SET x := REPLACE(x,',','');
    SET j := LENGTH(x);
    SET x := TRIM(LEADING '0' FROM x);   # strip leading zeros
    SET k := LENGTH(x);
    SET suf := CONCAT(suf,LPAD(CONV(LEAST((j-k)*2,1294) + IF(i=j,0,1),10,36),2,'0'));   # (j-k)*2 + IF(i=j,0,1) = (count of leading zeros)*2 + (1 if there are thousands-separators, 0 otherwise)  Note the first term is bounded to <= base-36 'ZY' as it must fit within 2 characters
    SET i := LOCATE('.',y,2);
    IF i=0 THEN
      SET r := CONCAT(r,LPAD(CONV(LEAST(k,359),10,36),2,'0'),x,y,suf);   # k = count of digits in number, bounded to be <= '9Z' base-36
    ELSE   # encode a version number (like 3.12.707, etc)
      SET r := CONCAT(r,LPAD(CONV(LEAST(k,359),10,36),2,'0'),x);   # k = count of digits in number, bounded to be <= '9Z' base-36
      WHILE LENGTH(y)>0 AND n!=0 DO
        IF i=0 THEN SET x := SUBSTRING(y,2); SET y := ''; ELSE SET x := SUBSTRING(y,2,i-2); SET y := SUBSTRING(y,i); SET i := LOCATE('.',y,2); END IF;
        SET j := LENGTH(x);
        SET x := TRIM(LEADING '0' FROM x);   # strip leading zeros
        SET k := LENGTH(x);
        SET r := CONCAT(r,LPAD(CONV(LEAST(k,359),10,36),2,'0'),x);   # k = count of digits in number, bounded to be <= '9Z' base-36
        SET suf := CONCAT(suf,LPAD(CONV(LEAST((j-k)*2,1294),10,36),2,'0'));   # (j-k)*2 = (count of leading zeros)*2, bounded to fit within 2 base-36 digits
        SET n := n-1;
      END WHILE;
      SET r := CONCAT(r,y,suf);
    END IF;
  END LOOP;
END
$$
delimiter ;

Tôi là người mới bắt đầu sử dụng MySQL và đã thử điều này. Gặp lỗi này: "# 1305 - FUNCTION mydatabase.REGEXP_INSTR không tồn tại". Bất kỳ ý tưởng?
John T

Đối với bất kỳ thành viên mới nào khác ngoài đó. Tôi chưa cài đặt MySQL 8.0. Nó cần cho REGEXP_INSTR (và các nội dung REGEXP khác).
John T

Vừa sửa một lỗi nghiêm trọng trong NatSortKey: có một ký tự regex không chính xác. Nếu bạn đã tự mình sử dụng chức năng này, vui lòng cập nhật mã của bạn!
Doin


0

Đây là một cách đơn giản nếu tiêu đề chỉ có phiên bản dưới dạng số:

ORDER BY CAST(REGEXP_REPLACE(title, "[a-zA-Z]+", "") AS INT)';

Nếu không, bạn có thể sử dụng SQL đơn giản nếu bạn sử dụng một mẫu (mẫu này sử dụng dấu # trước phiên bản):

create table titles(title);

insert into titles (title) values 
('Final Fantasy'),
('Final Fantasy #03'),
('Final Fantasy #11'),
('Final Fantasy #10'),
('Final Fantasy #2'),
('Bond 007 ##2'),
('Final Fantasy #01'),
('Bond 007'),
('Final Fantasy #11}');

select REGEXP_REPLACE(title, "#([0-9]+)", "\\1") as title from titles
ORDER BY REGEXP_REPLACE(title, "#[0-9]+", ""),
CAST(REGEXP_REPLACE(title, ".*#([0-9]+).*", "\\1") AS INT);     
+-------------------+
| title             |
+-------------------+
| Bond 007          |
| Bond 007 #2       |
| Final Fantasy     |
| Final Fantasy 01  |
| Final Fantasy 2   |
| Final Fantasy 03  |
| Final Fantasy 10  |
| Final Fantasy 11  |
| Final Fantasy 11} |
+-------------------+
8 rows in set, 2 warnings (0.001 sec)

Bạn có thể sử dụng các mẫu khác nếu cần. Ví dụ: nếu bạn có phim "Tôi là số 1" và "Tôi là số 1 phần 2" thì có thể kết thúc phiên bản, ví dụ: "Final Fantasy {11}"


-4

Tôi biết chủ đề này là cổ xưa nhưng tôi nghĩ tôi đã tìm ra cách để làm điều này:

SELECT * FROM `table` ORDER BY 
CONCAT(
  GREATEST(
    LOCATE('1', name),
    LOCATE('2', name),
    LOCATE('3', name),
    LOCATE('4', name),
    LOCATE('5', name),
    LOCATE('6', name),
    LOCATE('7', name),
    LOCATE('8', name),
    LOCATE('9', name)
   ),
   name
) ASC

Phế rằng, nó sắp xếp tập hợp sau không chính xác (Thật vô dụng lol):

Final Fantasy 1 Final Fantasy 2 Final Fantasy 5 Final Fantasy 7 Final Fantasy 7: Advent Children Final Fantasy 12 Final Fantasy 112 FF1 FF2


3
tại sao không loại bỏ câu trả lời này? bạn sẽ nhận được một huy hiệu cho việc này
m47730
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.