Странное поведение команды LENGTH - ORACLE

Я столкнулся с непредвиденной ситуацией, которую я не мог понять. Кроме того, документация о функциях, о которых я буду писать, может что-то осветить.

У меня есть таблица с полем titulo varchar2(55). Я в Бразилии, у некоторых персонажей в этом поле есть акценты, и моя цель - создать подобное поле без акцентов (заменено оригинальным персонажем, так как этот á стал a и т.д.).

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

Мой код обновления похож:

update myTable 
   set TITULO_URL = replace(
                 utl_raw.cast_to_varchar2(
                           nlssort(titulo, 'nls_sort=binary_ai')
                                         )
                            ,' ','_');

Как я сказал, целью является преобразование каждого акцентированного символа в его эквиваленте без акцента плюс символ пробела для _

Затем я получил эту ошибку:

ORA-12899: value too large for column 
     "mySchem"."myTable"."TITULO_URL" (actual: 56, maximum: 55)

И сначала я, хотя, возможно, эти функции добавляют некоторый символ, позвольте мне проверить. Я сделал команду select, чтобы получить строку, где titulo имеет 55 символов.

select titulo from myTable where length(titulo) = 55

Затем я выбираю строку для выполнения некоторых тестов, строка, которую я выбираю, имеет это значение: 'FGHJTÓRYO DE YHJKS DA DGHQÇÃA DE ASGA XCVBGL EASDEÔNASD' (я изменил бит, чтобы сохранить данные, но результат тот же)

Когда я делаю следующий оператор select, что вещи стали странными:

select a, length(a), b, length(b)
  from ( select 'FGHJTÓRYO DE YHJKS DA DGHQÇÃA DE ASGA XCVBGL EASDEÔNASD' a,
                replace(
                   utl_raw.cast_to_varchar2( 
                               nlssort('FGHJTÓRYO DE YHJKS DA DGHQÇÃA DE ASGA XCVBGL EASDEÔNASD', 'nls_sort=binary_ai')
                                           )
                       ,' ','_') b
           from dual
       )

Результат для этого sql (я поставил значения один за другим для лучшей визуализации):

                     a                                       LENGTH(a)
FGHJTÓRYO DE YHJKS DA DGHQÇÃA DE ASGA XCVBGL EASDEÔNASD        55     
                     b                                       LENGTH(b)
fghjtoryo_de_yhjks_da_dghqcaa_de_asga_xcvbgl_easdeonasd        56

Сравнение двух строк один над другим не имеет разницы в размере:

FGHJTÓRYO DE YHJKS DA DGHQÇÃA DE ASGA XCVBGL EASDEÔNASD
fghjtoryo_de_yhjks_da_dghqcaa_de_asga_xcvbgl_easdeonasd

Я тестировал этот запрос на Toad, PLSQL Developer и SQLPLUSW с одинаковым результатом. Поэтому мой вопрос Где эта ДЛИНА (b) = 56 появилась от? Я знаю, что это может быть что-то с набором символов, но я не мог понять, почему. Я даже тестировал команду trim, и результат тот же.

Другие тесты, которые я сделал

  • substr(b, 1,55) результат был тем же самым текстом, что и выше
  • lenght(trim(b)) результат был 56
  • substr(b,56) результат был пустым (без пустого, без пробела, просто пустым)

Предлагается @Sebas:

  • LENGTHB(b) результат был 56
  • ASCII(substr(b,56))

Итак, снова: Где эта ДЛИНА (b) = 56 появилась от?

Извините за длинный пост и благодарю вас за тех, кто здесь (прочитайте все). Спасибо тем, кто еще не читал:)

С наилучшими пожеланиями

Ответ 1

Документация по функциям 'nlssort' не указывает, что выходная строка будет нормализована входной строкой или будет иметь одинаковую длину. Назначение функции - вернуть данные, которые можно использовать для сортировки входной строки.

См. http://docs.oracle.com/cd/E11882_01/server.112/e26088/functions113.htm#SQLRF51561

Заманчиво использовать его для нормализации вашей строки, поскольку, по-видимому, она работает, но вы играете здесь в азартные игры...

Черт, он может даже дать LENGTH (b) = 200 и все еще делать то, что он должен делать:)

Ответ 2

1) Oracle выделяет длины в байтах и ​​длинах символов: varchar2(55) означает 55 байтов, поэтому 55 символов UTF-8 подходят только в том случае, если вам повезло: вы должны объявить свое поле как varchar2 (55 char).

2) Контуры, такие как

replace(utl_raw.cast_to_varchar2(nlssort(
'FGHJTÓRYO DE YHJKS DA DGHQÇÃA DE ASGA XCVBGL EASDEÔNASD', 
'nls_sort=binary_ai')),' ','_') b

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

3) Что касается заявленной задачи удаления акцентов, вы должны сделать это самостоятельно с помощью ЗАМЕНЫ, ПЕРЕВОДА и т.д., потому что только вы знаете свои требования; это не нормализация Юникода или что-то "стандартное", никаких ярлыков нет. Вы можете определить функцию и вызвать ее из любого запроса и любой PL/SQL-программы без уродливого копирования и вставки.

Ответ 3

Функция "nlssort()" возвращает двоичный код с дополнительным 00 в конце исходного двоичного файла строки.

Тестирование:

select NLSSORT('abc') from dual

Выход:

61626300

эту проблему можно решить, удалив последние 2 цифры из возврата NLSSORT.

Решение:

  select a, length(a), b, length(b)   
    from ( select 'FGHJTÓRYO DE YHJKS DA DGHQÇÃA DE ASGA XCVBGL EASDEÔNASD' a,
                  replace(
                      utl_raw.cast_to_varchar2( 
                                 substr(nlssort('FGHJTÓRYO DE YHJKS DA DGHQÇÃA DE ASGA XCVBGL EASDEÔNASD', 'nls_sort=binary_ai'),1, 
                                                  length(nlssort('FGHJTÓRYO DE YHJKS DA DGHQÇÃA DE ASGA XCVBGL EASDEÔNASD', 'nls_sort=binary_ai'))-2 
                                                 )
                                          )

                         ,' ','_') b
             from dual
     )

  )