OCCI處理CHAR類型字串變數的不同,occichar
問題背景:
一箇舊應用,原先應用是用proc寫的,9i的庫,現在應用需要改為使用OCCI,其中有一段查詢邏輯:select ... where upper(state)=upper(:1)。
(此處請不要糾結於where條件中state欄位使用了upper函數,因為此表資料量很小,且其曆史比較悠久,未建索引。)
對應表中定義的state欄位類型是char(3),但此處查詢條件變數的值可能是兩位,例如'NY'。
現象:
1. 使用sqlplus執行select ... where upper(state)=upper(:1)可以正常顯示。
2. 使用sql developer執行select ... where upper(state)=upper(:1)可以正常顯示。
3. 使用proc執行,可以正常顯示。
4. 使用OCCI方式,執行,顯示為空白。
解決:
對於使用OCCI的方式,將其改寫為:
1. select ... where trim(upper(state)) = trim(upper(:1));
2. select ... where upper(state) = upper(rpad(:1, 3, ' '));
原理推斷:
1. 首先char和varchar2類型的最大區別,就是char是定長類型,varchar2是不定長類型,網上包括官方文檔有很多介紹了,用例子簡單講,就是:
create table test(
a char(25),
b varchar2(25)
);
insert into test values('a', b');
a欄位儲存的是“a+24個空格”,b欄位儲存的就是“b”。
可以從select a, length(a), b, length(b) from test;進一步驗證。
即char會佔用最大的儲存空間,varchar2則只會儲存實際佔用的空間。
2. 從http://www.itpub.net/thread-1014651-1-1.html這篇文章可以看出,和這個問題相同。推斷是OCCI的bug導致。
雖然翻了OCCI的文檔,並未找到對這個問題的解釋,但從Oracle官方文檔對填補空格比較字串的語義說明,可以看出一些端倪:
Blank-Padded Comparison Semantics
If the two values have different lengths, then Oracle first adds blanks to the end of the shorter one so their lengths are equal. Oracle then compares the values character by character up to the first character that differs. The value with the greater character in the first differing position is considered greater.
If two values have no differing characters, then they are considered equal. This rule means that two values are equal if they differ only in the number of trailing blanks. Oracle uses blank-padded comparison semantics only when both values in the comparison are either expressions of datatype CHAR, NCHAR, text literals, or values returned by the USER function.
Nonpadded Comparison Semantics
Oracle compares two values character by character up to the first character that differs. The value with the greater character in that position is considered greater. If two values of different length are identical up to the end of the shorter one, then the longer value is considered greater. If two values of equal length have no differing characters, then the values are considered equal. Oracle uses nonpadded comparison semantics whenever one or both values in the comparison have the datatype VARCHAR2 or NVARCHAR2.
即對於CHAR、NCHAR類型的字串比較,Oracle首先會自動補齊空格,然後再一個字元一個字元地比較,不會因為空白格數不同認為兩者不同,且這個過程應該不是簡單的trim()操作,因為如果欄位有索引仍會使用。
對於VARCHAR2、NVARCHAR2類型的字串比較,由於其不會自動儲存空格,如果有空格,則也是作為有意義的儲存,因此不存在上述問題。
綜上所述,對於CHAR類型,不應該因為補空格位元的問題,作為比較的依據,除非使用的where a = trim('a'),人為對值進行處理,因此有理由懷疑OCCI對CHAR類型字串的比較,至少和其他終端查詢的邏輯不同,至於是不是bug,需要看看有沒有官方的解釋了。