2017-02-08 54 views
2

我使用PostgreSQL 9.5和有一個表有「AREA_NAME」文本名稱中有編號的一些推廣柱:使用LPAD在REGEXP_REPLACE

area_name 
---------------- 
AREA 
AREA EXT 
AREA EXT 1 
AREA EXT 5 
AREA EXT 49 
AREA EXT 50 

我想,如上圖所示由extention數字排序的結果。

我已經嘗試使用regexp_replace來lpad數字0,但使用4的長度是在數字前加2 0s,無論它是否是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 

我在哪裏出錯了替換表達式?

+0

我希望你看看我的答案。 ;)大大簡單快捷。 –

回答

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) 
0

更新:

(我添加了區域中的文本509多一個排)

看起來你不能引用 '\ 1',從普通到lpad(這樣。看下面 - 列o是你的 - 結果是,第二列的長度('\ 1')*當然總是兩個,因爲'\1'被解釋爲文本,而不是正則表達式元語法。所以我相信會發生什麼是lpad解釋第一個論點元語法,但隨後從第二argumet從其減去的'\1'長度將其解釋爲文本 => 4-2 總是 2.所以其附加兩個零到任何輸入。

雖然這是錯誤或不是意味着將SQL函數與正則表達式元語法混合使用,我建議從正則表達式獲取值,然後將其與sql函數一起使用。這裏列r就是例子。和列更換是例子的意思輸出:

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

這是一個簡化的例子。在上面的實際數據中,'AREA'是來自數千條記錄的數百個不同的名稱。有時where子句選擇名稱的開頭,然後我可以使用:按長度排序(area_name),area_name' where where子句變得更加複雜,結果中可能有多個area_names,因此此dosn't工作。 – Derek

+0

請用更多的例子更新你的問題呢? –

+0

這是足以顯示問題的數據。真正的問題是爲什麼在regexp_replace中填充不能按預期工作? – Derek

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位數

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

查看更新的答案 –

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