1

我在 Delphi XE2 中遇到 unicode(宽)字符串字段的问题,它没有返回字符串的最后一部分,并且任何数据库控制组件也没有完全显示整个字符串。

您可以在下面看到简单的测试。

with TOracleDataSet.Create(self) do
try
  Session := OraSession;
  SQL.Text := 'CREATE TABLE test1 (fsString10 VARCHAR2(10))';
  ExecSQL;
  SQL.Text := 'INSERT INTO test1 (fsString10) VALUES ('''1234567890''')';
  ExecSQL;
  SQL.Text := 'INSERT INTO test1 (fsString10) VALUES ('''й234567890''')';
  ExecSQL;
  SQL.Text := 'INSERT INTO test1 (fsString10) VALUES ('''йцукенгшщз''')';
  ExecSQL;
  SQL.Text := 'SELECT fsString10 FROM test1';
  Open;
  while not Eof do
    ShowMessage(FieldByName('fsString10').AsString); 
    // '1234567890' turned into '1234567890' 
    // 'й234567890' turned into 'й23456789'
    // 'йцукенгшщз' turned into 'йцуке'
  SQL.Text := 'DROP TABLE test1';
  ExecSQL;
finally
  Free;
end;

如您所见,未正确加载 unicode 字符串。

另一方面,“Direct Oracle Access 4.1.3”组件在半个后记录后没有在字符串中保存字符。无论如何,它只保存前半串。

有没有办法解决它?

  • BytesPerCharacter 设置为 bcAutoDetect。
  • 我尝试切换 NoUnicodeSupport 和所有其他选项,但没有成功。

有没有人对如何解决这个问题有任何想法?

PS:由于多种原因,我无法更改生产数据库架构。

DB Server NLS_DATABASE_PARAMETERS NLS_CHARACTERSET = CL8MSWIN1251
DB Client NLS_LANG=AMERICAN_AMERICA.UTF8
DB Client NLS_LANG=RUSSIAN_CIS.CL8MSWIN1251 <- the same thing
4

2 回答 2

2

What is your database character set (from v$nls_parameters)? Have you set a non-default NLS_LENGTH_SEMANTICS for the database or in the session before you created the table?

Assuming that your database character set supports Unicode (i.e. it is AL32UTF8) and that you are using the default NLS_LENGTH_SEMANTICS, a VARCHAR2(10) allocates up to 10 bytes of storage. Since AL32UTF8 is a variable-width character set, one character will require between 1 and 3 bytes of storage. If you are declaring your column in terms of bytes, that means that your column will be able to store between 3 and 10 characters depending on the specific characters you store. That appears to be consistent with the behavior you are seeing.

The best approach is generally to declare your columns using character length semantics. If you declare your table

CREATE TABLE test1 (fsString10 VARCHAR2(10 CHAR))

you'll allow up to 10 characters regardless of the number of bytes that would require. That appears to be the behavior that you want.

You can change the default length semantics at a session level by setting NLS_LENGTH_SEMANTICS before you issue your DDL. If you run

ALTER SESSION SET nls_length_semantics = CHAR;

CREATE TABLE test1 (fsString10 VARCHAR2(10));

your column will also use character length semantics (allocating space for up to 10 characters rather than up to 10 bytes).

It is possible to set nls_length_semantics at the system level as well. However, the Oracle globalization folks generally discourage that because various scripts (Oracle and third-party) are either not tested in that configuration or are known to have problems.

If you don't want to use character length semantics, you can also triple the size of the column in bytes. That would allow your application to store all three strings. But that would mean that you could store a 30 character string if it was only comprised of English characters.

于 2013-08-27T22:17:55.780 回答
1

我找到了另一种没有任何源代码更改的解决方案。

只需将 oracle 客户端 NLS_LANG 参数从

AMERICAN_AMERICA.UTF8 

AMERICAN_AMERICA.CL8MSWIN1251 

并在主窗体创建事件上明确设置参数:

Windows.SetEnvironmentVariable(PChar('NLS_LANG'),
                               PChar('AMERICAN_AMERICA.CL8MSWIN1251'));
Oracle.NoUnicodeSupport := True;

实际上,如果应用程序显式使用 TWideStringField,它需要变成 TStringField。

于 2013-08-28T17:18:53.013 回答