2015-12-02 65 views
2

替換計算值我有一個包含字符串的表列numbers像:postgres的在文本

1, 2, 2A, 14, 14A, 20 

列於期望的升序排列。

我怎樣才能制定一個ORDER BY子句來實現這個順序?

每默認情況下,Postgres的有訴諸字母順序具體做法是:

1, 2, 14, 20, 2A, 14A 

可以這樣做僅使用字符串操作功能,配備了Postgres的? (replace()regex_replace()等?)

我的第一個想法是:

  1. 切的信,如果存在
  2. 數* 100
  3. 加字母的ASCII碼,如果存在

這將產生期望的結果,因爲映射值將是:

100, 200, 265, 1400, 1465, 2000 

我也可以索引這個操縱值來加快排序。

附加限制:

我不能使用強制類型轉換爲十六進制的數字,因爲如:14Z有效了。

理想情況下,結果是單個表達式。我需要用這種轉變的篩選和排序,如:

SELECT * FROM table WHERE transform(numbers) < 15 ORDER BY transform(numbers) 
RESULT: 

1, 2, 2A, 14, 14A 

我想實現我的想法,用我學到的東西從@克林的回答是:

  1. 切的信再乘以100號:

    substring('12A' from '(\d+).*')::int*100 
    
  2. 削減的數量和獲得信ASCII:

    ascii(substring('12A' from '\d+([A-Z])')) 
    
  3. 加兩個。

這正常工作與12A,但不與12工作,因爲第二個表達式返回NULL,而不是0(數字零)。有任何想法嗎?

+0

BTW,你會使用'WHERE變換(數字)<1500'爲例,因爲transform()乘以100. –

+0

我會使用變換(數字)<變換('15') – billdoor

+0

是的,甚至更好。 –

回答

3

基於這些假設

  • 數字由數字和可選的一個待處理的字母組成,不包含任何其他內容。
  • 始終有至少一位前導數字。
  • 所有字母大寫[A-Z]或小寫[a-z],但不是混合。

我會強制執行,表列上的CHECK約束是絕對可靠的。

創建一個微小的IMMUTABLE SQL函數:

CREATE OR REPLACE FUNCTION f_nr2sort(text) 
    RETURNS int AS 
$func$ 
    SELECT CASE WHEN right($1, 1) > '9' COLLATE "C" -- no collation 
       THEN left($1, -1)::int * 100 + ascii(right($1, 1)) 
       ELSE $1::int * 100 END -- only digits 
$func$ LANGUAGE SQL IMMUTABLE; 

優化基於上述假設的表現。我用更便宜的left() and right()替換了所有正則表達式。

disabled collation rules with COLLATE "C"CASE表達式(它也更便宜),以確保ASCII字母的默認字節順序。 [a-zA-Z]中的字母大於'9',如果最後一封信的情況如此,我們會相應處理。
這樣我們可以避免添加NULL值,也不需要使用COALESCE修復。

然後你的查詢可以是:

SELECT * 
FROM tbl 
WHERE f_nr2sort(numbers) < f_nr2sort('15C') 
ORDER BY f_nr2sort(numbers); 

由於功能IMMUTABLE,你甚至可以創建一個簡單的functional index支持這一類的查詢:

CREATE INDEX tbl_foo_id ON tbl (f_nr2sort(numbers)); 
+0

非常明確和直接的解決方案,謝謝!我添加了一個我發現的問題 - 如果有人螞蟻避免使用stored_procedures - 它會像你提到的那樣使用coalesce - 所以你甚至包括了我發現的方法,並指出了用於索引的不可變關鍵字。 – billdoor

0

我是新的PostgreSQL的,但我發現這非常有用的帖子: Alphanumeric sorting with PostgreSQL

那麼,關於這樣的事情:

select val 
from test 
order by (substring(val, '^[0-9]+'))::int, substring(val, '[^0-9_].*$') desc 

希望它可以幫助

+0

謝謝,他的作品太多了@klins解決方案看起來更乾淨。我在他的回答中添加了一條評論 – billdoor