Странное поведение команды 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
)
)