在regexp_replace中使用lpad

时间:2017-02-08 07:19:24

标签: 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来将数字设为0,但使用长度为4的数字会在数字前加上2 0,无论是1还是2位数!

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

我在哪里使用替换表达式出错?

4 个答案:

答案 0 :(得分:0)

<强>更新

(我在 AREA TEXT 509 中添加了一行

看起来你无法引用&#39; \ 1&#39;从常规到lpad(都是这样的。看下面 - 列o是你的 - 结果是 00509 ,第二列有长度(&#39; \ 1&#39;)*当然总是两个,因为'\1'是解释为文本,而不是正则表达式元语法。所以我认为发生的事情是lpad将第一个参数解释为metasyntax,但是从第二个参数中减去'\1'的长度将其解释为 text =&gt; 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

答案 1 :(得分:0)

执行regexp_replace(area_name, ' EXT (\d*)', ' EXT ' || lpad('\1', 4, '0'))时 首先评估参数

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

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

这意味着捕获的组(本例中的数字)将以2个零开头。

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

  1. 左边填上X前面的所有数字
  2. 将每个数字截断为最右边的X位数
  3. select      area_name
               ,regexp_replace(regexp_replace(area_name,'\d+',repeat('0',4) || '\&'),'\d*(\d{4})','\1') 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 0001 |
    +-------------+---------------+
    | AREA EXT 5  | AREA EXT 0005 |
    +-------------+---------------+
    | AREA EXT 49 | AREA EXT 0049 |
    +-------------+---------------+
    | AREA EXT 50 | AREA EXT 0050 |
    +-------------+---------------+
    

    如果您的文字中包含多个号码,请使用此版本基于'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
    ;
    

答案 2 :(得分:0)

这是实现目标的另一种简单方法

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           |
+-------------+--------------+

答案 3 :(得分:0)

我不确定你为什么试图将它们格式化为你的问题要求订购。这很简单。只需将它们保留为int

  

我想通过扩展按数字顺序排序结果。

只需输入ORDER BY声明。

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)
相关问题