【问题标题】:Sorting multiple types of numbers and characters对多种类型的数字和字符进行排序
【发布时间】:2020-01-21 04:42:13
【问题描述】:

如何对具有不同数字和字符的表格进行排序。 我表中的列是

res_no
------
B14                         
A6                              
C16
105 FF
114A
113
37
39A
91G
93
93(2)
A143B
A141
D154a
A141(25)
C40FF
D153(1)
E18A
D154
A51A
A50
E18A1 

   

我想这样排序

res_no
------

37  
39A  
91G 
93 
93(2)  
105FF 
113
114A
A6
A50
A51A
A141
A141(25)
A143B
B14
C40FF
D153(1)
D154
D154A
E18A
E18A1 

我试过ORDER BY

REGEXP ('^[0-9]')
CAST(res_no AS UNSIGNED)
REGEXP_SUBSTR(res_no, '(^[a-zA-Z]+)|([a-zA-Z]$)')
CAST(REGEXP_SUBSTR(res_no, '(^[0-9]+)|([0-9]+$)') AS UNSIGNED

如何解决这个问题?排序数字、字母数字值与上述代码完美匹配,但括号内的值以及 A50、A51A 等无法正确排序。

【问题讨论】:

  • 为什么要在 A50 之前 A6 而在 D153 之后要 D154?

标签: mysql sql codeigniter stored-procedures


【解决方案1】:

这个查询应该会给你想要的结果。查询命令有 5 件事:

  1. 一组起始字母(如果不存在,则为空,因此以数字开头的值将排在以字母开头的值之前);
  2. 字符串中的第一个数字(前面可以有字母);
  3. 字符串中的第二组字母(如果存在);
  4. 括在括号中的尾随数字(如果存在);
  5. 不包含在括号中的尾随数字(如果存在)。
SELECT res_no
FROM table1
ORDER BY REGEXP_SUBSTR(res_no, '^[a-zA-Z]+'),
         CAST(REGEXP_REPLACE(res_no, '^[a-zA-Z]*([0-9]+)', '\\1') AS UNSIGNED),
         REGEXP_SUBSTR(res_no, '(?<=[0-9])[a-zA-Z]+'),
         CAST(REGEXP_SUBSTR(res_no, '(?<=\\()[0-9]+(?=\\))') AS UNSIGNED),
         CAST(REGEXP_SUBSTR(res_no, '[0-9]+$') AS UNSIGNED)

输出:

res_no
37
39A
91G
93
93(2)
105FF
113
114A
A6
A50
A51A
A141
A141(25)
A143B
B14
C16
C40FF
D153(1)
D154
D154a
E18A
E18A1

Demo on dbfiddle

【讨论】:

  • @Simba 不用担心。我很高兴能帮上忙。
【解决方案2】:

对于您发布的示例数据,这将起作用:

select * from tablename
order by 
  res_no + 0 > 0 desc,
  res_no + 0,
  left(res_no, 1),
  substr(res_no, 2) + 0,
  case 
    when res_no like '%(%)%' then
      substring_index(substring_index(res_no, ')', 1), '(', -1) + 0
    else ''
  end,
  length(res_no),
  reverse(reverse(res_no) + 0) + 0,
  right(res_no, 1)

请参阅demo
结果:

| res_no   |
| -------- |
| 37       |
| 39A      |
| 91G      |
| 93       |
| 93(2)    |
| 105 FF   |
| 113      |
| 114A     |
| A6       |
| A50      |
| A51A     |
| A141     |
| A141(25) |
| A143B    |
| B14      |
| C16      |
| C40FF    |
| D153(1)  |
| D154     |
| D154a    |
| E18A     |
| E18A1    |

【讨论】:

    【解决方案3】:

    这对我来说非常棘手,但是我可以通过此查询获得您想要的结果。可能不是解决方案,但希望您能从中获得灵感。

    我的方法是:

    1. res_no 值中提取数字。我正在使用REGEXP_REPLACEres_no 中查找任何字母值并替换为空值。所以输出只是(CAST AS UNSIGNED)数字。
       CAST(REGEXP_REPLACE(res_no,' *[A-Z] *', '') AS UNSIGNED) AS numbers
    
    1. 按数字分配自定义行号值,如 1、A 为 2、B 为 3.. 等等。因此,如果您的 res_no 值跨越到 Z,那么......
       CASE WHEN res_no REGEXP ('^[0-9]')=1 THEN 1 
                       WHEN res_no LIKE 'A%' THEN 2
                       WHEN res_no LIKE 'B%' THEN 3
                       WHEN res_no LIKE 'C%' THEN 4
                       WHEN res_no LIKE 'D%' THEN 5
                       WHEN res_no LIKE 'E%' THEN 6
                       WHEN res_no LIKE 'F%' THEN 7 END AS rownum 
    
    1. 最后一部分是先根据 rownum 然后数字进行排序,我添加了最后一个条件 LENGTH(res_no),因为我得到了一些类似 93, 93(2) 的排序。
      SELECT res_no,
             CAST(REGEXP_REPLACE(res_no,' *[A-Z] *', '') AS UNSIGNED) AS numbers,
             CASE WHEN res_no REGEXP ('^[0-9]')=1 THEN 1 
                  WHEN res_no LIKE 'A%' THEN 2
                  WHEN res_no LIKE 'B%' THEN 3
                  WHEN res_no LIKE 'C%' THEN 4
                  WHEN res_no LIKE 'D%' THEN 5
                  WHEN res_no LIKE 'E%' THEN 6
                  WHEN res_no LIKE 'F%' THEN 7 END AS rownum 
        FROM TableA     
    ORDER BY rownum,numbers,LENGTH(res_no);
    

    上面的查询产生了以下输出:

    +----------+---------+--------+
    |  res_no  | numbers | rownum |
    +----------+---------+--------+
    | 37       |      37 |      1 |
    | 39A      |      39 |      1 |
    | 91G      |      91 |      1 |
    | 93       |      93 |      1 |
    | 93(2)    |      93 |      1 |
    | 105FF    |     105 |      1 |
    | 113      |     113 |      1 |
    | 114A     |     114 |      1 |
    | A6       |       6 |      2 |
    | A50      |      50 |      2 |
    | A51A     |      51 |      2 |
    | A141     |     141 |      2 |
    | A141(25) |     141 |      2 |
    | A143B    |     143 |      2 |
    | B14      |      14 |      3 |
    | C40FF    |      40 |      4 |
    | D153(1)  |     153 |      5 |
    | D154     |     154 |      5 |
    | D154A    |     154 |      5 |
    +----------+---------+--------+
    

    现在,您进行如下查询,也得到相同的结果:

    SELECT res_no 
      FROM TableA
    ORDER BY CASE WHEN res_no REGEXP ('^[0-9]')=1 THEN 1 
                  WHEN res_no LIKE 'A%' THEN 2
                  WHEN res_no LIKE 'B%' THEN 3
                  WHEN res_no LIKE 'C%' THEN 4
                  WHEN res_no LIKE 'D%' THEN 5
                  WHEN res_no LIKE 'E%' THEN 6
                  WHEN res_no LIKE 'F%' THEN 7 END,
             CAST(REGEXP_REPLACE(res_no,' *[A-Z] *', '') AS UNSIGNED),
             LENGTH(res_no);
    

    SELECT 切换到ORDER BY。 顺便说一句,我正在使用 MariaDB 10.3;

    【讨论】:

    • 此方法有效,但不能像值 @tcadidot0 那样对 E18A、E18A1 进行排序
    • E18A1 出现在 E18A 之前?
    • 还是这样? dbfiddle.uk/…
    【解决方案4】:

    如果我理解正确,您希望前导数字按数字排列,其余按字母顺序排列。

    以下查询执行此操作:

    select t.*
    from t
    order by (res_no + 0) > 0 desc,  -- numbers first
             (res_no + 0),
             res_no;
    

    Here 是一个 dbfiddle。

    您可以“特殊情况”a 记录:

    select t.*
    from t
    order by (res_no + 0) > 0 desc,  -- numbers first
             (res_no + 0),
             (case when res_no like 'A%' then res_no end) desc,
             res_no;
    

    【讨论】:

    • 我需要 A6 在 A50,A51A,A141,A141(25)..@Gordon Linoff 之前出现
    • @Simba 。 . .这是什么规则?它似乎不适用于“D”记录。
    猜你喜欢
    • 2019-01-18
    • 2013-12-30
    • 1970-01-01
    • 2020-05-29
    • 1970-01-01
    • 1970-01-01
    • 2021-01-11
    • 1970-01-01
    • 2012-11-14
    相关资源
    最近更新 更多