2013-08-27 27 views
1

我在Delphi中遇到unicode(寬)字符串字段的問題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字符串沒有正確加載。

另一方面,「直接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 
+0

在您的調試中,您是否確定這是插入還是選擇的問題? –

+0

你的數據庫配置了哪個字符集?你是否用'NVARCHAR2'數據類型試過你的例子? – TLama

+0

@Rob,問題實際上是字符串緩衝區大小。 –

回答

1

我發現了另一個解決方案,沒有任何源代碼更改。

簡單的Oracle客戶端NLS_LANG參數從

AMERICAN_AMERICA.UTF8 

改變

AMERICAN_AMERICA.CL8MSWIN1251 

,並設置明確的MainForm的PARAMS創建事件:

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

其實,如果應用程序是明確使用TWideStringField,它需要變成TStringField。

2

什麼是您的數據庫字符集(從v$nls_parameters)?您是否在創建表之前爲數據庫或會話設置了非默認的NLS_LENGTH_SEMANTICS

假設您的數據庫字符集支持Unicode(即它是AL32UTF8)並且您使用的是默認NLS_LENGTH_SEMANTICS,則VARCHAR2(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個字符的字符串。

+0

謝謝你的解決方案。你說得對,nls_length_semantics是等於字節。但是由於種種原因,我無法修改數據庫模式中的所有字段,對我而言,這不是更好的解決方案。 –

+0

我已經根據您的方法對已更改的創建表架構進行了測試,但結果與上面顯示的相同。 –