Postgresql 在regexp\u replace中使用lpad

Postgresql 在regexp\u replace中使用lpad,postgresql,Postgresql,我使用的是PostgreSQL 9.5,表中有一个“area_name”文本列,其中包含编号的扩展名: area_name ---------------- AREA AREA EXT AREA EXT 1 AREA EXT 5 AREA EXT 49 AREA EXT 50 我想对结果进行数字排序,如上图所示 我曾尝试使用regexp_replace将数字lpad为0,但使用长度为4时,无论数字是1位还是2位,都会在数字前面加上20 create table ext_test ( a

我使用的是PostgreSQL 9.5,表中有一个“area_name”文本列,其中包含编号的扩展名:

area_name
----------------
AREA
AREA EXT
AREA EXT 1
AREA EXT 5
AREA EXT 49
AREA EXT 50
我想对结果进行数字排序,如上图所示

我曾尝试使用regexp_replace将数字lpad为0,但使用长度为4时,无论数字是1位还是2位,都会在数字前面加上20

create table ext_test (
    area_name text
);

insert into ext_test values
  ('AREA'),
  ('AREA EXT'),
  ('AREA EXT 1'),
  ('AREA EXT 5'),
  ('AREA EXT 49'),
  ('AREA EXT 50');

select
    area_name,
    regexp_replace(area_name, ' EXT (\d*)', ' EXT ' || lpad('\1', 4, '0')) as order_result
from ext_test
order by order_result;

area_name    | order_result
------------------------------ 
AREA         | AREA
AREA EXT     | AREA EXT
AREA EXT 1   | AREA EXT 001
AREA EXT 49  | AREA EXT 0049
AREA EXT 5   | AREA EXT 005
AREA EXT 50  | AREA EXT 0050

替换表达式哪里出了问题?

更新:

(我又添加了一行区域文本509)

看起来您不能像这样将“\1”从常规引用到
lpad(
)。请看下面-o列是您的-结果是00509,第二列有长度(“\1”)*这当然总是两个,因为
'\1'
被解释为文本,而不是正则表达式元语法。所以我相信发生的事情是
lpad
将第一个参数解释为元语法,但从第二个参数中减去
'\1'
的长度将其解释为文本=>4-2始终是2。因此它在任何输入

虽然这可能是错误,也可能不是SQL函数与正则表达式元语法的混合使用,但我建议从正则表达式中获取一个值,然后将其与SQL函数一起使用。这里的r列是示例。而replace列是平均输出的示例:

t=# with p as (select regexp_replace(area_name, ' EXT (\d*)', ' EXT ' || lpad('\1', 4, '0')) o,regexp_replace(area_name,'AREA EXT (\d*)',length('\1')||'.\1'||'.'), area_name,regexp_replace(area_name,'AREA EXT (\d*)','\1') r from ext_test)
select *, length(r),replace(area_name,r,lpad(r,4,'0')) from p;
       o        | regexp_replace |  area_name   |    r     | length |    replace
----------------+----------------+--------------+----------+--------+---------------
 AREA           | AREA           | AREA         | AREA     |      4 | AREA
 AREA EXT       | AREA EXT       | AREA EXT     | AREA EXT |      8 | AREA
 AREA EXT 001   | 2.1.           | AREA EXT 1   | 1        |      1 | AREA EXT 0001
 AREA EXT 005   | 2.5.           | AREA EXT 5   | 5        |      1 | AREA EXT 0005
 AREA EXT 0049  | 2.49.          | AREA EXT 49  | 49       |      2 | AREA EXT 0049
 AREA EXT 0050  | 2.50.          | AREA EXT 50  | 50       |      2 | AREA EXT 0050
 AREA EXT 00509 | 2.509.         | AREA EXT 509 | 509      |      3 | AREA EXT 0509
(7 rows)

Time: 0.519 ms
只是一个建议-可能使用整数进行排序,如这里所示:

t=# with a as (select *,split_part(area_name,'AREA EXT ',2) s from ext_test) select area_name,case when s='' then 0 else s::int end c from a order by c,area_name;
  area_name  | c
-------------+----
 AREA        |  0
 AREA EXT    |  0
 AREA EXT 1  |  1
 AREA EXT 5  |  5
 AREA EXT 49 | 49
 AREA EXT 50 | 50
(6 rows)

Time: 0.354 ms

执行
regexp\u replace(区域名称,'EXT(\d*),'EXT'| | lpad('\1',4,'0'))时

首先,正在评估所有参数

lpad('\1',4',0')
变成
'00\1'

'EXT'| | lpad('\1',4',0')
变为
'EXT 00\1'

这意味着捕获的组(本例中的数字)前面将有2个零


你的目标可以分两个阶段实现-

  • 左键填充前面有X个零的所有数字
  • 将每个数字截断到其最右边的X位



  • 如果您的文本中有多个数字,请使用基于
    'AREA EXT'
    -

    select      area_name
               ,regexp_replace(regexp_replace(area_name,'(?<=AREA EXT )\d+',repeat('0',4) || '\&'),'(?<=AREA EXT )\d*(\d{4})','\1') as order_result
    
    from        ext_test
    
    order by    order_result
    ;
    
    选择区域名称
    
    ,regexp_replace(regexp_replace)(区域名称),(?这里是实现目标的另一种简单方法

    select      area_name
               ,substring (area_name,'(?<=AREA EXT )\d+')::int as order_result
    
    from        ext_test
    
    order by    order_result nulls first
               ,area_name
    ;
    

    我不知道你为什么要尝试将你的问题调用格式化以便排序。这很简单。只需将它们保留为
    int

    我想按如上所示的扩展对结果进行数字排序。

    只需输入
    orderby
    语句

    SELECT area_name
    FROM ext_test
    ORDER BY
      CASE
        WHEN area_name ~ '\d'
        THEN (regexp_matches(area_name, '\d+'))[1]::int
      END NULLS FIRST,
      area_name;
    
      area_name  
    -------------
     AREA
     AREA EXT
     AREA EXT 1
     AREA EXT 5
     AREA EXT 49
     AREA EXT 50
    (6 rows)
    

    这是一个简化的示例。在上面的实际数据“区域”中,有数千条记录中的数百个不同名称。有时where子句在名称的开头进行选择,然后我可以使用:
    按长度排序(区域名称),area_name
    Where my Where子句变得更复杂,结果中可能有多个area_名称,因此这不起作用。请用更多示例更新您的问题?这些数据足以说明问题。真正的问题是,为什么在regexp_replace中填充没有按预期工作?我不知道如何引起注意关于这些特殊的人。像Erwin或Craig这样的鲸鱼可以回答这是否是一个bug,或者你只是不想使用常规的元语法作为SQL函数的参数谢谢。现在我明白了为什么我没有得到我期望的结果。我希望你看看我的答案。;)它更简单、更快。
    select      area_name
               ,substring (area_name,'(?<=AREA EXT )\d+')::int as order_result
    
    from        ext_test
    
    order by    order_result nulls first
               ,area_name
    ;
    
    +-------------+--------------+
    | area_name   | order_result |
    +-------------+--------------+
    | AREA        | (null)       |
    +-------------+--------------+
    | AREA EXT    | (null)       |
    +-------------+--------------+
    | AREA EXT 1  | 1            |
    +-------------+--------------+
    | AREA EXT 5  | 5            |
    +-------------+--------------+
    | AREA EXT 49 | 49           |
    +-------------+--------------+
    | AREA EXT 50 | 50           |
    +-------------+--------------+
    
    SELECT area_name
    FROM ext_test
    ORDER BY
      CASE
        WHEN area_name ~ '\d'
        THEN (regexp_matches(area_name, '\d+'))[1]::int
      END NULLS FIRST,
      area_name;
    
      area_name  
    -------------
     AREA
     AREA EXT
     AREA EXT 1
     AREA EXT 5
     AREA EXT 49
     AREA EXT 50
    (6 rows)