带regexp的多级列表自然排序

带regexp的多级列表自然排序,regex,postgresql,natural-sort,Regex,Postgresql,Natural Sort,以下是一个数据示例: '1.' 'Numeric types' '1.1.' 'Integer' '1.2.' 'Float' ... '1.10' 'Double' 为了自然排序,我们可以使用字符串_To_数组和“.”作为分隔符,然后将text[]转换为int[]并按整数数组排序,但由于字段本身是text类型,并且可能存在用户决定使用非数字符号(例如1.1.3a)的情况,从而导致转换错误。 为了解决这个问题,我决定使用regexp: select regexp_matches('

以下是一个数据示例:

'1.'    'Numeric types'
'1.1.'  'Integer'
'1.2.'  'Float'
...
'1.10'  'Double'
为了自然排序,我们可以使用字符串_To_数组和“.”作为分隔符,然后将text[]转换为int[]并按整数数组排序,但由于字段本身是text类型,并且可能存在用户决定使用非数字符号(例如1.1.3a)的情况,从而导致转换错误。 为了解决这个问题,我决定使用regexp:

select regexp_matches('1.2.3.4.', E'(?:(\\d+)\.?)+')
预期的结果是数组:{'1','2','3','4'},但如果使用以下regexp,则只能得到所述数组的最后一个元素:

select regexp_matches('1.2.3.4.', E'((?:\\d+)\.?)+')
结果是{'1.2.3.4'}

使用全局标志“g”不是一个选项,因为regexp\u matches返回一列

是否有任何方法可以仅使用一个regexp_匹配项将“1.2.3.4a.”:text转换为{1,2,3,4}::int[]

select array(select m[1] from regexp_matches(dt_code, '(\d+)', 'g') m)::int[] nums, *
from data_types
order by 1;
或者,您可以使用string_to_数组将字符串拆分为数组,但仍需要使用regexp删除任何非数字字符:

select string_to_array(trim(regexp_replace(dt_code, '[^\d\.]+', ''), '.'), '.')::int[] nums, *
from data_types
order by 1;
对于更高级的自然排序,您需要自己将文本拆分为标记。有关更多信息,请访问

我可以提出一个简化的、可重用的功能:

create or replace function natural_order_tokens(text)
  returns table (
    txt text,
    num int,
    num_rep text
  )
  language sql
  strict
  immutable
as $func$
  select m[1], (case m[2] when '' then '0' else m[2] end)::int, m[2]
    from regexp_matches($1, '(\D*)(\d*)', 'g') m
   where m[1] != '' or m[2] != ''
$func$;
使用此功能,自然排序将变得非常简单:

select *
from data_types
order by array(select t from natural_order_tokens(dt_code) t);

你有没有读过这个问题?你只是认为我在问题中所说的每一种方法都不适用。还有一个问题是,如何只使用一个regexp_匹配项,而您也忽略了这一点。再次发布答案有什么意义?@Nick是的,我读过了。正如你所说,你的方法在你的问题中都不起作用,但这些都是有效的解决方案。此外,仅使用一个regexp_也与此答案中的第一个匹配。如果没有g修饰符,您就无法执行此操作。那么答案是“否”,您不能仅使用一个regexp\u matches调用来执行此操作。@Nick,但我可以使用一个调用来执行此操作。。。使用g不会多次调用regexp_matches,因此您希望从问题中删除单词以使其更简单。我将再试一次:只使用一个regexp\u匹配调用,这意味着没有其他函数调用。