2012-09-24 14 views
6

Tengo un problema de ordenación con Oracle 10g. No estoy seguro de si es específico para 10g o no.Oracle 10g SQL Sorting VARCHAR2

Tengo la siguiente tabla:

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 

Realización de la genérica SELECT NAME FROM table_name ORDER BY 1 produce:

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 

me gustaría que para ordenar correctamente cuando esas secciones tienen números mayores que 9, así:

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 

Tengo más entradas de número que esta w con diferentes longitudes y muchas secciones con un número de segmentos mayor que 10. Estaba tratando de jugar con regexp_replace() en la cláusula order by pero no he tenido suerte. Cualquier ayuda sería muy apreciada.

Respuesta

2

probar este

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 es un violín

+0

Eso definitivamente funciona. ¿Puedes explicar lo que está sucediendo? También esa es una consulta considerable que lleva mucho tiempo generar. ¿Es eso debido a With/Replace o la concatenación en ORDER BY? Gracias. –

+0

Primero lo convierte en un xml (no es realmente necesario, se puede hacer con substr e instr también, en realidad esto puede costar en rendimiento). luego toma cada nodo (una parte entre puntos) y lo rellena con ceros para que tenga una gran longitud (digamos 5). ahora se puede ordenar –

1

Lo siguiente puede darle una idea de qué hacer. Para pedir valores de la forma "A", puede ordenar por la longitud de la expresión seguida de la expresión. Entonces, A.1 y A.2 están antes que A.10, porque su longitud es más corta.

pueden ampliar este, con una orden por la manera siguiente:

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

No estoy seguro de seguir la lógica del código. ¿Faltan paréntesis o substr subs, len, substr dentro de la primera instrucción len? –

+0

Esta es una solución bastante buena pero solo maneja una cantidad limitada de puntos. – Luke101

+0

@ Luke101. . . El propósito de ". . ." es indicar que el código se puede extender usando la misma estructura que las otras líneas. –

0

Aquí está una manera de hacerlo. No estoy diciendo que esta es la única o incluso la mejor manera, pero es Un manera:

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

Compartir y disfrutar.

+0

¿Esto explicará el hecho de que tengo diferentes longitudes de caracteres y secciones de DOT? –

0

El uso de expresiones regulares puede resolver su problema,

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

Lo que esto significa consulta que estoy reemplazando los caracteres alfa + de la ' . ' carácter de la columna NOMBRE, volviendo a numerar y luego ordenando.

Espero que esto haya sido útil, ¡disfrútalo!

+0

¿Esta solución no resolvería A.5.11 antes de A.5.10.1 – Luke101

+0

Ese tipo de trabajo funciona, pero los ordena según la longitud de la cadena primero y luego en el orden correcto. –

0

Mi pregunta fue realmente respondida en otra publicación que publiqué para un problema similar pero no relacionado.

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

Gracias a acheong87 por la solución. Oracle SQL Regexp_replace matching

Cuestiones relacionadas