Проблема с оператором Oracle / SQL ORDER BY - PullRequest
5 голосов
/ 17 мая 2010

У меня есть следующее содержимое в столбце varchar2:

   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

Когда я запрашиваю базу данных, мне нужен результат:

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

параметр NLS_SORT установлен на немецкий, простой "order by COLUMN DESC/ASC" не работает, как исключение. Возвращает

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

есть предложения?

Ответы [ 7 ]

7 голосов
/ 17 мая 2010

Предполагая, что это 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)
2 голосов
/ 17 мая 2010

@ RobVanWijk делает соответствующий комментарий:

Вы можете утверждать, что это должно быть хранится вместо четырех числовых столбцов из 1 строки.

Это классический случай, когда было бы здорово, если бы мы могли определять домены данных в наших схемах. Oracle не поддерживает это, но, если честно, ни один из других поставщиков СУБД. Тем не менее, мы можем использовать пользовательские типы для создания сложных типов данных с прикрепленным поведением. Обидно, что синтаксис UDT такой неуклюжий.

Как бы то ни было, комментарий Роба напомнил мне, что я недавно создал Proof of Concept, используя этот домен. Я публикую это не как серьезное решение, а как показатель того, насколько аккуратными могут быть вещи ...

Спецификация типа ...

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

... и тело ...

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

Я буду использовать этот тип для определения столбца в тестовой таблице, который я буду заполнять некоторыми тестовыми данными.

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>

Всегда помните: при ссылках на атрибуты или методы столбца UDT мы должны использовать псевдоним таблицы :

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>
1 голос
/ 17 мая 2010
  1. Создание курсора, с которым вы будете зацикливаться;
  2. Использование FOR..LOOP при использовании [TABLE_NAME]% ROWTYPE в качестве контейнера информационных данных;
  3. Разделить строкуи приведите к NUMBER первую полученную строку;
  4. Сделайте то же самое для следующих строк, полученных при разбиении снова и снова для каждого из чисел;
  5. Вставьте отсортированный результат во временную таблицу ивыберите из него результат.

В противном случае:

Вы также можете добавить новый столбец для сортировки записей в вашей таблице данных, что, на мой взгляд, является лучшим подходом, если это возможнов вашу ситуацию.

0 голосов
/ 17 мая 2010

Используйте регулярные выражения:

порядок по lpad (ltrim (regexp_substr (COLUMN, '(.? [^.] ) | ([^.] ;?)', 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' )

0 голосов
/ 17 мая 2010

10 в строке отличается от 10 в числе.

Строки будут отсортированы следующим образом

10
4
8

Числа будут сортироваться следующим образом

4
8
10

Вам придется взять часть строки (до первого периода), преобразовать ее в число и затем отсортировать. Ищите для этого функции SUBSTR и INSTR. Затем найдите функцию для преобразования из строки в целое число.

Вам придется сделать это для каждого октета (я говорю это только потому, что показанные вами числа похожи на IP-адрес)

Таким образом, в основном вам нужно разделить данные на четыре целочисленных столбца, а затем отсортировать их (или просто разделить в предложении ORDER BY).

0 голосов
/ 17 мая 2010

Это правильный порядок. 1 предшествует 8, а предшествует 9. Поскольку это не «числа», это текст, вам, вероятно, придется преобразовать первую часть в число или заполнить нулями при сортировке.

Например, если вы сделаете каждый сегмент шириной 3 цифры, скажем, 008.002.004.001, то вы можете отсортировать по алфавиту, и это сработает. Вы можете отобразить его так, как хотите, но сортировать его нужно по-другому.

0 голосов
/ 17 мая 2010

должно быть

order by COLUMN

По умолчанию это только восходящий.

...