2010-05-17 8 views
5

tengo el siguiente contenido dentro de una columna VARCHAR2:Problema con Oracle/SQL ORDER BY

10.1.2.3 
    10.2.3.4 
    8.3.4.1 
    8.3.2.1 
    4.2.1.3 
    4.3.2.1 
    9.3.1.2 

Cuando i consultar la base de que necesito un resultado ordenó:

4.... 
8.... 
9.... 
10... 

el parámetro NLS_SORT es establecido en alemán, un simple "order by COLUMN DESC/ASC" no funciona como exceptuado. Devuelve

10..... 
8...... 
9...... 

alguna sugerencia?

+0

Independientemente de respuesta que se obtiene aquí: Nota mental para el futuro, no almacene los números como cadenas (y son números si necesita procesarlos como números). –

+0

gracias, pero no fue mi decisión :-) – opHASnoNAME

+0

@LasseVKarlsen - esos no son números, tienen demasiados períodos en ellos . Claramente, son cadenas que simplemente están compuestas por caracteres numéricos, al igual que las direcciones IP (que es, tal vez, la fuente de estos datos). – APC

Respuesta

7

asumiendo que es una dirección IP

SELECT col 
    FROM table 
ORDER BY 
(regexp_substr(col, '[^.]+', 1, 1) * 256 * 256 * 256) + (regexp_substr(col, '[^.]+', 1, 2) * 256 * 256) + (regexp_substr(col, '[^.]+', 1, 3) * 256)+ regexp_substr(col, '[^.]+', 1, 4) 
+1

+1 ¡Genial! ¡No sabía sobre este! =) –

-1

debe ser

order by COLUMN 

Por defecto es ascendente solamente.

0

Ese es el orden correcto. 1 viene antes que 8, que viene antes de 9. Como esos no son "números", son texto, es probable que tenga que convertir la primera parte en un número, o cero llenarlos al ordenar.

Por ejemplo, si hace que cada segmento tenga 3 dígitos, digamos 008.002.004.001, puede ordenarlos alfabéticamente y funcionará. Puede mostrarlo como desee, pero debe ordenarlo de manera diferente.

0

10 en una cadena es diferente de 10 en un número.

Cuerdas va a clasificar de la siguiente manera

10 
4 
8 

Números va a clasificar como esto

4 
8 
10 

Usted tendrá que tomar parte de la cadena (antes del primer período), convertirlo a un número y luego ordenarlo Busque las funciones SUBSTR e INSTR para esto. Luego busque una función para convertir de cadena a entero.

Tendrá que hacer esto para cada octeto (que sólo dicen que debido a que los números que ha demostrado mirada como una dirección IP)

Así que, básicamente, tendrá que separar los datos en cuatro columnas de tipo entero y luego ordenar por ellos (o, simplemente hacer la separación en la cláusula ORDER BY).

+0

pero entonces ¿10.10 no vendrá antes de 10.9? él tiene que hacer eso con cada parte de la cadena. – MJB

+0

@MJB respuesta editada para incluir respuesta a sus comentarios. –

1
  1. Crea un cursor con el que entrarás;
  2. Use un FOR..LOOP mientras usa un [TABLE_NAME]% ROWTYPE como contenedor de datos de información;
  3. Divida su cuerda y moldee a NUMBER la primera cuerda obtenida;
  4. Haga lo mismo con las siguientes cadenas obtenidas dividiendo una y otra vez para cada uno de los números;
  5. Inserta el resultado ordenado en una tabla temporal y selecciona el resultado.

lo contrario:

También puede añadir una nueva columna para ordenar los registros con los de la tabla de datos, que en mi opinión es el mejor enfoque, si factible en su situación.

0

utilizar expresiones regulares:.? [. ^]

ordenado por lpad (LTRIM (regexp_substr (columna, '() | ([. ^];?) ', 1,1),'. '), 3,' 0 ') , lpad (ltrim (regexp_substr (COLUMN,' (.? [^.] ) | ([^.]; ?) ', 1,2),'. '), 3,' 0 ') , lpad (ltrim (regexp_substr (COLUMN,' (.? [^.] ) | ([^.];?) ', 1,3),'. '), 3,' 0 ') , lpad (ltrim (regexp_substr (COLUMN, '(.? [^.] ) | ([^.];?)', 1,4), '.'), 3, '0')

2

@RobVanWijk hace un comentario pertinente:

se podría argumentar que esos documentos deben estar almacena como cuatro columnas numéricas en vez de 1 cadena.

Este es un caso clásico en el que sería genial si pudiéramos definir dominios de datos en nuestros esquemas. Oracle no es compatible con esto, pero es justo ni ninguno de los otros proveedores de DBMS. Aún así, podemos emplear tipos definidos por el usuario para construir tipos de datos complejos con comportamiento adjunto. Es una lástima que la sintaxis UDT sea tan torpe.

De todos modos, el comentario de Rob me ha recordado que me arrebaté una Prueba de concepto usando este mismo dominio hace un tiempo. He colgado, no como una solución seria, sino como un indicador de cómo podrían ser las cosas interesantes ....

La especificación del tipo ...

create or replace type ip_address_t as object 
    (octet1 number(3,0) 
    , octet2 number(3,0) 
    , octet3 number(3,0) 
    , octet4 number(3,0) 
    , constructor function ip_address_t 
      (octet1 number, octet2 number, octet3 number, octet4 number) 
         return self as result 
    , member function to_string 
         return varchar2 
    , member function to_padded_string 
         return varchar2 
    , map member function sort_order return number) 
/

... y el cuerpo ...

create or replace type body ip_address_t as 

    constructor function ip_address_t 
     (octet1 number, octet2 number, octet3 number, octet4 number) 
         return self as result 
    is 
    begin 
     if (octet1 is null or octet2 is null or octet3 is null or octet4 is null) 
     then 
      raise INVALID_NUMBER; 
     else 
      self.octet1 := octet1; 
      self.octet2 := octet2; 
      self.octet3 := octet3; 
      self.octet4 := octet4; 
     end if; 
     return; 
    end ip_address_t; 

    member function to_string return varchar2 
    is 
    begin  
     return trim(to_char(self.octet1))||'.'|| 
       trim(to_char(self.octet2))||'.'|| 
       trim(to_char(self.octet3))||'.'|| 
       trim(to_char(self.octet4)); 
    end to_string; 

    member function to_padded_string return varchar2 
    is 
    begin  
     return lpad(trim(to_char(self.octet1)),3,'0')||'.'|| 
       lpad(trim(to_char(self.octet2)),3,'0')||'.'|| 
       lpad(trim(to_char(self.octet3)),3,'0')||'.'|| 
       lpad(trim(to_char(self.octet4)),3,'0'); 
    end to_padded_string; 

    map member function sort_order return number 
    is 
    begin  
     return to_number(
         lpad(trim(to_char(self.octet1)),3,'0')|| 
         lpad(trim(to_char(self.octet2)),3,'0')|| 
         lpad(trim(to_char(self.octet3)),3,'0')|| 
         lpad(trim(to_char(self.octet4)),3,'0') 
      ); 
    end sort_order; 

end; 
/

Utilizaré este tipo para definir una columna en una tabla de prueba que completaré con algunos datos de prueba.

SQL> create table t23 (id number, domnain_name varchar2(128), ip_address ip_address_t) 
    2/

Table created. 

SQL> insert into t23 values (1000, 'http://www.example.com', ip_address_t(8,1,3,0)) 
    2/

1 row created. 

SQL> insert into t23 values (800, 'http://www.example1.com', ip_address_t(9,1,2,0)) 
    2/

1 row created. 

SQL> insert into t23 values (1100, 'http://www.example2.com', ip_address_t(10,1,2,0)) 
    2/

1 row created. 

SQL> insert into t23 values (1103, 'http://www.example3.com', ip_address_t(10,1,25,0)) 
    2/

1 row created. 

SQL> insert into t23 values (1102, 'http://www.example4.com', ip_address_t(1,11,25,0)) 
    2/

1 row created. 

SQL> insert into t23 values (1101, 'http://www.example5.com', ip_address_t(11,1,25,0)) 
    2/

1 row created. 

SQL> 

Recuerde siempre: al hacer referencia a los atributos o métodos de una columna UDT tenemos que utilizar un alias de mesa:

SQL> select t.id 
    2   , t.ip_address.to_string() as ip_address 
    3 from t23 t 
    4 order by t.ip_address.sort_order() 
    5/

     ID IP_ADDRESS 
---------- --------------- 
     1102 1.11.25.0 
     1000 8.1.3.0 
     800 9.1.2.0 
     1100 10.1.2.0 
     1103 10.1.25.0 
     1101 11.1.25.0 

SQL> 
+0

bastante la explicación detallada. Tuve que dar +1 por todo el trabajo. – MJB