Почему Oracle требует TO_NCHAR при привязке текста SQL_C_WCHAR через ODBC

Я использую следующий оператор, подготовленный и связанный в ODBC:

SELECT (CASE profile WHEN ? THEN 1 ELSE 2 END) AS profile_order 
FROM engine_properties;

Выполняется в соединении ODBC 3.0 с базой данных Oracle 10g в кодировке AL32UTF8, даже после привязки к строке wchar_t с использованием SQLBindParameter(SQL_C_WCHAR) все равно выдает ошибку ORA-12704: несоответствие набора символов.

Почему? Я связываюсь как wchar. Разве wchar не должен считаться NCHAR?

Если я изменю параметр, чтобы обернуть его TO_NCHAR(), тогда запрос будет работать без ошибок. Однако, поскольку эти запросы используются для нескольких серверных частей базы данных, я не хочу добавлять TO_NCHAR только к текстовым привязкам Oracle. Есть ли что-то, чего мне не хватает? Другой способ решить эту проблему без молотка TO_NCHAR?

Ни в поиске, ни в мануалах ничего подходящего не нашел.

Подробнее...

-- ошибка

SELECT (CASE profile WHEN          '_default'  THEN 1 ELSE 2 END) AS profile_order
FROM engine_properties;

-- ok

SELECT (CASE profile WHEN TO_NCHAR('_default') THEN 1 ELSE 2 END) AS profile_order
FROM engine_properties;
SQL> describe engine_properties;
 Name                                      Null?    Type
 ----------------------------------------- -------- ----------------------------
 EID                                       NOT NULL NVARCHAR2(22)
 LID                                       NOT NULL NUMBER(11)
 PROFILE                                   NOT NULL NVARCHAR2(32)
 PKEY                                      NOT NULL NVARCHAR2(50)
 VALUE                                     NOT NULL NVARCHAR2(64)
 READONLY                                  NOT NULL NUMBER(5)

Эта версия без TO_NCHAR отлично работает в SQL Server и PostgreSQL (через ODBC) и SQLite (прямо). Однако в Oracle он возвращает «ORA-12704: несоответствие набора символов».

SQLPrepare(SELECT (CASE profile WHEN ? THEN 1 ELSE 2 END) AS profile_order 
    FROM engine_properties;) = SQL_SUCCESS
SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_WCHAR, 
    SQL_VARCHAR, 32, 0, "_default", 18, 16) = SQL_SUCCESS
SQLExecute() = SQL_ERROR
SQLGetDiagRec(1) = SQL_SUCCESS
[SQLSTATE: HY000, NATIVE: 12704, MESSAGE: [Oracle][ODBC]
    [Ora]ORA-12704: character set mismatch]
SQLGetDiagRec(2) = SQL_NO_DATA

Если я использую TO_NCHAR, все в порядке (но не будет работать в SQL Server, Postgres, SQLite и т. д.).

SQLPrepare(SELECT (CASE profile WHEN TO_NCHAR(?) THEN 1 ELSE 2 END) AS profile_order
    FROM engine_properties;) = SQL_SUCCESS
SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_WCHAR, 
    SQL_VARCHAR, 32, 0, "_default", 18, 16) = SQL_SUCCESS
SQLExecute() = SQL_SUCCESS
SQLNumResultCols() = SQL_SUCCESS (count = 1)
SQLFetch() = SQL_SUCCESS

person brofield    schedule 03.12.2008    source источник


Ответы (2)


Если набор символов базы данных Oracle — AL32UTF8, почему столбцы определены как NVARCHAR2? Это означает, что вы хотите, чтобы эти столбцы были закодированы с использованием национального набора символов (обычно AL16UTF16, но в вашей базе данных он может отличаться). Если вы в основном не храните данные на азиатском языке (или другие данные, для которых требуется 3 байта памяти в AL32UTF8), относительно редко создаются столбцы NVARCHAR2 в базе данных Oracle, когда набор символов базы данных поддерживает Unicode.

В общем, гораздо лучше работать с набором символов базы данных (столбцы CHAR и VARCHAR2), чем пытаться работать с национальным набором символов (столбцы NCHAR и NVARCHAR2), потому что на разработка/конфигурация. Поскольку вы не увеличиваете набор символов, которые вы можете кодировать, выбирая типы данных NVARCHAR2, держу пари, что вам больше понравятся типы данных VARCHAR2.

person Justin Cave    schedule 03.12.2008

Спасибо, Джастин.

Не могу сказать, что я до сих пор точно понимаю, как выбирать между VARCHAR2 и NVARCHAR2. Я пытался использовать VARCHAR2 для своего свидания (который включает в себя множество разных языков, как европейских, так и азиатских), но в тот раз это не сработало.

Однако я снова немного поиграл и обнаружил, что использование предложения Джастина работает в этой комбинации:

  • Кодировка базы данных AL32UTF8
  • Типы столбцов VARCHAR2
  • установите NLS_LANG=.UTF8 перед запуском sqlplus.exe
  • файлы данных с использованием UTF-8 (т. е. файлы со всеми операторами INSERT)
  • вставка и извлечение строк из базы данных с помощью SQL_C_WCHAR

Я до сих пор не нахожу Oracle таким же интересным, как (например) PostgreSQL... :-)

person brofield    schedule 03.12.2008