Alphanumeric sorting with PostgreSQL

放肆的年华 提交于 2019-11-27 11:56:27

The ideal way would be to normalize your data and split the two components of the column into two individual columns. One of type integer, one text.

With the current table, you can do something like demonstrated here:

WITH x(t) AS (
    VALUES
     ('10_asdaasda')
    ,('100_inkskabsjd')
    ,('11_kancaascjas')
    ,('45_aksndsialcn')
    ,('22_dsdaskjca')
    ,('100_skdnascbka')
    )
SELECT t
FROM   x
ORDER  BY (substring(t, '^[0-9]+'))::int     -- cast to integer
          ,substring(t, '[^0-9_].*$')        -- works as text

The same substring() expressions can be used to split the column.

The regular expressions are somewhat fault tolerant:

  • The first regex picks the longest numeric string from the left, NULL if no digits are found, so the cast to integer can't go wrong.

  • The second regex picks the rest of the string from the first character that is not a digit or '_'.

If the underscore is unambiguous as separator anyway, split_part() is faster:

ORDER  BY (split_part(t, '_', 1)::int
          ,split_part(t, '_', 2)

Answer for your example

SELECT name
FROM   nametable
ORDER  BY (split_part(name, '_', 1)::int
          ,split_part(name, '_', 2)

You can use regular expressions with substrings

   order by substring(column, '^[0-9]+')::int, substring(column, '[^0-9]*$')

There is a way to do it with an index over an expression. It wouldn't be my preferred solution (I would go for Brad's) but you can create an index on the following expression (there are more ways to do it):

CREATE INDEX idx_name ON table (CAST(SPLIT_PART(columname, '_', 1) AS integer));  

Then you can search and order by CAST(SPLIT_PART(columname, '_', 1) AS integer) every time you need the number before the underline character, such as:

SELECT * FROM table ORDER BY CAST(SPLIT_PART(columname, '_', 1) AS integer);  

You can do the same to the string part by creating an index on SPLIT_PART(columname, '_', 2), and then sort accordingly as well.
As I said, however, I find this solution very ugly. I would definitely go with two other columns (one for the number and one for the string), then maybe even removing the column you mention here.

You should add a new column to the database which is has numeric data type and on persisting a new record set it to the same value as the prefix on the string value you have.

Then you can create an index on the properly typed numeric column for sorting.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!