rexp_like - не могу понять, как это работает - PullRequest
1 голос
/ 15 мая 2019

Я получил таблицу со столбцом «token», которая должна содержать только букву, это до сих пор применяется с простым ограничением regexp_like, но недавно я заметил определенную проблему: буква «а» (строчная A) не допускается. все остальные стандартные буквы ASCII работают, насколько я могу судить.

База данных:

Oracle Database 12c Enterprise Edition, выпуск 12.1.0.2.0 - 64 бита
Рабочая версия PL / SQL версии 12.1.0.2.0 - Рабочая версия

SELECT CHR(num), num
FROM (
  SELECT LEVEL AS num FROM dual CONNECT BY LEVEL <= 200
) WHERE REGEXP_LIKE(CHR(num), '^[A-Z]+$');

с использованием шаблона сопоставления 'c' или 'i', просто использование [a-z] или других вариаций дает правильный результат, но простое [A-Z] пропускает 'a' и только эту букву.

Edit: Да, использование [a-zA-Z] будет охватывать все символы, так же как и использование REGEXP_LIKE(CHR(num), '^[A-Z]+$', 'i') ('i' - match_pattern для регистра без учета регистра), но вопрос в том, почему здесь пропущен только нижний регистр 'a'. Мы генерируем некоторые другие строки и константы из этих токенов и имеем аналогичные ограничения по всей нашей базе данных, поэтому я не просто хочу исправить эту проблему, но и понять, что ее вызвало.

Ответы [ 2 ]

1 голос
/ 15 мая 2019

можно воспроизвести, выполнив

ALTER SESSION SET NLS_SORT = FRENCH;

SELECT CHR(num), num FROM (
  SELECT LEVEL AS num FROM dual CONNECT BY LEVEL <= 200
) WHERE REGEXP_LIKE(CHR(num), '^[a-z]+$')
order by CHR(num);

сравнить с

ALTER SESSION SET NLS_SORT = BINARY;

SELECT CHR(num), num FROM (
  SELECT LEVEL AS num FROM dual CONNECT BY LEVEL <= 200
) WHERE REGEXP_LIKE(CHR(num), '^[a-z]+$')
order by CHR(num);

похоже, что набор символов регулярного выражения следует параметру NLS_SORT

0 голосов
/ 15 мая 2019

Использование [A-Z] должно включать только заглавные буквы и исключать все строчные.По крайней мере, это то, что он должен - и делает - делать на английском языке с двоичной сортировкой:

alter session set nls_language = 'ENGLISH';

select * from nls_session_parameters where parameter = 'NLS_SORT';

PARAMETER                      VALUE                                   
------------------------------ ----------------------------------------
NLS_SORT                       BINARY                                  

SELECT CHR(num), num
FROM (
  SELECT LEVEL AS num FROM dual CONNECT BY LEVEL <= 200
) WHERE REGEXP_LIKE(CHR(num), '^[A-Z]+$');

C        NUM
- ----------
A         65
...
Z         90

26 rows selected. 

На немецком языке (или любом другом языке, который изменяет порядок сортировки по умолчанию), он делает то, что я считаю, выdescription:

alter session set nls_language = 'GERMAN';

select * from nls_session_parameters where parameter = 'NLS_SORT';

PARAMETER                      VALUE                                   
------------------------------ ----------------------------------------
NLS_SORT                       GERMAN                                  

SELECT CHR(num), num
FROM (
  SELECT LEVEL AS num FROM dual CONNECT BY LEVEL <= 200
) WHERE REGEXP_LIKE(CHR(num), '^[A-Z]+$');

C        NUM
- ----------
A         65
...
X         88
Y         89
Z         90
b         98
c         99
d        100
...
z        122

Переключение на классы символов делает его поведение более последовательным:

alter session set nls_language = 'GERMAN';

SELECT CHR(num), num
FROM (
  SELECT LEVEL AS num FROM dual CONNECT BY LEVEL <= 200
) WHERE REGEXP_LIKE(CHR(num), '^[[:upper:]]+$');

C        NUM
- ----------
A         65
...
Z         90

26 rows selected. 

Если вы хотите включить все символы верхнего и нижнего регистра, тогда вы можете использовать [:alpha:] вместо [:upper:].Обратите внимание, что любой из них будет включать в себя диакритические знаки (а lower или alpha будет включать 'ß');что может или не может быть желательным:

SELECT character
FROM (
  SELECT CHR(LEVEL + 64) AS character
  FROM dual CONNECT BY LEVEL <= 58
  UNION ALL SELECT column_value
  FROM TABLE(sys.odcivarchar2list('ß', 'ä', 'ö', 'ü', 'Ä', 'Ö', 'Ü'))
)
WHERE REGEXP_LIKE(character, '^[[:upper:]]+$')
ORDER BY character;

CHARACTER
---------
A
Ä
B
...

Это объясняется в документации .

Традиционные механизмы регулярных выражений были разработаны для работы только с английским текстом,Однако реализации регулярных выражений могут охватывать широкий спектр языков с характеристиками, которые сильно отличаются от западноевропейского текста.Реализация регулярных выражений в базе данных Oracle основана на Руководстве по регулярным выражениям Unicode.Функции SQL REGEXP работают со всеми наборами символов, которые поддерживаются в качестве наборов символов базы данных и национальных наборов символов.Более того, Oracle Database расширяет возможности сопоставления конструкций регулярных выражений POSIX для обработки уникальных лингвистических требований сопоставления многоязычных данных.

и

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

... База данных Oracle интерпретирует выражения диапазонов в соответствии с параметром NLS_SORT, чтобы определить элементы сопоставления, охватываемые данным диапазоном.

Языковое сопоставление в основном основано на поведении языков или поведении их пользователей.

Если у вас двоичная сортировка, то символы упорядочиваютсяоснованный исключительно на кодовой точке (значение ASCII / Unicode) каждого символа;«a» равно 97, что, очевидно, после (численно выше) «A», равного 65. Если вы выберете все символы и используете двоичную сортировку, вы увидите:

alter session set nls_language = 'GERMAN';
alter session set nls_sort = 'BINARY';

SELECT CHR(LEVEL + 64) AS character, NLSSORT(CHR(LEVEL + 64)) AS sort
FROM dual CONNECT BY LEVEL <= 58
UNION ALL SELECT column_value, NLSSORT(column_value)
FROM TABLE(sys.odcivarchar2list('ß', 'ä', 'ö', 'ü', 'Ä', 'Ö', 'Ü'))
ORDER BY character;

CHARACTER SORT     
--------- ---------
A         4100     
B         4200     
C         4300     
...
X         5800     
Y         5900     
Z         5A00     
[         5B00     
\         5C00     
]         5D00     
^         5E00     
_         5F00     
`         6000     
a         6100     
b         6200     
c         6300    
... 
x         7800     
y         7900     
z         7A00     
Ä         C38400   
Ö         C39600   
Ü         C39C00   
ß         C39F00   
ä         C3A400   
ö         C3B600   
ü         C3BC00   

65 rows selected. 

The nlssort() функция позволяет увидеть фактическое значение, которое Oracle использует для сортировки - здесь это двоичное значение для символов, поэтому «A» по-прежнему равно 65 (ну, 41 в шестнадцатеричном виде) плюс нулевой байт.Когда вы заказываете эти «а», все еще после «А».

При лингвистическом сопоставлении эти значения сортировки совершенно разные:

alter session set nls_sort = 'GERMAN';

SELECT CHR(LEVEL + 64) AS character, NLSSORT(CHR(LEVEL + 64)) AS sort
FROM dual CONNECT BY LEVEL <= 58
UNION ALL SELECT column_value, NLSSORT(column_value)
FROM TABLE(sys.odcivarchar2list('ß', 'ä', 'ö', 'ü', 'Ä', 'Ö', 'Ü'))
ORDER BY character;

CHARACTER SORT     
--------- ---------
[         00005B00 
\         00005C00 
]         00005D00 
^         00005E00 
_         00005F00 
`         00006000 
a         14000100 
A         14000200 
ä         14000900 
Ä         14000A00 
b         19000100 
B         19000200 
c         1E000100 
C         1E000200 
...
r         64000100 
R         64000200 
s         69000100 
S         69000300 
ß         69004400 
t         6E000100 
T         6E000200 
u         73000100 
U         73000200 
ü         73000900 
Ü         73000A00 
v         78000100 
V         78000200 
w         7A000100 
W         7A000200 
x         7D000100 
X         7D000200 
y         82000100 
Y         82000200 
z         87000100 
Z         87000200 

65 rows selected. 

«Лингвистическая» часть означает, что все варианты«a» вместе - «a», «A», «ä» и «Ä», в этом порядке.Затем все варианты 'b' и т. Д. С символами, специфичными для языка, сгруппированы с их родовыми отношениями.Об этом гораздо больше в документации, , включая примеры .

Поведение, которое вы видите, объясняется этим порядком.Посмотрите еще раз на начало этого отсортированного списка:

CHARACTER SORT     
--------- ---------
...
a         14000100 
A         14000200 
ä         14000900 
Ä         14000A00 
b         19000100 
B         19000200 
c         1E000100 
C         1E000200 

Если вы попытаетесь использовать регулярное выражение с диапазоном [A-Z], тогда оно начинается с 'A', но в таком порядке сортировки, который после 'a' - так что нижний регистр 'a' исключается.Точно так же, если вы используете [B-Z], тогда вы начинаете с 'B', что после всех вариантов 'a' и строчной буквы 'b', так что теперь это исключено.

...