2009-02-16 55 views
3

我們的應用程序有一個CustomerNumber字段。我們有數百個使用該系統的人(每個人都有自己的登錄名和他們自己的CustomerNumber s列表)。一個用戶最多可能擁有100,000個客戶。許多人少於100人。如何排序和顯示用戶期望的混合alpha和數字列表?

有些人只將實際數字放到他們的顧客號碼字段中,而另一些人則使用混合物。系統允許20個字符,它們可以是A-Z,0-9或破折號,並將它們存儲在VARCHAR2(20)中。在存儲之前,小寫字母都是大寫的。

現在,假設我們有一個簡單的報告,列出特定用戶的所有客戶,按客戶編號排序。例如

SELECT CustomerNumber,CustomerName 
FROM Customer 
WHERE User = ? 
ORDER BY CustomerNumber; 

這是一個天真的解決方案,只有用不完號不希望看到一個普通的字母排序的人(其中「10」在「9」之前)。

我不想問用戶有關他們的數據的任何不必要的問題。

我使用的是Oracle,但我認爲看到其他數據庫的一些解決方案會很有趣。請包括您的答案適用於哪個數據庫。

您認爲最好的實現方式是?

回答

2

在Oracle 10g:

SELECT cust_name 
FROM t_customer c 
ORDER BY 
    REGEXP_REPLACE(cust_name, '[0-9]', ''), TO_NUMBER(REGEXP_SUBSTR(cust_name, '[0-9]+')) 

這將通過數第一次出現的排序,而不是把它的位置,我。 È:

  1. customer1 < customer2 < customer10
    • cust1omer ? customer1
    • cust8omer1 ? cust8omer2

,其中?意味着該順序是不確定的。

這對大多數情況已經足夠了。

要強制案例2排序順序,您可以添加一個REGEXP_INSTR(cust_name, '[0-9]', n)ORDER BY列表n次,對n個(2nd3rd等)的數字組的首次亮相強制命令。

要強制案例3排序順序,您可以添加一個TO_NUMBER(REGEXP_SUBSTR(cust_name, '[0-9]+', n))ORDER BY列表n倍,迫使n個的順序。數字組。

實際上,我寫的查詢就足夠了。

您可以創建一個功能對這些表達式的索引,但你需要有一個提示強制它,一通SORT ORDER BY無論如何都會被執行,因爲CBO不信任功能基指數足夠允許他們使用ORDER BY

4

也許你最好的選擇是預先計算一個單獨的列並使用它來訂購併使用客戶編號進行顯示。這可能涉及將任何內部整數填充到固定長度。

另一種可能性是在返回的結果上進行排序後選擇。

Jeff Atwood has put together a blog發佈了一些人如何計算人類友好的排序順序。

+0

另請參閱http://stackoverflow.com/questions/34518/natural-sorting-algorithm以及其他標記爲「自然排序」的內容以供討論和實施... – dmckee 2009-02-16 14:39:49

1

你可以有一個數字列[CustomerNumberInt]即只使用時爲CustomerNumber是純數字(NULL,否則[1]),然後

ORDER BY CustomerNumberInt, CustomerNumber 

[1]根據您的SQL版本是如何處理空值在ORDER BY,你可能需要將其默認值爲零(或無窮大!)

1

我有一個類似的可怕情況,並制定了適當的可怕的函數來處理它(SQLServer的)

在我的情況我有一個「單位」表(這是一個學生的工作跟蹤系統,所以在這種情況下單位代表他們正在做的一門課程)。單位有一個代碼,其中大部分是純粹的數字,但由於各種原因,它被作爲一個varchar,他們決定以最多5個字符作爲前綴。因此,他們希望53123237356正常排序,但也T53,T123,T237,T356

UnitCode是爲nvarchar(30)

這裏的函數體:

declare @sortkey nvarchar(30) 

select @sortkey = 
    case 
     when @unitcode like '[^0-9][0-9]%' then left(@unitcode,1) + left('000000000000000000000000000000',30-(len(@unitcode))) + right(@unitcode,len(@unitcode)-1) 
     when @unitcode like '[^0-9][^0-9][0-9]%' then left(@unitcode,2) + left('000000000000000000000000000000',30-(len(@unitcode))) + right(@unitcode,len(@unitcode)-2) 
     when @unitcode like '[^0-9][^0-9][^0-9][0-9]%' then left(@unitcode,3) + left('000000000000000000000000000000',30-(len(@unitcode))) + right(@unitcode,len(@unitcode)-3) 
     when @unitcode like '[^0-9][^0-9][^0-9][^0-9][0-9]%' then left(@unitcode,4) + left('000000000000000000000000000000',30-(len(@unitcode))) + right(@unitcode,len(@unitcode)-4) 
     when @unitcode like '[^0-9][^0-9][^0-9][^0-9][^0-9][0-9]%' then left(@unitcode,5) + left('000000000000000000000000000000',30-(len(@unitcode))) + right(@unitcode,len(@unitcode)-5) 
     when @unitcode like '%[^0-9]%' then @unitcode 
     else left('000000000000000000000000000000',30-len(@unitcode)) + @unitcode 
    end 

return @sortkey 

我想拍攝在寫完之後,我自己面對面,但是它運行起來並且似乎不會在運行時終止服務器。

0

我在SQL SERVER中使用了它,並且效果很好:這裏的解決方案是用前面的字符填充數字值,這樣所有的字符串長度都是相同的。

下面是使用這種方法的一個示例:

select MyCol 
from MyTable 
order by 
    case IsNumeric(MyCol) 
     when 1 then Replicate('0', 100 - Len(MyCol)) + MyCol 
     else MyCol 
    end 

的100應該與該列的實際長度來代替。