Question

I am having a sorting issue with oracle 10g. Not sure if it is specific to 10g or not.

I have the following table:

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

Performing the generic SELECT NAME FROM table_name ORDER BY 1 produces:

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

I would like it to sort correctly when those sections have numbers greater than 9, like so:

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

I have way more number entries than this with varying lengths and many sections with number segments greater than 10. I was trying to mess around with regexp_replace() in the order by clause but have had no luck. Any help would be greatly appreciated.

Was it helpful?

Solution

Try this

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 is a fiddle

OTHER TIPS

The following may give you an idea of what to do. To order values of the form "A.", you can order by the length of the expression followed by the expression. So, A.1 and A.2 are before A.10, because their length is shorter.

You can expand this, with an order by as follows:

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

Here's a way to do it. I'm not saying this is the only or even the best way, but it is A way:

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

Share and enjoy.

Using regex can solve your problem ,

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

What this query means that I'm replacing the alpha characters + the '.' character from the column NAME , coverting to number and then sorting.

I hope this was helpful , enjoy !

My question was actually answered in another post that I posted for a similar but unrelated issue.

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')`

Thanks to acheong87 for the solution. Oracle SQL Regexp_replace matching

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top