排序字母数字列

时间:2014-03-26 21:47:48

标签: sql postgresql alphanumeric natural-sort

我在数据库中有一个列:

Serial Number
-------------
S1
S10
...
S2
S11
..
S13

我想对序列号< = 10:

进行排序并返回如下结果
S1
S2
S10

我试过的一种方法是:

select Serial_number form table where Serial_Number IN ('S1', 'S2',... 'S10');

这解决了目的,但寻找更好的方法

3 个答案:

答案 0 :(得分:3)

以下是此格式的简便方法:

order by length(Serial_Number),
         Serial_Number

这是有效的,因为前缀('S')在所有值上的长度相同。

答案 1 :(得分:2)

对于Postgres,您可以使用以下内容:

select serial_number
from the_table
order by regexp_replace(serial_number, '[^0-9]', '', 'g')::integer;

regexp_replace将删除所有非数字字符,结果将被视为适合"正确"的数字。排序

修改1:

您可以使用新的"号码"限制查询结果:

select serial_number
from (
  select serial_number, 
         regexp_replace(serial_number, '[^0-9]', '', 'g')::integer as snum
  from the_table
) t 
where snum <= 10
order by snum;

修改2

如果您收到错误ERROR: invalid input syntax for integer: "",那么显然您在serial_number列中的值不会遵循您在问题中发布的格式。这意味着regexp_replace()从字符串中删除所有字符,因此像S这样的字符串会导致这种情况。

为了防止这种情况,您需要使用以下方法从结果中排除这些行:

where length(regexp_replace(serial_number, '[^0-9]', '', 'g')) > 0

在内部选择中。或者,如果由于某种原因需要这些行,请在选择列表中处理:

select serial_number
from (
  select serial_number, 
         case
            when length(regexp_replace(serial_number, '[^0-9]', '', 'g')) > 0 then regexp_replace(serial_number, '[^0-9]', '', 'g')::integer as snum
            else null -- or 0 whatever you need
         end as snum
  from the_table
) t 
where snum <= 10
order by snum;

这是一个非常好的例子,说明为什么你不应该在一个列中混合两个不同的东西。如果您的所有序列号都有前缀S,则不应将其存储并将实数放在真实的integer(或bigint)列中。

使用NOT_SET之类的东西来表示缺失值也是一个糟糕的选择。由于这个原因,精确地发明了NULL值:表明没有数据。

答案 2 :(得分:1)

由于只有第一个角色会破坏你的数字乐趣,只需用right()修剪它并按数值排序:

SELECT *
FROM   tbl
WHERE  right(serial_number, -1)::int < 11
ORDER  BY right(serial_number, -1)::int;

需要Postgres 9.1或更高版本。在旧版本中,使用substring (x, 10000)替代。