Семантика CHAR и ORA-01461

Я поддерживаю приложение на основе PHP с серверной частью Oracle (функции OCI8). Приложение разработано с помощью Oracle 10g XE и развернуто в любой версии, которой владеет заказчик.

Приложение обрабатывает однобайтовый текст (ISO-8859-15), и у меня никогда не было проблем при разработке с использованием западноевропейской версии Oracle XE. Однако я недавно установил версию Universal, и у меня возникают проблемы при вставке больших строк с символами, отличными от ASCII. Эта версия устанавливает NLS_CHARACTERSET = AL32UTF8; поскольку мое приложение использует WE8ISO8859P15, Oracle незаметно преобразует мои входные данные из ISO-8859-15 в UTF-8 (что нормально). Но кажется, что определенные проверки размера идут не так: строка с 1500 символами (1500 байтов в ISO-8889-15, 4500 байтов в UTF-8) кажется переполняющей столбец VARCHAR2(4000 CHAR).

Я создал эту тестовую таблицу:

CREATE TABLE FOO (
    FOO_ID NUMBER NOT NULL ENABLE,
    DATA_BYTE VARCHAR2(4000 BYTE),
    DATA_CHAR VARCHAR2(4000 CHAR),

    CONSTRAINT FOO_PK PRIMARY KEY (FOO_ID)
);

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

<?php
$connection = oci_connect(DB_USER, DB_PASS, DB_CONN_STRING, 'WE8ISO8859P15');
if( !$connection ){
    $e = oci_error();
    die(htmlspecialchars($e['message']));
}

$id = 1;
$data = str_repeat('€', 1500);

$sql = 'INSERT INTO FOO (FOO_ID, DATA_CHAR) ' .
    'VALUES (:id, :data)';
$res = oci_parse($connection, $sql);
if(!$res){
    $e = oci_error();
    die(htmlspecialchars($e['message']));
}
if(!oci_bind_by_name($res, ':id', $id)){
    $e = oci_error();
    die(htmlspecialchars($e['message']));
}
if(!oci_bind_by_name($res, ':data', $data)){
    $e = oci_error();
    die(htmlspecialchars($e['message']));
}
if(!oci_execute($res, OCI_COMMIT_ON_SUCCESS)){
    $e = oci_error();
    die(htmlspecialchars($e['message']));
}

... который вызывает:

Предупреждение: oci_execute (): ORA-01461: sólo puede enlazar un valor LONG para insertarlo en una columna LONG

Это та же ошибка, которую я получаю, когда пытаюсь вставить строку 4001 char. Этого не произойдет, если я вставлю xxx... вместо €€€ , и этого не произойдет, если я сохраню свой скрипт как UTF-8 и подключусь как таковой:

<?php
$connection = oci_connect(DB_USER, DB_PASS, DB_CONN_STRING, 'AL32UTF8');

[Обновление: мой тест был ошибочным. Использование UTF-8 не позволяет избежать ошибки ORA-01461]

Как я могу обойти эту проблему? Параметр базы данных NLS_CHARACTERSET - это не то, что я контролирую , и переключение моего приложения на UTF-8 может вызвать другие проблемы (почти все наши клиенты имеют однобайтовые базы данных).


person Álvaro González    schedule 08.03.2011    source источник


Ответы (1)


Вероятно, это не то, что вы можете обойти, если вы не хотите использовать CLOB вместо VARCHAR2.

В Oracle при объявлении столбца по умолчанию используется семантика длины байта. Так, например, VARCHAR2 (100) выделяет 100 байт памяти. Если вы используете однобайтовый набор символов, такой как ISO 8859-1, для каждого символа требуется 1 байт памяти, поэтому при этом также выделяется место для 100 символов. Но если вы используете многобайтовый набор символов, такой как UFT-8, для каждого символа может потребоваться от 1 до 4 байтов памяти. Следовательно, в зависимости от данных, VARCHAR2 (100) может хранить только 25 символов данных (для английских символов обычно требуется 1 байт, для европейских символов обычно требуется 2 байта, а для азиатских символов обычно требуется 3 байта).

Вы можете указать Oracle использовать семантику длины символа, что обычно я предлагаю при переходе от базы данных ISO-8859-1 к базе данных UTF-8. Если вы объявите столбец VARCHAR2 (100 CHAR), Oracle выделит пространство для 100 символов независимо от того, будет ли это 100 или 400 байтов. Вы также можете установить для параметра NLS_LENGTH_SEMANTICS значение CHAR, чтобы изменить значение по умолчанию (для нового DDL), чтобы VARCHAR2 (100) выделял 100 символов памяти, а не 100 байтов.

Однако, к сожалению для вас, ограничение на размер Oracle VARCHAR2 (в контексте механизма SQL, а не PL / SQL) составляет 4000 байтов. Таким образом, даже если вы объявите столбец VARCHAR2 (4000 CHAR), вы все равно будете ограничены фактической вставкой 4000 байтов данных, которые могут содержать всего 1000 символов. Например, в базе данных, использующей набор символов AL32UTF8, я могу объявить столбец VARCHAR2 (4000 CHAR), но вставка символа, для которого требуется 2 байта памяти, показывает, что я действительно не могу вставить 4000 символов данных

SQL> create table foo (
  2    col1 varchar2(4000 char)
  3  );

Table created.

SQL> insert into foo values( rpad( 'abcde', 4000, unistr('\00f6') ) );

1 row created.

SQL> ed
Wrote file afiedt.buf

  1* insert into foo values( rpad( 'abcde', 6000, unistr('\00f6') ) )
SQL> /

1 row created.

SQL> select length(col1), lengthb(col1)
  2    from foo;

LENGTH(COL1) LENGTHB(COL1)
------------ -------------
        2003          4000
        2003          4000

Если вам нужно хранить 4000 символов данных UTF-8, вам понадобится тип данных, который может обрабатывать 16000 байтов, что потребует перехода в CLOB.

person Justin Cave    schedule 08.03.2011
comment
Вы правы, у меня была ошибка в моем тестовом скрипте UTF-8: он также запускает ORA-01461. Кажется, что VARCHAR2(4000 CHAR) не может содержать более 4000 байтов. Я изучу, уменьшать ли размер столбца или переключаться на CLOB. - person Álvaro González; 08.03.2011
comment
Я нашел ссылку: когда вы создаете таблицу со столбцом VARCHAR2, вы указываете максимальную длину строки (в байтах или символах) от 1 до 4000 байтов для столбца VARCHAR2. - download.oracle.com/docs/cd /B19306_01/server.102/b14220/ - person Álvaro González; 08.03.2011
comment
Вы можете свести к минимуму проблему, используя альтернативный набор символов с фиксированным байтом. Например, JA16SJIS использует два байта для японских символов, а TH8TISASCII - это однобайтовый набор символов тайского языка. - person Gary Myers; 09.03.2011
comment
@Gary - Единственная проблема в том, что Альваро использует XE, что означает, что единственными двумя вариантами набора символов являются ISO 8859-1 и UTF-8. При обычной установке вы абсолютно правы. Конечно, я не уверен, действительно ли вы можете изменить набор символов базы данных XE после ее установки. Oracle не поощряет это, но не похоже, что они поддерживают какие-либо установки XE ... - person Justin Cave; 09.03.2011
comment
@Gary Myers - Если проблема в том, что VARCHAR2 не может содержать более 4000 байтов (независимо от кодировки), использование JA16SJIS снизит максимальную емкость до 2000 символов. Единственный плюс в том, что я могу предсказать, подойдет ли данная строка. В том-то и дело, или я что-то упускаю? - person Álvaro González; 14.03.2011
comment
Также проверьте следующую демонстрацию и объяснение для ORA-01461 crealoq.com/solved/ - person clq; 21.10.2015