Проверка строки на наличие запрещённых символов

Сергей Васкецов
Дата: 17.10.2015 02:26:02
Дано - поле и переменная типа NVARCHAR2(255).
Проверку можно сделать как на поле (constraint), так и перед выполнением insert или update (источник данных для поля один), это не принципиально. В некоторым смысле функция от переменной типа NVARCHAR2(255) даже была бы удобнее. Всё это выполняется в пакете в функции.

Задача - убедиться, что в строке есть только "интернациональные" символы от 32 до 127 включительно. Ошибочный символ может быть где угодно. Положение его, само значение, их число - ничего это не интересует, нужна простая проверка, есть хотя бы один символ вне диапазона 32-127, или все символы попадают в этот диапазон.

Как это сделать, чтобы в среднем проверка работала максимально быстро? В том смысле, что через эту проверку будут проходить и валидные строки, и невалидные, так вот необходимо минимизировать время проверки валидных строк. Если есть какой грязный полудокументированный способ - готов рассмотреть. Версия 11.

Или ничего тупее перебора символов по очереди не придумать?
матерные буквы
Дата: 17.10.2015 03:33:56
Сергей Васкецов
Или ничего тупее перебора символов по очереди не придумать?
Ни тупее, ни умнее цикла, явного или неявного, для обработки массива не придумаешь.
Elic
Дата: 17.10.2015 08:18:18
ltrim(s, c_Alphabet) is null
translate(s, chr(1)||c_Alphabet, chr(1)) is null
not regexp_like(name,'[^ -'||chr(127)||']')
Сергей Васкецов
максимально быстро?
Метрологией владеешь?
Сергей Васкецов
Дата: 17.10.2015 18:10:55
матерные буквы
Сергей Васкецов
Или ничего тупее перебора символов по очереди не придумать?
Ни тупее, ни умнее цикла, явного или неявного, для обработки массива не придумаешь.

Проблема в том, что в oracle это будет явно неоптимально ввиду отсутствия работы с указателями на символ внутри строки (его нельзя просто сдвинуть и проверить, как на сях или дельфях).

Elic
Метрологией владеешь?

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

Но пока что-то не очень у меня получается.

Через ltrim(s, c_Alphabet) is null - значит оно сначала всё вычистит, а потом вернёт результат, при этом может несколько раз пройти через запрещённые символы.

Через regexp - ORA-12728: недопустимый диапазон в регулярном выражении.

Интуитивно кажется, что REGEXP_LIKE будет быстрее всего, буду его копать, но пока родить результат не выходит.
Благодарю.
andreymx
Дата: 17.10.2015 19:51:59
Сергей Васкецов
REGEXP
в оракле - не самые производительные функции
Сергей Васкецов
Дата: 17.10.2015 20:59:53
andreymx, я безусловно продублирую проверку на уровне клиента, там оно и проще и быстрее, и главное что она там работает ))).

А тут ерунда какая-то:
regexp_like(name,'[^ -'||chr(127)||']') даёт

ORA-12728: недопустимый диапазон в регулярном выражении
12728. 00000 - "invalid range in regular expression"

Походу какие-то клинические ограничения в синтаксисе, или несовместимость какая с перловым синтаксисом. Причём если пишу через \0F - тот же результат. Но если верхнюю границу уменьшить - работает.

То есть буквально

SELECT COUNT(1) FROM table WHERE regexp_like(CODE,'[^ -'||CHR(122)||']')

работает, а

SELECT COUNT(1) FROM table WHERE regexp_like(CODE,'[^ -'||CHR(123)||']')

не работает (ORA-12728), и даже экранирование \ не помогает никак.

Мне бы хотя бы как оно заработало через regexp_like, чтобы можно было хотя бы сравнить с другими вариантами.

зы. Вообще не думал, что такие проблемы будут с regexp.
orawish
Дата: 17.10.2015 22:47:33
Сергей Васкецов
andreymx, я безусловно продублирую проверку на уровне клиента, там оно и проще и быстрее, и главное что она там работает ))).

А тут ерунда какая-то:..

матчасть изучайте. регулярусы в оракле вполне работают
(при том, что не идеальны и своеобразны ;)

для вашей же задачи они совершенно избыточны
сказали же вам уже - достаточно и - ltrim/rtrim/translate
Сергей Васкецов
Дата: 18.10.2015 00:09:56
>матчасть изучайте. регулярусы в оракле вполне работают
Несомненно, чем "оригинальнее" матчасть, тем яростнее посылают её изучать.
Вам не приходило в голову, что у меня сейчас будет задача, как это развидеть?
После перла - это какая-то поделка пьяных индусов.

Знаете, как сработало?
SELECT COUNT(1) FROM table WHERE regexp_like(CODE,'[^ -'||CHR(122)||CHR(123)||CHR(124)||CHR(125)||CHR(126)||']')

Может покажете мне матчасть, в которой написано, что именно так надо делать диапазон 32-126? ;)
Если знаете решение короче - приведите (исключая замену chr на символы).

>достаточно и - ltrim/rtrim/translate
Вот как потестирую скорость - тогда и скажу, достаточно или нет прошерстить всю строку сначала для выпиливания всех символов, а потом ещё раз для сравнения хотя бы длины сравнить.
Сергей Васкецов
Дата: 18.10.2015 00:15:55
orawish
ltrim

Слушайте, вообще Вы меня заинтриговали.

select ltrim('ЮaabЯsaЙ', X) from dual

Разумеется вместо Ю и Й может быть всё что угодно, и умляуты и иероглифы любые, что надо написать вместо X, чтобы оно заработало?
Сергей Васкецов
Дата: 18.10.2015 00:27:46
Сергей Васкецов
Знаете, как сработало?

Нет, однако чуда не произошло. Это разумеется тоже не сработало после расширения тестового задания.
Итого - решения через regexp_like нет.