为什么 Oracle 在通过 ODBC 绑定 SQL_C_WCHAR 文本时需要 TO_NCHAR

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/337769/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-18 17:31:59  来源:igfitidea点击:

Why does Oracle require TO_NCHAR when binding SQL_C_WCHAR text via ODBC

oracleodbcoracle10g

提问by brofield

I use the following statement prepared and bound in ODBC:

我在 ODBC 中使用了以下准备和绑定的语句:

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

Executed in an ODBC 3.0 connection to an Oracle 10g database in AL32UTF8 charset, even after binding to a wchar_t string using SQLBindParameter(SQL_C_WCHAR), it still gives the error ORA-12704: character set mismatch.

在到 AL32UTF8 字符集的 Oracle 10g 数据库的 ODBC 3.0 连接中执行,即使在使用 绑定到 wchar_t 字符串后SQLBindParameter(SQL_C_WCHAR),它仍然给出错误 ORA-12704:字符集不匹配。

Why? I'm binding as wchar. Shouldn't a wchar be considered an NCHAR?

为什么?我绑定为 wchar。wchar 不应该被视为 NCHAR 吗?

If I change the parameter to wrap it with TO_NCHAR()then the query works without error. However since these queries are used for multiple database backends, I don't want to add TO_NCHAR just on Oracle text bindings. Is there something that I am missing? Another way to solve this without the TO_NCHAR hammer?

如果我更改参数以将其包装起来,TO_NCHAR()那么查询将正常工作。但是,由于这些查询用于多个数据库后端,我不想只在 Oracle 文本绑定上添加 TO_NCHAR。有什么我想念的吗?在没有 TO_NCHAR 锤子的情况下解决此问题的另一种方法?

I haven't been able to find anything relevant via searches or in the manuals.

我无法通过搜索或手册找到任何相关内容。

More details...

更多细节...

-- error

- 错误

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)

This version without TO_NCHAR works fine in SQL Server and PostgreSQL (via ODBC) and SQLite (direct). However in Oracle it returns "ORA-12704: character set mismatch".

这个没有 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

If I do use TO_NCHAR, it's okay (but won't work in SQL Server, Postgres, SQLite, etc).

如果我确实使用了 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

采纳答案by Justin Cave

If the Oracle database character set is AL32UTF8, why are the columns defined as NVARCHAR2? That means that you want those columns encoded using the national character set (normally AL16UTF16, but that may be different on your database). Unless you are primarily storing Asian language data (or other data that requires 3 bytes of storage in AL32UTF8), it is relatively uncommon to create NVARCHAR2 columns in an Oracle database when the database character set supports Unicode.

如果Oracle 数据库字符集是AL32UTF8,为什么列定义为NVARCHAR2?这意味着您希望使用国家字符集(通常为 AL16UTF16,但在您的数据库上可能有所不同)对这些列进行编码。除非您主要存储亚洲语言数据(或其他需要在 AL32UTF8 中存储 3 个字节的数据),否则当数据库字符集支持 Unicode 时,在 Oracle 数据库中创建 NVARCHAR2 列相对少见。

In general, you are far better served sticking with the database character set (CHAR and VARCHAR2 columns) rather than trying to work with the national character set (NCHAR and NVARCHAR2 columns) because there are far fewer hoops that need to be jumped through on the development/ configuration side of things. Since you aren't increasing the set of characters you can encode by choosing NVARCHAR2 data types, I'll wager that you'd be happier with VARCHAR2 data types.

一般来说,您最好坚持使用数据库字符集(CHAR 和 VARCHAR2 列)而不是尝试使用国家字符集(NCHAR 和 NVARCHAR2 列),因为在开发/配置方面的事情。由于您不会通过选择 NVARCHAR2 数据类型来增加可以编码的字符集,我敢打赌您会更喜欢 VARCHAR2 数据类型。

回答by brofield

Thanks Justin.

谢谢贾斯汀。

I can't say that I understand exactly how to choose between VARCHAR2 and NVARCHAR2 still. I had tried using VARCHAR2 for my date (which does include a lot of different languages, both European and Asian) and it didn't work that time.

我不能说我仍然完全理解如何在 VARCHAR2 和 NVARCHAR2 之间进行选择。我曾尝试将 VARCHAR2 用于我的约会(其中确实包括许多不同的语言,包括欧洲和亚洲),但当时没有用。

I have had another bit of playing around again though and I found that using Justin's suggestion works in this combination:

不过,我又玩了一会,我发现在这种组合中使用贾斯汀的建议是有效的:

  • AL32UTF8 database charset
  • VARCHAR2 column types
  • set NLS_LANG=.UTF8 before starting sqlplus.exe
  • data files using UTF-8 (i.e. the files with all the INSERT statements)
  • inserting and extracting strings from the database using SQL_C_WCHAR
  • AL32UTF8 数据库字符集
  • VARCHAR2 列类型
  • 在启动 sqlplus.exe 之前设置 NLS_LANG=.UTF8
  • 使用 UTF-8 的数据文件(即包含所有 INSERT 语句的文件)
  • 使用 SQL_C_WCHAR 从数据库中插入和提取字符串

I still don't find Oracle as fun to play with as (for instance) PostgreSQL though... :-)

我仍然不觉得 Oracle 像(例如)PostgreSQL 那样有趣...... :-)