Oracle 11g中的char類型使用

來源:互聯網
上載者:User

Oracle 11g中的char類型使用

在Oracle資料表和程式類型中,字元類型可能是使用最多的一種了。從曆史上看,Oracle首先推出了固定長度char類型欄位,之後才推出了變長度類型varchar2。目前,主流Oracle應用開發都已經普遍接受了varchar2作為資料表欄位類型表示。如果存在超長字元,都考慮使用CLOB這類大對象進行儲存。

但是,我們在實際領域中,還是會在各種遺留系統中發現很多char類型欄位。而且連帶這些遺留系統的下遊系統中,char類型也會經常出現。在這樣的背景下,開發營運人員其實還是有很多的接觸char的機會的。

Char最大的特點就是固定長度儲存,例如定義長度char(10),儲存的字串為’kkk’三位長度。在儲存的時候,Oracle會自動將其尾部補齊空格。這樣就確保了每個儲存在char(10)的欄位都是10位長度。根據筆者的猜測,char的策略是在Oracle對於儲存空間管理能力較弱的背景下提出的一種折中策略。隨後,varchar2的出現逐步將這種變通策略加以替代。

在一些時候,筆者還是會遇到一些朋友的問題:明明檢索全表時候看到字串取值是xxx,但是用SQL加在後面where條件的時候,就沒有檢索結果,彷彿“見鬼一樣”。究其根源,常常是char在裡面“搗亂”,where條件後面加上若干個空格或者使用rpad命令進行補全。

本篇主要介紹幾個在11g裡面常見的使用情境。注意:隨著版本的升級,Oracle對於char的支援其實也在不斷升級(默默地),儘可能讓其靠近varchar2的使用效果。所以,本文中的實驗內容,在其他版本下可能會有不同的測試結果,請注意。

1、環境說明

本文使用Oracle 11gR2進行測試,具體版本為11.2.0.4。

SQL> select * from v$version;

BANNER

--------------------------------------------------------------------------------

Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 - 64bit Production

PL/SQL Release 11.2.0.4.0 - Production

CORE    11.2.0.4.0    Production

TNS for Linux: Version 11.2.0.4.0 - Production

NLSRTL Version 11.2.0.4.0 – Production

建立實驗資料表T。

SQL> create table t (chr_a char(100), vchar_a varchar2(100));

Table created

SQL> desc t;

Name    Type          Nullable Default Comments 

------- ------------- -------- ------- -------- 

CHR_A  CHAR(100)    Y                         

VCHAR_A VARCHAR2(100) Y                         

SQL> insert into t select owner, owner from dba_objects;

119498 rows inserted

SQL> commit;

Commit complete

SQL> exec dbms_stats.gather_table_stats(user,'T',cascade => true);

PL/SQL procedure successfully completed

資料取值’SCOTT’的記錄數目為6。

SQL> select count(*) from t where vchar_a='SCOTT';

  COUNT(*)

----------

        6

2、常量where條件測試

首先我們測試一下單獨寫常量條件的情況。

SQL> select count(*) from t where chr_a='SCOTT';

  COUNT(*)

----------

        6

SQL> select length(chr_a) from t where chr_a='SCOTT';

LENGTH(CHR_A)

-------------

          100

          100

          100

          100

          100

          100

6 rows selected

上面兩個SQL語句表明:在使用常量作為條件的where語句時候,即使欄位類型是char,系統會自動進行右側空格的“補齊”動作,讓其滿足char條件,檢索出正確的結果。藉助length函數,我們也可以確定長度為100。

筆者一直想瞭解這個“偷天換日”的過程,從執行計畫角度也不能看出實際的情況。

SQL> explain plan for select count(*) from t where chr_a='SCOTT';

Explained

SQL> select * from table(dbms_xplan.display(format => 'advanced'));

PLAN_TABLE_OUTPUT

--------------------------------------------------------------------------------

Plan hash value: 2966233522

---------------------------------------------------------------------------

| Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time    |

---------------------------------------------------------------------------

|  0 | SELECT STATEMENT  |      |    1 |  101 |  510  (1)| 00:00:07 |

|  1 |  SORT AGGREGATE    |      |    1 |  101 |            |          |

|*  2 |  TABLE ACCESS FULL| T    |    11 |  1111 |  510  (1)| 00:00:07 |

---------------------------------------------------------------------------

Query Block Name / Object Alias (identified by operation id):

-------------------------------------------------------------

  1 - SEL$1

  2 - SEL$1 / T@SEL$1

Outline Data

-------------

  /*+

PLAN_TABLE_OUTPUT

--------------------------------------------------------------------------------

      BEGIN_OUTLINE_DATA

      FULL(@"SEL$1" "T"@"SEL$1")

      OUTLINE_LEAF(@"SEL$1")

      ALL_ROWS

      DB_VERSION('11.2.0.4')

      OPTIMIZER_FEATURES_ENABLE('11.2.0.4')

      IGNORE_OPTIM_EMBEDDED_HINTS

      END_OUTLINE_DATA

  */

Predicate Information (identified by operation id):

---------------------------------------------------

  2 - filter("CHR_A"='SCOTT')

Column Projection Information (identified by operation id):

-----------------------------------------------------------

  1 - (#keys=0) COUNT(*)[22]

39 rows selected

從執行計畫上,我們看不出什麼過多的端倪。filter條件中也沒有對chr_a取值進行格外的處理。這個過程其實是針對char類型進行的特殊處理,如果我們在where條件後面人為加上空格,會如何呢?

SQL> select count(*) from t where chr_a='SCOTT ';

  COUNT(*)

----------

        6

SQL> select count(*) from t where chr_a='SCOTT    ';

  COUNT(*)

----------

        6

同樣可以正確找到結果。對應執行計畫為:

SQL> explain plan for select count(*) from t where chr_a='SCOTT    ';

Explained

SQL> select * from table(dbms_xplan.display(format => 'advanced'));

PLAN_TABLE_OUTPUT

--------------------------------------------------------------------------------

Plan hash value: 2966233522

---------------------------------------------------------------------------

| Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time    |

---------------------------------------------------------------------------

|  0 | SELECT STATEMENT  |      |    1 |  101 |  510  (1)| 00:00:07 |

|  1 |  SORT AGGREGATE    |      |    1 |  101 |            |          |

|*  2 |  TABLE ACCESS FULL| T    |    11 |  1111 |  510  (1)| 00:00:07 |

---------------------------------------------------------------------------

Query Block Name / Object Alias (identified by operation id):

-------------------------------------------------------------

  1 - SEL$1

  2 - SEL$1 / T@SEL$1

Outline Data

-------------

  /*+

PLAN_TABLE_OUTPUT

--------------------------------------------------------------------------------

      BEGIN_OUTLINE_DATA

      FULL(@"SEL$1" "T"@"SEL$1")

      OUTLINE_LEAF(@"SEL$1")

      ALL_ROWS

      DB_VERSION('11.2.0.4')

      OPTIMIZER_FEATURES_ENABLE('11.2.0.4')

      IGNORE_OPTIM_EMBEDDED_HINTS

      END_OUTLINE_DATA

  */

Predicate Information (identified by operation id):

---------------------------------------------------

  2 - filter("CHR_A"='SCOTT    ')

Column Projection Information (identified by operation id):

-----------------------------------------------------------

  1 - (#keys=0) COUNT(*)[22]

39 rows selected

執行計畫上同樣看不到什麼變化。

上面的實驗,告訴我們char常量使用規則:如果在where條件後面使用常量取值,Oracle會根據欄位類型和常量情況進行自動補齊空格動作。常量後面人為添加的空格也會作為自動空格加以處理。

下面討論如果是rpad函數在常量上,效果如何呢?

3、函數操作效果

在一些比較老的版本Oracle版本裡面,一些開發人員為了避免char空格影響,廣泛使用了rpad函數,自動的添加後面的空格。

SQL> select count(*) from t where chr_a=rpad('SCOTT',100,' ');

  COUNT(*)

----------

        6

正常是可以找到結果的。執行計畫上,filter部分顯然是先計算出函數取值,之後再進行處理。

SQL> explain plan for select count(*) from t where chr_a=rpad('SCOTT',100,' ');

Explained

SQL> select * from table(dbms_xplan.display(format => 'advanced'));

PLAN_TABLE_OUTPUT

--------------------------------------------------------------------------------

Plan hash value: 2966233522

---------------------------------------------------------------------------

(篇幅原因,有省略……)

Predicate Information (identified by operation id):

---------------------------------------------------

  2 - filter("CHR_A"='SCOTT

                                                                ')

Column Projection Information (identified by operation id):

-----------------------------------------------------------

  1 - (#keys=0) COUNT(*)[22]

40 rows selected

但是,如果設定函數補齊的長度不是“一步到位”,而是部分長度的。Oracle是不能夠找到對應結果的。

SQL> select count(*) from t where chr_a=rpad('SCOTT',10,' ');

  COUNT(*)

----------

        0

SQL> select count(*) from t where chr_a=rpad('SCOTT',50,' ');

  COUNT(*)

----------

        0

使用常量時候的自動補齊動作不再生效了!這就表明:最佳化器在處理常量時候,有一些特殊的規則邏輯,這部分在應用函數之後就不再使用了。另外一種可能性就是針對等號兩端取值進行類型匹配的時候,常量和函數量的取值方式是有差別的。rpad返回的雖然是一個字元類型,但是變長度還是固定長度是不容易確定的。如果Oracle將常量認可為char,rpad結果認可為varchar2,那麼結果不一樣是可以理解的。

4、綁定變數處理

SQL最佳化器是目前Oracle中非常複雜的組件之一。其中很多步驟和邏輯都需要我們不斷地進行測試和合理猜測得到的。綁定變數是我們在實際開發過程中常常使用的技術之一。下面我們測試一下在PL/SQL程式碼片段中綁定變數的使用。

SQL> declare

  2    a char(100);

  3    coun number;

  4  begin

  5    a := 'SCOTT';

  6    execute immediate 'select count(*) from t where chr_a=:1'

  7                into coun

  8                using a;

  9    dbms_output.put_line('Result Is : '||to_char(coun));

 10  end;

 11  /

Result Is : 6

PL/SQL procedure successfully completed

綁定變數使用上,關鍵的一個問題就是綁定變數定義的類型。在上面的代碼中,如果使用了char類型的變數,雖然定義取值的時候後面沒有空格,Oracle還是事先了常量變數中的“自動補齊動作”。

如果和常量時候一樣,賦值是有空格結尾的,那麼會如何呢?

SQL> set serveroutput on size 10000;

SQL> declare

  2    a char(100);

  3    coun number;

  4  begin

  5      a := 'SCOTT  ';

  6      execute immediate 'select count(*) from t where chr_a=:1'

  7                    into coun

  8                    using a;

  9      dbms_output.put_line('Result Is : '||to_char(coun));

 10  end;

 11  /

Result Is : 6

PL/SQL procedure successfully completed

效果相同。那麼,可以得到結論:如果使用char類型的綁定變數,效果和常量是一樣的。那麼,如果使用varchar2類型綁定變數,效果如何呢?

SQL> declare

  2    a varchar(100);

  3    coun number;

  4  begin

  5    a := 'SCOTT';

  6    execute immediate 'select count(*) from t where chr_a=:1'

  7                into coun

  8                using a;

  9    dbms_output.put_line('Result Is : '||to_char(coun));

 10  end;

 11  /

Result Is : 0

PL/SQL procedure successfully completed

SQL> set serveroutput on size 10000;

SQL> declare

  2    a varchar(100);

  3    coun number;

  4  begin

  5      a := 'SCOTT  ';

  6      execute immediate 'select count(*) from t where chr_a=:1'

  7                    into coun

  8                    using a;

  9      dbms_output.put_line('Result Is : '||to_char(coun));

 10  end;

 11  /

Result Is : 0

PL/SQL procedure successfully completed

如果綁定變數明確是varchar2,自動補齊效果是不發生的!和之前函數的問題是沒有關係的。如果我們在char類型中使用rpad函數進行賦值,看結果應該是可以進行自動補齊的。

SQL> set serveroutput on size 10000;

SQL> declare

  2    a char(100);

  3    coun number;

  4  begin

  5      a := rpad('SCOTT',10,' ');

  6      execute immediate 'select count(*) from t where chr_a=:1'

  7                    into coun

  8                    using a;

  9      dbms_output.put_line('Result Is : '||to_char(coun));

 10  end;

 11  /

Result Is : 6

PL/SQL procedure successfully completed

結果和預計相同。

5、結論

綜合上述的實驗分析,我們可以得到如下結論:

  • 對char類型,最佳化器方面會有一定的最佳化動作,主要在於末尾空格的自動補齊動作;
  • 但是,自動補齊動作出現的條件是類型的匹配,就是進行比較時候變數類型一定是char類型;
  • 如果是varchar2類型,自動補齊動作是不會出現的;
  • 很多字串相關函數的傳回值,都是varchar2類型。

相關文章

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.