Я хочу отсортировать следующие элементы данных в том порядке, в котором они представлены ниже:

ZZZ-AA1
ZZZ-AA2
ZZZ-AA3
ZZZ-AA11
ZZZ-AA12
ZZZ-AAA1
ZZZ-AB1
ZZZ-AB2
ZZZ-BB1
ZZZ-BB12
ZZZ-CA1

Есть ли уловки, чтобы сделать сортировку более правильной?

Благодарность!

Запросы, которые я пробовал:

SELECT bay FROM table GROUP BY bay ORDER BY SUBSTR(bay FROM 1 FOR 6), CAST(SUBSTR(bay FROM 6) AS UNSIGNED)
SELECT bay FROM table GROUP BY bay ORDER BY LENGTH(bay), bay
0
Gabriel 18 Янв 2021 в 05:30

3 ответа

Лучший ответ

В MySQL до 8.0 попытка добиться этого - довольно долгий путь. В MySQL v8.0 одним из возможных решений является использование REGEXP_REPLACE как:

SELECT * FROM test
      ORDER BY 
      REGEXP_REPLACE(val, "[0-9]", "") ASC,
      ABS(REGEXP_REPLACE(val, "[A-Za-z]", "")) ASC;

Однако, поскольку вы используете MysQL v5.7, я пришел к следующему:

SELECT val FROM 
(SELECT val, 
     LEAST(IF(LOCATE(0, val)=0,999,LOCATE(0, val)), 
     IF(LOCATE(1, val)=0,999,LOCATE(1, val)),  
     IF(LOCATE(2, val)=0,999,LOCATE(2, val)), 
     IF(LOCATE(3, val)=0,999,LOCATE(3, val)),  
     IF(LOCATE(4, val)=0,999,LOCATE(4, val)), 
     IF(LOCATE(5, val)=0,999,LOCATE(5, val)),  
     IF(LOCATE(6, val)=0,999,LOCATE(6, val)), 
     IF(LOCATE(7, val)=0,999,LOCATE(7, val)),  
     IF(LOCATE(8, val)=0,999,LOCATE(8, val)), 
     IF(LOCATE(9, val)=0,999,LOCATE(9, val))) lv
FROM test) t
ORDER BY SUBSTRING(val,1,lv-1) ASC, SUBSTRING(val,lv)+0 ASC;

Идея состоит в том, чтобы получить первое вхождение числа, а затем использовать его для разделения между значениями перед числами и самими числами с помощью LOCATE функция:

LOCATE(1, val)
*translate to "Locate the number 1 in `val` column".

IF(LOCATE(1, val)=0, 999, LOCATE(1, val))
*translate to "If you can't locate the number 1 in `val`, return 999 
 otherwise return the location of it.".

Так как это числа, которые мы ищем, это намного проще, потому что мне нужно только найти числовые значения 0 to 9. Затем, если LOCATE возвращает 0 в качестве результата, замените его на 999. Я заменяю их, потому что использую НАИМЕНЕЕ вернуть наименьшее значение; что является первым числом:

LEAST(IF(LOCATE ..

После этого я делаю запрос как подзапрос, а затем использую результат из LEAST(IF(LOCATE .. как центр SUBSTRING в ORDER BY:

SUBSTRING(val,1,lv-1)
*Will return the value before the first number occurrence. 
(e.g ZZZ-AA1 to ZZZ-AA OR ZZZ-AAA1 to ZZZ-AAA).

AND 

SUBSTRING(val,lv)
*Will return the value on the first number onwards. 
(e.g. ZZZ-AA1 to 1 OR ZZZ-AAA1 to 1).

Конечно, если вы хотите посмотреть на значение, вы можете просто добавить оператор в ORDER BY к SELECT, например:

SELECT val, SUBSTRING(val,1,lv-1) , SUBSTRING(val,lv)+0  FROM 
...

* Если вы заметили, в первом SUBSTRING я добавил lv-1, потому что без minus 1 SUBSTRING вернется вместе с первым вхождением числа. А во втором SUBSTRING я добавил +0 в конце, потому что без plus 1 тип данных результата - строка, поэтому порядок будет нарушен.

Демо скрипка

1
fadlikidd 18 Янв 2021 в 05:08

Вы можете сделать это с помощью пары REGEXP_SUBSTR():

ORDER BY REGEXP_SUBSTR(tmp.`code`,"[A-Za-z-]+"), CAST(REGEXP_SUBSTR(tmp.`code`,"[0-9]+") AS UNSIGNED)

Здесь вы можете увидеть это в действии:

SELECT tmp.`code`,
       REGEXP_SUBSTR(tmp.`code`,"[A-Za-z-]+") as `key`,
       CAST(REGEXP_SUBSTR(tmp.`code`,"[0-9]+") AS UNSIGNED) as `sort`
  FROM (SELECT 'ZZZ-AA1' as code UNION ALL
        SELECT 'ZZZ-AA2' as code UNION ALL
        SELECT 'ZZZ-AA3' as code UNION ALL
        SELECT 'ZZZ-AA11' as code UNION ALL
        SELECT 'ZZZ-AA12' as code UNION ALL
        SELECT 'ZZZ-AAA1' as code UNION ALL
        SELECT 'ZZZ-AB1' as code UNION ALL
        SELECT 'ZZZ-AB2' as code UNION ALL
        SELECT 'ZZZ-BB1' as code UNION ALL
        SELECT 'ZZZ-BB12' as code UNION ALL
        SELECT 'ZZZ-CA1' as code) tmp
 ORDER BY REGEXP_SUBSTR(tmp.`code`,"[A-Za-z-]+"),
          CAST(REGEXP_SUBSTR(tmp.`code`,"[0-9]+") AS UNSIGNED);

Это будет неэффективно при сортировке больших наборов данных, поэтому обязательно используйте его только с последним возвращенным подмножеством.

0
Matigo 18 Янв 2021 в 03:13

В MySQL 5.7 вы можете попробовать использовать функцию SUBSTRING_INDEX.

SELECT *,
  SUBSTRING_INDEX(
  SUBSTRING_INDEX(
  SUBSTRING_INDEX(
  SUBSTRING_INDEX(
  SUBSTRING_INDEX(
  SUBSTRING_INDEX(
  SUBSTRING_INDEX(
  SUBSTRING_INDEX(
  SUBSTRING_INDEX(
  SUBSTRING_INDEX(bay
    , '1', 1)
    , '2', 1)
    , '3', 1)
    , '4', 1)
    , '5', 1)
    , '6', 1)
    , '7', 1)
    , '8', 1)
    , '9', 1)
    , '0', 1) AS alpha_key
FROM t
ORDER BY alpha_key, CAST(SUBSTRING_INDEX(bay, alpha_key, -1) AS UNSIGNED)

скрипка

1
user14717238 18 Янв 2021 в 05:04