.Net程序員學用Oracle系列(5):三大數據類型


選擇一個正確的數據類型,這看上去再容易不過了,但我們屢屢見到選擇不當的情況。要選擇什么類型來存儲你的數據,這是一個最基本的決定,而且這個決定會在以后的數年間影響着你的應用和數據。因此選擇適當的數據類型至關重要,而且很難事后再做改變,也就是說,一旦選擇某些類型實現了應用,在相當長的時間內就只能“忍耐”,因為你選擇的類型可能不太合適。

上面這段話來自 Thomas Kyte(Oracle公司核心技術集團副總裁)的——《Oracle Database 9i/10g/11g編程藝術》。我對這段話描述的情況感同身受,因此引用了過來。本文部分內容源自該書第 12 章第一節——《Oracle 數據類型概述》,有刪改。

1、字符類型

1.1、字符集 & NLS

字符集(character set)是各個字符的一種二進制表示(用位和字節表示)。

NLS 代表國家語言支持(National Language Support)。它控制着數據庫的兩個方面:

  • 文本數據持久存儲在磁盤上時如何編碼
  • 透明的將數據從一個字符集轉換到另一個字符集

在創建新的數據庫實例時,如果選擇了與原數據庫不兼容的字符集,會導致原數據庫中的字符型字段數據導入新數據庫中之后變成亂碼。可以通過NLS_DATABASE_PARAMETERS視圖或SYS.PROPS$表來查詢當前數據庫的字符集,如:

SELECT t.value res FROM NLS_DATABASE_PARAMETERS t WHERE t.parameter='NLS_CHARACTERSET'; -- res: ZHS16GBK

NLS_DATABASE_PARAMETERS視圖中包含了數據庫中所有的 NLS 參數,SYS.PROPS$表中則包含了更多的參數,而且還有對各個參數用途的說明,如:

SELECT t.name 參數名,t.value$ 參數值,t.comment$ 參數說明 FROM SYS.PROPS$ t WHERE t.name='NLS_CHARACTERSET';

查詢結果:

參數名                   參數值             參數說明
------------------------ ------------------ ---------------------------
NLS_CHARACTERSET         ZHS16GBK           Character set

1.2、常見的兩種字符串

  • 定長字符串(CHAR(x)&NCHAR(x)):CHAR 和 NCHAR 實際上只是偽裝的 VARCHAR2/NVARCHAR2,區別是 CHAR 和 NCHAR 總是會用空格填充來達到一個固定長度。所以不論是表段還是索引段,CHAR 和 NCHAR 都會占用最大的存儲空間。更嚴重的是 CHAR 和 NCHAR 還會帶來“混亂”。幾乎所有應用都不適合用 CHAR 和 NCHAR,因此如果你要決定一個字段的類型,建議忽略 CHAR 和 NCHAR 的存在。
  • 變長字符串(VARCHAR2(x)&NVARCHAR2(x)):VARCHAR2 是 VARCHAR 的同義詞,它們的制造商分別是 Oracle 和 ANSI。一開始接觸 Oracle 的時候我就很好奇為啥大家普遍用 VARCHAR2,而不是 VARCHAR,后來才知道原來 VARCHAR2 這個怪異的類型是 Oracle 私有的,而且 Oracle 只確保它的兼容性,不保證標准的 VARCHAR 的兼容性,我猜這可能是大家普遍選用 VARCHAR2 的原因吧。VARCHAR2 有兩個特殊的地方在開發中需要格外的注意,以防出現意外:
    • VARCHAR2 把空字符串與 NULL 做等同處理,而 VARCHAR 仍按照空字符串處理
    • VARCHAR2 中一個字符要用幾個字節來存儲,取決於所選用的字符集

1.3、NCHAR & NVARCHAR2

如果系統中需要管理和存儲多種字符集,就可以使用這兩個字符串類型。NCHAR/NVARCHAR2 與 CHAR/VARCHAR2 的不同主要有兩點:

  • 文本采用數據庫的國家字符集來存儲和管理,而不是默認字符集。
  • 長度總是字符數,而 CHAR/VARCHAR2 可能會指定是字節還是字符。

如果一個串中每個字符可能存儲不同數目的字節,與這個串相比,定寬字符串上的串操作效率更高。

2、數值類型

2.1、固有數值類型

  • NUMBER(p,s):Oracle NUMBER 類型能以多達 38 位的精度存儲數值。其底層數據格式類似於一種封包小數表示,長度為 0~22 字節。NUMBER(p,s) 中的 p 和 s 都是可選的,p(precision)用於指定精度,或總位數。默認情況下,精度為 38 位,取值范圍是 1~38 之間。也可以用字符 * 表示 38。s(scale)用於指定小數位數,或小數點右邊的位數。小數位數的合法值為 -84~127,其默認值取決於是否指定了精度。如果指定了精度,小數位數默認為 0,否則默認有最大的取值區間。下面給出兩個案例來進一步說明:

    • number(p) 與 number(p,0) 等同,都只能存儲精度小於或等於 p 的的整數。
    • number(p,s) 只能存儲精度小於或等於 p,刻度小於或等於 s 的小數。比如精度是2,保存時給的是整數 5,保存之后會變成 5.00。
  • 新數值類型:Oracle Database 10g 引入了兩種新的數值類型來存儲數據,分別是 BINARY_FLOAT 和 BINARY_DOUBLE。它們就是許多程序員過去常用的 IEEE 標准浮點數。浮點數用於近似數值,它們沒有 Oracle NUMBER 類型那么精確。浮點數常用在科學計算中,由於允許在硬件(CPU、芯片)上執行運算,而不是 Oracle 子例程中運算。因此,如果在一個科學計算應用中執行實數處理,算術運算的速度會快得多。

2.2、非固有數值類型

除了 NUMBER、BINARY_FLOAT 和 BINARY_DOUBLE,Oracle 在語法上還支持(在語法上支持是指 CREATE 語句可以使用這些數據類型,但是在底層實際上它們都是 NUMBER 類型)以下數值數據類型,這些數值數據類型總是會映射到固有的 Oracle NUMBER 類型。

  • NUMERIC(p,s):完全映射至 NUMBER(p,s)。
  • DECIMAL(p,s) 或 DEC(p,s):完全映射至 NUMBER(p,s)。
  • INTEGER 或 INT:完全映射至 NUMBER(38)。
  • SMALLINT:完全映射至 NUMBER(38)。
  • FLOAT(p):映射至 NUMBER 類型。
  • DOUBLE(p):映射至 NUMBER 類型。
  • REAL:映射至 NUMBER 類型。

2.3、性能考慮

一般而言,Oracle NUMBER 類型對大多數應用來講都是最佳的選擇,不過這個類型會帶來性能影響。Oracle NUMBER 是一種軟件數據類型,在 Oracle 軟件本身中實現。不能使用固有硬件操作將兩個 NUMBER 類型相加,這要在軟件中模擬。而兩個浮點數相加時,Oracle 會使用硬件來執行運算。

由於 Oracle NUMBER 精度高於浮點類型,而浮點類型的計算速度又遠遠超過 Oracle NUMBER。此時可以用 Oracle NUMBER 精確地存儲數據,然后用 CAST 函數來達到提速的目的。示例:

SELECT SUM(LN(CAST(num_type AS BINARY_DOUBLE))) FROM t;

3、日期類型

3.1、日期格式

日期格式有兩個作用:

  • 以某種樣式對數據庫中的數據進行格式化,以滿足你的需求;
  • 告訴數據庫如何將一個輸入串轉換為 DATE、TIMESTAMP 或 INTERVAL。

一個常見的誤解是:使用格式會以某種方式影響磁盤上存儲的數據,並且會影響數據如何具體的存儲。格式對數據如何存儲根本沒有任何影響。格式只是用於將存儲 DATE 所用的二進制格式轉換為一個串,或者將一個串轉換為用於存儲 DATE 的二進制格式。對於 TIMESTAMP 和 INTERVAL 也是如此。

應該使用格式,而不要依賴於默認的日期格式,默認格式很可能會在將來的某個時刻被其他人改掉。如果被改掉,響應的應用也會受到負面影響,譬如格式錯誤,甚至插入錯誤數據和 SQL 諸如攻擊。

3.2、常用日期類型及四個案例

Oracle 中有 DATE、TIMESTAMP 等多個日期類型,但常用作字段數據類型的只有 DATE,其它有些類型可能會在日期運算中涉及到。

案例一:向 DATE 增加或減去指定時間,對一個 DATE 變量加 1,相當於在變量基礎上增加 1 天,增加 -1 就是減去 1 天,增加 124 就是增加 1 個小時,依此類推。示例:

SELECT fn_now+1 res FROM DUAL;      -- res:2017-01-11 19:21:30
SELECT fn_now-1 res FROM DUAL;      -- res:2017-01-09 19:21:30
SELECT fn_now+(1/24) res FROM DUAL; -- res:2017-01-10 20:21:30

案例二:兩個日期相減會得到相隔的天數。示例:

SELECT t1.dt2-t1.dt1 days 
  FROM(SELECT TO_DATE('2015-08-01 01:02:03','yyyy-mm-dd hh24:mi:ss') dt1,
              TO_DATE('2017-01-02 19:42:30','yyyy-mm-dd hh24:mi:ss') dt2 
       FROM DUAL) t1;
-- days:398.778090277778,即約 398.8 天

案例三:使用內置函數 MONTHS_BETWEEN 會得到兩個日期相隔的月數。示例:

SELECT MONTHS_BETWEEN(dt2,dt1) months 
FROM(SELECT TO_DATE('2015-08-01 01:02:03','yyyy-mm-dd hh24:mi:ss') dt1,
            TO_DATE('2017-01-02 19:42:30','yyyy-mm-dd hh24:mi:ss') dt2 
     FROM DUAL) t1;
-- months:13.0573577508961,即 13 個月

案例四:利用 INTERVAL 類型得到兩個日期的間隔。示例:

SELECT NUMTOYMINTERVAL(TRUNC(MONTHS_BETWEEN(t1.dt2,t1.dt1)),'month') years_months,
       NUMTODSINTERVAL(t1.dt2-ADD_MONTHS(t1.dt1,TRUNC(MONTHS_BETWEEN(t1.dt2,t1.dt1))),'day') day_hours 
FROM(SELECT TO_DATE('2015-08-01 01:02:03','yyyy-mm-dd hh24:mi:ss') dt1,
            TO_DATE('2016-10-06 10:06:30','yyyy-mm-dd hh24:mi:ss') dt2 
     FROM DUAL) t1;
-- years_months:+000000001-02,即 1年 2個月
-- day_hours:+000000005 09:04:27.000000000,即 5天 9小時 4 分鍾 27 秒

4、總結

4.1、Oracle 與 SQL Server 數據類型比較

有些業務的數據類型用布爾值是比較合適的,比如存儲用戶的激活狀態,要么已激活,要么未激活,不會有第三種情況出現。在 SQL Server 中一般用 bit 類型,我曾想在 Oracle 中找個布爾類型,結果發現 Oracle 只在語法上對布爾類型提供支持。字段數據類型你想用?對不起,這個真沒有!

有時候我們需要用整形,有時候需要用浮點型,SQL Server 中有 int、float、double、decimal 等一整套非常契合程序員三觀的數值類型。如果你想在 Oracle 中直接用這些類型,不好意思,它不直接提供,它只在語法上支持這些類型,好在 Oracle 中的 NUMBER 可以代替上述所有類型。這也有個好處就是數據庫編程容易了,但同時帶來的一個壞處就是后台編程麻煩了。

SQL Server 中提供了日期(date)和日期時間(datetime)兩個類型,開發中一般都是用 datetime 居多。如果你發現 Oracle 中沒有 datetime,不必驚慌,因為它的 date 就相當於 SQL Server 中的 datetime,只是精度稍差,但一般也足夠用了。

4.2、Oracle 數據類型使用感言

Oracle 中的數據類型看上去很多,但對於大多數應用而言,大部分類型不怎么好用,甚至永遠都不應該使用,假如不慎用了,還有可能會引起災難性的后果。有時候我覺得 Oracle 之於數據庫頗有點 IE6 之於瀏覽器的味道。好的很多新的應用已不再選用 Oracle,而 Oracle 本身也在不斷改進,相信將來一切都會變得更好!

事實上基於 Oracle 的程序開發,真正常用的數據類型也就字符、數值和日期“三大主流數據類型”。其它數據類型要么不好用(如 CHAR、NCHAR 等類型),要么極少用(如 BLOB、CLOB 等 LOB 類型),要么根本就不應該使用(如 LONG、LONG RAW 等類型)。

4.3、附:Oracle 數據類型概述

Oracle 提供了 22 種不同的 SQL 數據類型供我們使用,分別如下:

  • CHAR:這是一種定長字符串,會用空格填充來達到其最大長度。非 null 的 CHAR(10) 總是包含 10 字節信息,使用默認 NLS 設置,CHAR 最多可以存儲 2000 字節的信息。
  • NCHAR:這是一種包含 UNICODE 格式數據的定長字符串。有了 NCHAR 類型,就允許數據庫中包含采用兩種不同字符集的數據:使用數據庫字符集的 CHAR 類型和使用國家字符集的 NCHAR 類型。非 null 的 NCHAR(10) 總是包含 10 個字符的信息,NCHAR 最多可以存儲 2000 字節的信息。
  • VARCHAR2:目前這也是 VARCHAR 的同義詞。這是一種變長字符串,與 CHAR 類型不同,它不會用空格填充至最大長度。VARCHAR2(10) 可能包含 0~10 字節的信息,使用默認 NLS 設置,VARCHAR2 最多可以存儲 4000 字節的信息。
  • NVARCHAR2:這是一種包含 UNICODE 格式數據的變長字符串。NVARCHAR2(10) 可以包含 0~10 個字符的信息,NVARCHAR2 最多可以存儲 4000 字節的信息。
  • RAW:這是一種變長的二進制數據類型,采用這種數據類型存儲的數據不會發生字符集轉換。可以把它看作是由數據庫存儲的信息的二進制字節串。RAW 最多可以存儲 2000 字節的信息。
  • NUMBER:這種數據類型能存儲精度多達 38 位的數字。這些數介於 1.0 * 10-130 至 1.0 * 10 126(不含)之間。每個數存儲在一個變長字段中,其長度在 0(尾部的 NULL 列就是 0 字節)~22字節之間。Oracle 的 NUMBER 類型精度很高,遠遠高於許多編程語言中常規的 float 和 double 類型。
  • BINARY_FLOAT:這是 Oracle Database 10g Release 1 及以上版本中才有的一種新類型。它是一種 32 位單精度浮點數,可以支持至少 6 為精度,占用磁盤上 5 字節的存儲空間。
  • BINARY_DOUBLE:這是 Oracle Database 10g Release 1 及以上版本中才有的一種新類型。它是一種 64 位雙精度浮點數,可以支持至少 13 位精度,占用磁盤上 9 字節的存儲空間。
  • LONG:這種類型能存儲最多 2GB 的字符數據(2GB 指的是 2 千兆字節,而不是 2 千兆字符,因為在一個多字節字符集中,每個字符可能有多個字節)。由於 LONG 類型有許多限制,提供 LONG 類型也只是為了保證向后兼容性,所以強烈建議新應用中不要使用 LONG 類型,而且現有應用中的 LONG 也要盡可能轉換為 CLOB 類型。
  • LONG RAW:LONG RAW 類型能存儲多達 2GB 的二進制信息。由於 LONG 同樣的原因,建議用 BLOB 類型全面替代 LONG RAW 類型。
  • DATE:這是一種 7 字節的定寬日期數據類型。它總是包含 7 個屬性,包括:世紀、世紀中那一年、月份、月份中那一天、小時、分鍾和秒。
  • TIMESTAMP:這是一種 7 字節或 11 字節的定寬日期類型。與 DATE 類型不同的是 TIMESTAMP 還包含小數秒(fractional second),帶小數秒的 TIMESTAMP 在小數點右邊最多可保留 9 位。
  • TIMESTAMP WITH TIME ZONE:與 TIMESTAMP 類似,這是一種 13 字節的定寬 TIMESTAMP,不過它還提供了對時區(time zone)的支持。
  • TIMESTAMP WITH LOCAL TIME ZONE:與 TIMESTAMP 類似,這是一種 7 字節或 11 字節的定寬日期數據類型。這種類型對時區敏感,會根據數據庫或會話的時區自動轉換為對應時區的日期。
  • INTERVAL YEAR TO MONTH:這是一種 5 字節的定寬數據類型,用於存儲一個時段,這個類型將時段存儲為年數和月數。可以在日期運算中使用這種時間間隔使一個 DATE 或 TIMESTAMP 類型增加或減少一段時間。
  • INTERVAL DAY TO SECOND:這是一種 11 字節的定寬數據類型,用於存儲一個時段,這個類型將時段存儲為天/小時/分鍾/秒數,還可以有最多 9 位的小數秒。
  • BFILE:這種數據類型允許在數據庫列中存儲一個 Oracle 目錄對象(操作系統目錄的一個指針)和一個文件名,並讀取這個文件。這實際上允許你以一種只讀的方式訪問數據庫服務器上可用的操作系統文件,就好像它們存儲在數據庫表中一樣。
  • BLOB:在 Oracle Database 10g 及以上版本中允許存儲最多 4GB×10(數據塊大小)字節的數據。BLOB 包含不需要進行字符集轉換的“二進制”數據,如果要存儲電子表格、字處理文檔、圖像文件等就很適合采用這種數據類型。
  • CLOB:在 Oracle Database 10g 及以上版本中允許存儲最多 4GB×10(數據塊大小)字節的數據。CLOB 包含要進行字符集轉換的信息,這種數據類型很適合存儲大塊兒純文本信息。如果你的純文本信息只有 4000 字節或更少,那么這種數據類型並不適用,這種情況更適合用 VARCHAR2。
  • NCLOB:在 Oracle Database 10g 及以上版本中允許存儲最多 4GB×10(數據塊大小)字節的數據。NCLOB 存儲用數據庫國家字符集編碼的信息,而且像 CLOB 一樣需要進行字符集轉換。
  • ROWID:ROWID 實際上是數據庫中一行的 10 字節地址。ROWID 中編碼有足夠的信息,足以在磁盤上定位這一行,以及標識 ROWID 指向的對象(表等)。
  • UROWID:UROWID 是一種通用的 ROWID,用於表(如 IOT 和通過異構數據庫網關訪問的沒有固定 ROWID 的表)。UROWID 是行主鍵值的一種表示,因此取決於所指向的對象,UROWID 的大小會有所變化。

顯然以上列表中還少了許多類型,如 INT、INTEGER、SMALLINT、FLOAT、REAL 等。這些類型實際上都是在上表所列的某種基本類型的基礎上實現的,也就是說,它們只是固有 Oracle 類型的同義詞。還有 XMLType、SYS.ANYTYPE、SEO_GEOMETRY 等復雜類型,總之 Oracle 中的類型是很多的。如果你還想了解更多有關 Oracle 數據類型的細節,請參考:《Oracle Database Concepts: Oracle Data Types》

本文鏈接http://www.cnblogs.com/hanzongze/p/oracle-datatype.html
版權聲明:本文為博客園博主 韓宗澤 原創,作者保留署名權!歡迎通過轉載、演繹或其它傳播方式來使用本文,但必須在明顯位置給出作者署名和本文鏈接!本人初寫博客,水平有限,若有不當之處,敬請批評指正,謝謝!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM