Согласно спецификацииVARCHAR2(max_size CHAR)
должен хранить max_size
символов. Я наблюдаю другое/странное поведение для текстов Unicode.
Рассмотрим этот пример:
create table test (id varchar2(3 char) not null, primary key (id));
insert into test(id) values('abc');
insert into test(id) values('ффф');
Query 1 ERROR: ORA-12899: value too large for column "TEST"."TEST"."ID" (actual: 6, maximum: 3)
Итак, 3 символа varchar2 на самом деле означают то же самое, что и байт? НЕТ :)
create table test (id varchar2(3 byte) not null, primary key (id))
insert into test(id) values('abc')
insert into test(id) values('ффф')
Query 1 ERROR: ORA-12899: value too large for column "TEST"."TEST"."ID" (actual: 18, maximum: 3)
И мой вопрос остается, как сообщить Oracle, что длина varchar2 предназначена для текста Unicode (точнее, UTF8)?
Обновление: можно ли записать SQL-запрос, который покажет все таблицы/столбцы, длина которых была в байтах?
На самом деле, моя проблема разделена на 2 части: неправильная кодировка запроса TablePlus, длина в байтах (без суффикса char) для случайных столбцов :)
Обновление 2: спасибо @Wernfried Domscheit!
Запрос показывает таблицу и столбцы с varchar2
, длина которых указана в byte
s:
SELECT TABLE_NAME, COLUMN_NAME, DATA_LENGTH, CHAR_USED
FROM USER_TAB_COLUMNS WHERE DATA_TYPE = 'VARCHAR2' AND CHAR_USED = 'B'
Ваш пример работает для меня:
SELECT *
FROM V$NLS_PARAMETERS
WHERE PARAMETER = 'NLS_CHARACTERSET';
PARAMETER VALUE
------------------------------
NLS_CHARACTERSET AL32UTF8
1 row selected.
CREATE TABLE TEST (ID VARCHAR2(3 CHAR));
Table created.
INSERT INTO TEST(ID) VALUES('abc');
1 row created.
INSERT INTO TEST(ID) VALUES('ффф');
1 row created.
Может у вас опечатка?
Обновлять:
Похоже, ваш клиент использует неправильные настройки символов.
ф (U+0444: кириллическая строчная буква Ef) имеет следующие значения байтов:
+-------------------------------------------------------------------------------+
|Encoding|hex |dec (bytes)|dec |binary |
+-------------------------------------------------------------------------------+
|UTF-8 |D1 84 |209 132 |53636 |11010001 10000100 |
|UTF-16BE|04 44 |4 68 |1092 |00000100 01000100 |
|UTF-16LE|44 04 |68 4 |17412 |01000100 00000100 |
|UTF-32BE|00 00 04 44|0 0 4 68 |1092 |00000000 00000000 00000100 01000100|
|UTF-32LE|44 04 00 00|68 4 0 0 |1141112832|01000100 00000100 00000000 00000000|
+-------------------------------------------------------------------------------+
DUMP
должен вернуться Typ=1 Len=6 CharacterSet=AL32UTF8: d1,84,d1,84,d1,84
, но вы получите ef,bf,bd
, который является U+FFFD: Символ замены
Вы не вставляете ффф
, он преобразуется в ���
.
Я предполагаю, что на самом деле ваш клиент использует UTF-8, но вы не сообщили базе данных, поэтому, скорее всего, база данных предполагает, что клиент использует значение по умолчанию US7ASCII
(или что-то еще). Клиент отправляет 6 байтов (d1,84,d1,84,d1,84
), но база данных Oracle интерпретирует их как 6 однобайтовых символов.
Обычно вы используете переменную среды NLS_LANG
, чтобы определить это. Однако dbeaver основан на Java, а Java/JDBC не использует настройки NLS_LANG
— по крайней мере, по умолчанию.
Перепроверил с помощью вашего SQL (скопируйте и вставьте в консоль + удалите существующую тестовую таблицу). Та же ошибка. Моя версия базы данных: "Oracle Database 11g Express Edition Release 11.2.0.2.0 - 64-битная версия"
Какой инструмент вы используете? Попробуйте INSERT INTO TEST(ID) VALUES('ф')
и проверьте с помощью DUMP()
, что на самом деле было вставлено.
TablePlus, dbeaver или Oracle jdbc (тонкий) драйвер Java
Что вы получаете от SELECT DUMP(ID, 1016) FROM TEST;
?
Тип=1 Len=6 CharacterSet=AL32UTF8: ef,bf,bd,ef,bf,bd
Ты прав! Это ДВЕ разные проблемы: TablePlus неправильно установил кодировку запроса. Некоторые столбцы были предоставлены без обозначения char (т.е. были в байтах!) Можно ли написать скрипт, который показывает все таблицы/столбцы, где длина varchar указана в байтах?
Да, попробуйте SELECT TABLE_NAME, COLUMN_NAME, DATA_LENGTH, CHAR_USED FROM USER_TAB_COLUMNS WHERE DATA_TYPE = 'VARCHAR2' AND CHAR_USED = 'B'
Также, если вы не укажете ни BYTE
, ни CHAR
, то по умолчанию задается параметр сеанса NLS_LENGTH_SEMANTICS
Неа. SELECT * FROM V$NLS_PARAMETERS WHERE PARAMETER = 'NLS_CHARACTERSET' NLS_CHARACTERSET AL32UTF8 Может ли такое поведение быть привязано к версии/выпуску Oracle?