Direct Oracle Access切断了unicode(宽)字符串的结尾

时间:2013-08-27 21:53:39

标签: string oracle delphi unicode delphi-xe2

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

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

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

2 个答案:

答案 0 :(得分:2)

您的数据库字符集是什么(来自v$nls_parameters)?您是否在创建表之前为数据库或会话设置了非默认NLS_LENGTH_SEMANTICS

假设您的数据库字符集支持Unicode(即它是AL32UTF8)并且您使用默认的NLS_LENGTH_SEMANTICSVARCHAR2(10)最多可分配10个字节的存储空间。由于AL32UTF8是可变宽度字符集,因此一个字符将需要1到3个字节的存储空间。如果您以字节为单位声明列,则表示您的列将能够存储3到10个字符,具体取决于您存储的特定字符。这似乎与您所看到的行为一致。

最好的方法通常是使用字符长度语义声明列。如果你宣布你的表

CREATE TABLE test1 (fsString10 VARCHAR2(10 CHAR))

无论需要多少字节数,您最多允许10个字符。这似乎是你想要的行为。

您可以在发布DDL之前设置NLS_LENGTH_SEMANTICS来更改会话级别的默认长度语义。如果你运行

ALTER SESSION SET nls_length_semantics = CHAR;

CREATE TABLE test1 (fsString10 VARCHAR2(10));

您的列也将使用字符长度语义(最多可分配10个字符,而不是最多10个字节)。

也可以在系统级别设置nls_length_semantics。但是,Oracle全球化人员通常不鼓励这样做,因为各种脚本(Oracle和第三方)要么未在该配置中进行测试,要么已知存在问题。

如果您不想使用字符长度语义,您还可以将列的大小增加三倍(以字节为单位)。这将允许您的应用程序存储所有三个字符串。但这意味着如果它只包含英文字符,你可以存储一个30个字符的字符串。

答案 1 :(得分:1)

我发现了另一个没有任何源代码更改的解决方案。

只需从

更改oracle客户端NLS_LANG参数即可
AMERICAN_AMERICA.UTF8 

AMERICAN_AMERICA.CL8MSWIN1251 

并在mainform create event上显式设置params:

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

实际上,如果应用程序显式使用TWideStringField,则需要转换为TStringField。