2012-09-24 69 views
6

我遇到了oracle 10g的排序問題。不知道它是否特定於10g。Oracle 10g SQL排序VARCHAR2

我有如下表:

ID NAME 
1 A.1 
2 A.3 
3 A.4 
4 A.5 
5 A.2 
6 A.5.1 
7 A.5.2 
8 A.5.10 
9 A.5.10.1 
10 A.5.3 

執行通用SELECT NAME FROM table_name ORDER BY 1生產:

A.1 
A.2 
A.3 
A.4 
A.5 
A.5.1 
A.5.10 
A.5.10.1 
A.5.2 
A.5.3 

我想它正確排序,當這些板塊有一個數字大於9,像這樣:

A.1 
A.2 
A.3 
A.4 
A.5 
A.5.1 
A.5.2 
A.5.3 
A.5.10 
A.5.10.1 

我有更多的數字條目比這個w不同的長度和很多部分的數字段大於10.我試圖在order by子句中使用regexp_replace(),但沒有運氣。任何幫助將不勝感激。

回答

2

試試這個

WITH t AS 
(
    SELECT id,name, 
    xmltype('<r><c>' ||replace(NAME, '.', '</c><c>')||'</c></r>') AS xmlname 
    FROM table1 
) 

SELECT name ,id 
FROM t 
ORDER BY lpad(extract(xmlname,'//c[1]/text()').getstringval(), 5, '0') 
||lpad(extract(xmlname,'//c[2]/text()').getstringval(), 5, '0') 
||lpad(extract(xmlname,'//c[3]/text()').getstringval(), 5, '0') 
||lpad(extract(xmlname,'//c[4]/text()').getstringval(), 5, '0') 

Here是小提琴

+0

這絕對有效。你能解釋發生了什麼嗎?而且這是一個需要花費大量時間才能生成的龐大查詢。那是因爲With/Replace還是ORDER BY中的連接?謝謝。 –

+0

首先它使它成爲一個xml(不是真的需要,可以用substr和instr來完成 - 實際上這可能會降低性能)。那麼它需要每個節點(點之間的一個部分)並用零填充長度較高(比如說5)。現在可以排序 –

1

以下內容可能會讓您知道該怎麼做。要訂購「A.」形式的值,可以按表達式的長度和表達式排序。因此,A.1和A.2在A.10之前,因爲它們的長度較短。

您可以通過如下擴大這一點,用命令:

order by substr(val, 1, instr('.')), 
     len(substr(val, 1, instr('.', 1, 2)), 
     substr(val, 1, instr('.', 1, 2)), 
     len(substr(val, 1, instr('.', 1, 3)), 
     substr(val, 1, instr('.', 1, 3)) . . . 
+0

我不確定我是否遵循代碼邏輯。是否有一個括號丟失或者是第一個len語句中的後續substr,len,substr? –

+0

這是一個相當不錯的解決方案,但它只處理有限數量的點。 – Luke101

+0

@ Luke101。 。 。 「......」的目的代表可以使用與其他行相同的結構來擴展代碼。 –

0

這裏有一個辦法做到這一點。我並不是說這是唯一的,甚至是最好的方式,但它是一個方式

SELECT ID, 
     NAME 
FROM 
    (SELECT ID, NAME, 
     INSTR(NAME, '.', 1, 1) AS FIRST_DOT_INDEX, 
     INSTR(NAME, '.', 1, 2) AS SECOND_DOT_INDEX, 
     INSTR(NAME, '.', 1, 3) AS THIRD_DOT_INDEX, 
     INSTR(NAME, '.', 1, 4) AS FOURTH_DOT_INDEX 
    FROM test_table) 
ORDER BY SUBSTR(NAME, 1, FIRST_DOT_INDEX-1), 
     TO_NUMBER(SUBSTR(NAME, FIRST_DOT_INDEX+1, (CASE WHEN SECOND_DOT_INDEX>0 
                 THEN SECOND_DOT_INDEX-1 
                 ELSE LENGTH(NAME) 
                END - FIRST_DOT_INDEX))), 
     TO_NUMBER(CASE WHEN SECOND_DOT_INDEX = 0 AND THIRD_DOT_INDEX = 0 
        THEN '0' 
        ELSE SUBSTR(NAME, SECOND_DOT_INDEX+1, (CASE WHEN THIRD_DOT_INDEX>0 
                   THEN THIRD_DOT_INDEX-1 
                   ELSE LENGTH(NAME) 
                  END - SECOND_DOT_INDEX)) 
        END), 
     TO_NUMBER(CASE WHEN THIRD_DOT_INDEX > 0 
        THEN SUBSTR(NAME, THIRD_DOT_INDEX+1, LENGTH(NAME) - THIRD_DOT_INDEX) 
        ELSE '0' 
        END); 

分享和享受。

+0

這是否說明我有不同長度的字符和DOT段? –

0

使用正則表達式可以解決你的問題,

select * 
from new_table 
    order by to_number(regexp_replace(name,'[[:alpha:].]*')); 

什麼這個查詢意味着我更換字母字符+的「 「。來自NAME列的字符,轉換爲數字,然後排序。

我希望這是有益的,享受!

+0

此解決方案在A.5.10.1之前排序A.5.11 – Luke101

+0

這類作品按照字符串的長度先排序,然後按正確的順序排序。 –

0

我的問題實際上是在另一篇文章中回答的,我發佈了一個類似但無關的問題。

Oracle SQL doesn't support lookaround assertions, which would be useful for this case: 

s/([0-9](?<![0-9]))/0\1/g 

You'll have to use at least two replacements: 

REGEXP_REPLACE(REGEXP_REPLACE(col, '([0-9]+)', '0\1'), '0([0-9]{2})', '\1')` 

感謝acheong87的解決方案。 Oracle SQL Regexp_replace matching