大部分字符串比較默認爲ASCIIbetical sorting。它將每個字符轉換爲其ASCII碼(或者可能是UTF-8,與前面7位的ASCII重疊),然後進行排序。這似乎排序精...
select 'a' < 'z'; -- true
...但很快就出了問題。
select 'a' < 'Z'; -- false
這是因爲所有大寫字母都出現在ASCII和UTF-8的所有小寫字母之後。
解決此問題的簡單方法是通過在兩邊調用upper
或lower
來標準化該情況。
select lower('a') < lower('Z'); -- true
與ASCIIbetical排序問題二是數字。再次,它開始罰款...
select 'a9' < 'a1'; -- true
...但很快去鍋。
select 'a9' < 'a10'; -- false
ASCIIbetical排序一次只比較一個字符。 ASCII 9(57)小於ASCII 1(49)。
你想要的是一個natural sort,其中字符串和數字作爲比較的數字串的部分進行比較。在SQL中進行自然排序並不是最容易的事情。你要麼需要固定的字段寬度來分別排序每個子字符串,要麼用正則表達式...
幸運的是pg_natural_sort_order,這是一個Postgres擴展,實現了高效的自然排序。
如果你不能安裝擴展,你可以使用存儲過程如2kan的btrsort。
CREATE FUNCTION btrsort_nextunit(text) RETURNS text AS $$
SELECT
CASE WHEN $1 ~ '^[^0-9]+' THEN
COALESCE(SUBSTR($1, LENGTH(SUBSTRING($1 FROM '[^0-9]+'))+1), '')
ELSE
COALESCE(SUBSTR($1, LENGTH(SUBSTRING($1 FROM '[0-9]+'))+1), '')
END
$$ LANGUAGE SQL;
CREATE FUNCTION btrsort(text) RETURNS text AS $$
SELECT
CASE WHEN char_length($1)>0 THEN
CASE WHEN $1 ~ '^[^0-9]+' THEN
RPAD(SUBSTR(COALESCE(SUBSTRING($1 FROM '^[^0-9]+'), ''), 1, 12), 12, ' ') || btrsort(btrsort_nextunit($1))
ELSE
LPAD(SUBSTR(COALESCE(SUBSTRING($1 FROM '^[0-9]+'), ''), 1, 12), 12, ' ') || btrsort(btrsort_nextunit($1))
END
ELSE
$1
END
;
$$ LANGUAGE SQL;
儘管它沒有提供比較運算符,我也不會假裝理解它。這使您可以在order by
中使用它。
select * from things order by btrsort(whatever);
要防止自然排序的查詢從轉向泥大表,you can create a btree index on the result of that function。
create index things_whatever_btrsort_idx ON things(btrsort(whatever));
'select'a'<'b';'在postgres 9.6.1結果爲True。 'select'6.2(5a)'<'6.2(5b)';'返回True。 'select'6.2(15)'<'6.2(5a)';'也返回True。最後一個是真的,因爲'6.2('是匹配的,下一個字符'''''''5''使得6.2(15)小於6.2(5a)。是不是你的期望? – zedfoxus
似乎是這個問題,後面的選擇'6.2(15)'''6.2(5a)'的比較是真實的,我沒有注意到,直到你指出來,所以我猜這個問題現在變成了如何確保'15'會大於'5a'可能先測試字母,刪除它們並測試數字字符串? – bakamike
[PostgreSQL ORDER BY issue - natural sort]可能重複(http://stackoverflow.com/questions/9173558/postgresql-order-by-issue-natural-sort) – Schwern