Естественная сортировка в MySQL - PullRequest
72 голосов
/ 30 сентября 2008

Существует ли элегантный способ иметь эффективную, естественную сортировку в базе данных MySQL?

Например, если у меня есть этот набор данных:

  • Final Fantasy
  • Final Fantasy 4
  • Final Fantasy 10
  • Final Fantasy 12
  • Final Fantasy 12: Цепи Проматии
  • Final Fantasy Adventure
  • Final Fantasy Origins
  • Final Fantasy Tactics

Любое другое элегантное решение, кроме деления названий игр на составляющие

  • Заголовок : "Final Fantasy"
  • Число : "12"
  • Подзаголовок : "Цепи Проматии"

чтобы убедиться, что они выходят в правильном порядке? (10 после 4, а не до 2).

Это - боль в a **, потому что время от времени появляется другая игра, которая нарушает этот механизм анализа названия игры (например, «Warhammer 40,000», «James Bond 007»)

Ответы [ 19 ]

4 голосов
/ 13 июня 2016

На заказ:
0
1
2
10
23
101
205
1000

AAC
b
casdsadsa
css

Используйте этот запрос:

SELECT 
    column_name 
FROM 
    table_name 
ORDER BY
    column_name REGEXP '^\d*[^\da-z&\.\' \-\"\!\@\#\$\%\^\*\(\)\;\:\\,\?\/\~\`\|\_\-]' DESC, 
    column_name + 0, 
    column_name;
4 голосов
/ 30 сентября 2008

Другой вариант - выполнить сортировку в памяти после извлечения данных из mysql. Хотя это не будет лучшим вариантом с точки зрения производительности, если вы не сортируете огромные списки, все будет в порядке.

Если вы посмотрите на пост Джеффа, вы можете найти множество алгоритмов для любого языка, с которым вы можете работать. Сортировка для людей: естественный порядок сортировки

3 голосов
/ 25 августа 2016

Я пробовал несколько решений, но на самом деле это очень просто:

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
...
*/
3 голосов
/ 17 октября 2013

Вы также можете динамически создать «столбец сортировки»:

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

Таким образом, вы можете создавать группы для сортировки.

В моем запросе я хотел поставить перед всеми знак «-», затем цифры и текст. Что может привести к чему-то вроде:

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

Таким образом, вам не нужно поддерживать столбец сортировки в правильном порядке при добавлении данных. Вы также можете изменить порядок сортировки в зависимости от того, что вам нужно.

3 голосов
/ 02 октября 2008

Добавьте поле для «ключа сортировки», в котором все строки цифр заполнены нулями до фиксированной длины, а затем вместо этого выполните сортировку по этому полю.

Если у вас могут быть длинные строки цифр, другой метод заключается в добавлении числа цифр (фиксированной ширины, с добавлением нуля) к каждой строке цифр. Например, если у вас не будет более 99 цифр подряд, то для «Super Blast 10 Ultra» ключом сортировки будет «Super Blast 0210 Ultra».

1 голос
/ 02 марта 2015

Упрощенная не-udf версия лучшего ответа @ plaix / Richard Toth / Luke Hoggett, которая работает только для первого целого числа в поле, равна

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 голос
/ 07 марта 2011

Если вы используете PHP, вы можете сделать естественную сортировку в 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]; 
}

Я надеюсь, что MySQL внедрит естественную сортировку в будущей версии, но запрос функции (# 1588) открыт с 2003 года, так что я не задерживаю дыхание.

0 голосов
/ 12 ноября 2012

Я знаю, что эта тема древняя, но я думаю, что нашел способ сделать это:

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

Отбрось, неправильно отсортировал следующий набор (бесполезно, смеется):

Final Fantasy 1 Final Fantasy 2 Final Fantasy 5 Final Fantasy 7 Final Fantasy 7: дети пришествия Final Fantasy 12 Final Fantasy 112 FF1 FF2

0 голосов
/ 20 июня 2011

Также есть нацорт . Предполагается, что он является частью плагина drupal , но он отлично работает автономно.

...