Доброго времени суток. В разработке одного магазина столкнулся с проблемой неправильной сортировки товаров по имени, если у них в названии присутствует число.
Что имеется: база данных MySQL и таблица products с полем name.
В названии товаров может присутствовать несколько чисел и все они могут находится в разных местах строки.
Вот пример:PRODUCT METALIC BALL SIZE 16 MM (0.629 IN)
PRODUCT WITH AN EYELET BALL SIZE 14 MM (0.551 IN)
PRODUCT METALIC BALL SIZE 9 MM (0.354 IN)
PRODUCT METALIC BALL SIZE 13 MM (0.511 IN)
PRODUCT WITH AN EYELET BALL SIZE 9 MM (0.354 IN)
PRODUCT WITH AN EYELET BALL SIZE 10 MM (0.393 IN)
При стандартной сортировке (
ORDER BY name ASC) я получаю
следующий список:
PRODUCT METALIC BALL SIZE 13 MM (0.511 IN)
PRODUCT METALIC BALL SIZE 16 MM (0.629 IN)
PRODUCT METALIC BALL SIZE 9 MM (0.354 IN)
PRODUCT WITH AN EYELET BALL SIZE 10 MM (0.393 IN)
PRODUCT WITH AN EYELET BALL SIZE 14 MM (0.551 IN)
PRODUCT WITH AN EYELET BALL SIZE 9 MM (0.354 IN)
Товары сортируются по первым буквам, которые до цифр, а потом идет классическая строковая сортировка чисел: 13, 16, 9 и 10, 14, 9. Сравнение происходит только по первой цифре, поэтому 9 оказывается после 10 и выше.
В чем проблема: в том, что эти числа могут находится в любом месте строки, они могут быть в полукруглых скобках, или нет. Поскольку числа в названии товара могут находится где угодно, то отпадают все варианты с SUBSTRING_INDEX. Так же мне даже в голову не приходит, смогут ли тут помочь регулярные выражения. Получается, что нужно создать сортировку, которая будет по-очереди брать каждое слово после пробела и сравнивать его как строку или как число.
На данный момент я решил вопрос, как мне кажется, зверским костылем. При выборке товара я сделал вырезку скобок и создал еще одну переменную name_order - это чтобы в будущем не пришлось еще раз избавляться от скобок:
SELECT name, REPLACE(REPLACE(name, '(', ''), ')', '') as name_order FROM product
Далее я создал функцию
SPLIT_STR, которая будет резать переменную по пробелу. Ну и последнее, и самое страшное - это разрезка названия товара на отдельные слова и числа, приведение каждого элемента этого "массива" к числу через CAST, и как следствие, сортировка по огромному количеству переменных:
SELECT name, REPLACE(REPLACE(name, '(', ''), ')', '') as name_order FROM product ORDER BY
CAST(SPLIT_STR(name_order, ' ', 1) AS SIGNED), SPLIT_STR(name_order, ' ', 1),
CAST(SPLIT_STR(name_order, ' ', 2) AS SIGNED), SPLIT_STR(name_order, ' ', 2),
CAST(SPLIT_STR(name_order, ' ', 3) AS SIGNED), SPLIT_STR(name_order, ' ', 3),
CAST(SPLIT_STR(name_order, ' ', 4) AS SIGNED), SPLIT_STR(name_order, ' ', 4),
CAST(SPLIT_STR(name_order, ' ', 5) AS SIGNED), SPLIT_STR(name_order, ' ', 5),
CAST(SPLIT_STR(name_order, ' ', 6) AS SIGNED), SPLIT_STR(name_order, ' ', 6),
CAST(SPLIT_STR(name_order, ' ', 7) AS SIGNED), SPLIT_STR(name_order, ' ', 7),
CAST(SPLIT_STR(name_order, ' ', 8) AS SIGNED), SPLIT_STR(name_order, ' ', 8),
CAST(SPLIT_STR(name_order, ' ', 9) AS SIGNED), SPLIT_STR(name_order, ' ', 9),
CAST(SPLIT_STR(name_order, ' ', 10) AS SIGNED), SPLIT_STR(name_order, ' ', 10),
CAST(SPLIT_STR(name_order, ' ', 11) AS SIGNED), SPLIT_STR(name_order, ' ', 11),
CAST(SPLIT_STR(name_order, ' ', 12) AS SIGNED), SPLIT_STR(name_order, ' ', 12),
name ASC
Как видно из кода, я разрезал название на 12 элементов по пробелу, затем поочередно сравнил каждый элемент как число и как строку. С такой сортировкой все работает идеально, вот только на этот код больно смотреть.
Внимание вопрос: может быть у кого-то есть более корректное и адекватное решение задачи? Собственный опыт или хотя бы мысли, как сделать такой же функционал, но более аккуратно и красиво?
Буду благодарен за любые мысли! И заранее спасибо ))