.NET程序员看Oracle数据类型
一、 数值类型:
1. NUMBER:
Oracle里面其实只有一种数据类型,即NUMBER。(好吧,我承认这句话有问题,但后面会说明)。NUMBER可以指定精度(precision)和小数位数(scale),格式是NUMBER(p,s),其中p表示精度,s表示小数位数,两者都是可选的。
l 精度(precision),或总位数,表示最多可以存储几位数字。取值范围是1~38,默认是38。
l 小数位数(scale),或小数点右边的位数,表示最多可以存储几位小数。取值范围是-84~127,而默认值取决于是否指定了精度:若没有指定精度,则小数位默认有最大的取值区间;若指定了精度而没有指定小数位数,则默认为0。如果插入的小数位数超过设定的位数,则Oracle会舍入到设定的位数。有人可能会问,小数位数怎么还能是负数呢?是的,其作用就是允许对小数点左边的值进行舍入,如类型是number(5,-2),则123就会被舍入为100。
NUMBER类型有好几个变体,但它们本质上都是NUMBER类型。
l INTEGER:映射到NUMBER(38),它不允许存储小数。
l INT或SMALLINT:完全映射到INTEGER。
l NUMERIC(p,s)、DECIMAL(p,s)、或DEC(p,s):完全映射到NUMBER(p,s)。如果p未指定,则默认为38,即INTEGER。
l FLOAT(b):这里的b指定的也是“精度”,取值范围是1~126,默认值为126。但是它与p不同,这里使用的是二进制的精度。可以换算b和p:b*0.30103=p。
l REAL:映射到FLOAT(63)。
l DOUBLE PRECISION:映射到FLOAT。
Oracle的NUMBER类型,比.NET所能支持的数值范围要大的多。设计数据库表时,可以根据存储的数值范围和精度的需要,来合理的选择p和s。简单的对应,如果要存储整数(Byte/SByte/Int16/UInt16/Int32/UInt32/Int64/UInt64结构),使用INTEGER;如果要存储浮点数(Single/Double结构),使用FLOAT;如果要存储小数(Decimal结构),则使用NUMBER。
2. BINARY_FLOAT和BINARY_DOUBLE:
从Oracle 10g开始,引入了两种新的数值类型:BINARY_FLOAT和BINARY_DOUBLE。它们就是IEEE 754定义的单精度浮点数和双精度浮点数。这里修正了前面“Oracle里面其实只有一种数据类型,即NUMBER”的说法。
由于.NET里面的Single/Double结构,同样符合IEEE 754定义,因此它们和BINARY_FLOAT/BINARY_DOUBLE是完全对应的。
与之相反的是,NUMBER类型是使用十进制指数方式存储的,同时它是一种“软件数据类型”,即是在Oracle软件本身中实现的,因此性能要相对低一些。如果需要存储完全符合IEEE 754的浮点数,或者比较在意性能问题,则可以考虑选择BINARY_FLOAT和BINARY_DOUBLE类型。
二、 字符类型:
1. CHAR/VARCHAR2及它们的变体NCHAR/NVARCHAR2:
Oracle中有四种字符类型,可以按照定长/变长,和存储字符集两个维度的分类,将它们划分如下:
| 默认字符集 (NLS_CHARACTERSET) | 国家字符集 (NLS_NCHAR_CHARACTERSET) |
定长字符类型 | CHAR | NCHAR |
变长字符类型 | VARCHAR2 | NVARCHAR2 |
CHAR/NCHAR最多能存储2000个字节的字符串,VARCHAR2/NVARCHAR2最多能存储4000个字节的字符串。
一般情况下,不需要考虑定长字符类型。因为定长字符类型总是会用空格填充得到的串,使之达到一个固定宽度,所以它们实际浪费了存储空间。另一方面,这也会在字符串比较方面碰到麻烦。所以即使只是为了存储一个字符(Char结构),也没有必要使用定长字符类型。
CHAR和VARCHAR2,可以使用两种方式指定长度:
l 用字节数指定:VARCHAR2(10 byte)或VARCHAR2(10),表示最多可以存储10个字节长度的字符串。这最多能存储几个字符,取决于数据库的默认字符集。在一个多字节字符集中,一个字符可能需要采用4个字节表示,那么最多就只能存储2个字符。
l 用字符数指定:VARCHAR2(10 char),这表示最多可以存储10个字符长度的字符串。
从Oralce9i开始,数据库的国家字符集只能允许两种可能的取值:UTF8或AL16UTF16(9i中是UTF16,10g中是AL16UTF16)。所以NCHAR/NVARCHAR2就非常适合用来存储Unicode字符串。与CHAR/VARCHA2不同,指定NCHAR/NVARCHAR2的长度时,所使用的单位是字符:NVARCHAR2(10)就表示最多能存储10个字符长度的字符串。
.NET中,字符串(System类)的编码固定是Unicode(确切的说是UTF-16),所以看起来它最适合对应的Oracle数据类型是NVARCHAR2。但是在一般的应用中,大部分的字符串应该是本土化的,所以完全可以对应VARCHAR2数据类型,当然前提是为数据库设置了合适的默认字符集。
2. CLOB及NCLOB:
VARCHAR2/ NVARCHAR2类型的长度有限,当需要存储的字符串长度超过4000个字节时,就需要考虑使用大文本对象。
与VARCHAR2/NVARCHAR2类似,CLOB/NCLOB也分别使用默认字符集和国家字符集编码进行存储。所不同的是,CLOB/NCLOB最多允许存储4GB的字符串。
CLOB和NCLOB,以及后面提到的BLOB,在Oracle中成为“大对象”(Large Object),在存储和处理方面有一些特殊性。不过由于这篇文章的主题是讨论数据类型的选择,因此并不准备讨论这些话题。
三、 日期类型:
1. DATE类型:
Oracle中的DATE类型表示日期和时间,它总是包含年、月、日、小时、分钟和秒。
.NET中的DateTime结构可以存储到DATE类型中。但DATE类型存在的明显的两个问题:(1)不支持小数秒;(2)不支持时区。
2. TIMESTAMP类型:
从Oracle 9i开始,引入了TIMESTAMP类型。TIMESTAMP类型与DATE类型非常类似,只不过另外还支持小数秒和时区。根据表示时区的不同形式,有三种具体的TIMESTAMP类型:TIMESTAMP、TIMESTAMP WITH TIME ZONE、TIMESTAMP WITH LOCAL TIME ZONE。
基本的TIMESTAMP 数据类型的语法很简单:TIMESTAMP(n),这里n是可选的,用于指定秒的小数位数,取值范围是0~9,默认值为6。如果n取值为0,则功能上就等同于DATE类型。
TIMESTAMP WITH TIME ZONE 类型继承了TIMESTAMP 类型的所有特点,并增加了时区支持。存储数据时,TIMESTAMP WITH TIME ZONE 会在数据中存储指定的时区,时区成为数据本身的一部分。
TIMESTAMP WITH LOCAL TIME ZONE类型也支持时区。在存储数据时,Oracle会将时间转换为数据库所在时区的时间,而并不存储时区信息。读取时,Oracle认为这里存储的时间就是数据库所在时区的时间。
从.NET 2.0 SP1开始,引入了一个新的DateTimeOffset结构,该结构包括一个 DateTime 值以及一个 Offset 属性,后者用于确定当前 DateTimeOffset 实例的日期和时间与协调世界时 (UTC) 之间的差值。我不知道它对于时区的处理是否与TIMESTAMP WITH TIME ZONE完全一致,但是直觉上它们应该是一个合适地映射。
另外,如果应用不需要记住源时区,只需要一种能对日期/时间类型提供一致的全球性处理数据类型,那么TIMESTAMP WITH LOCAL TIME ZONE已经提供了足够的支持。在对这个类型进行读取的时候,你需要确保对于会话设置了正确的时区。
3. INTERVAL类型:
INTERVAL类型示一段时间或一个时间间隔。有两种INTERVAL类型:
INTERVAL YEAR TO MONTH表示几年加几个月,由于它并不常用,我打算根本不介绍它。
INTERVAL DAY TO SECOND表示几天几小时几分钟又几秒。实际上,它和.NET中的TimeSpan结构是对应的。它的语法是INTERVAL DAY(n) TO SECOND(m)。在此n 是一个可选的位数,支持天数分量,取值范围为0~9,默认为2;m是秒字段小数部分中保留的位数,取值范围为0~9,默认为6。
四、 二进制类型:
二进制类型用于存储二进制字节串,对应的.NET类型是Byte数组。
和字符类型类似,RAW类型用于存储不操作2000个字节的二进制数据,其中可以指定最多存储的字节数;而BLOB可用于存储最多4GB的二进制数据。
五、 布尔类型:
Oracle中没有布尔类型。如果需要存储.NET中的Boolean结构,可以存储”Y”/”N”或者”T”/”F”的单个字符(使用VARCHAR2(1)类型),或者存储1/0的单个数值(使用NUMBER(1)类型)。
六、 其他类型:
1. 枚举类型:
Oracle中也没有枚举类型。和布尔类型相似,我们同样可以存储枚举常量的名称(使用VARCHAR2或NVARCHAR2类型),或者存储枚举的基础类型(UnderlyingType)的值(使用NUMBER类型)。
2. 可序列化的类型:
我们可以将可序列化类型的对象序列化保存到BLOB类型中,读取时则进行反序列化。
3. Guid:
Oracle不支持GUID类型,但是我们可以把Guid对象ToString()返回的字符串保存到数据库中。
最后总结如下:
类型分类 | .NET类型 | Oracle类型 |
数值类型 | Byte SByte | NUMBER(3) |
| Int16 UInt16 | NUMBER(5) |
| Int32 UInt32 | NUMBER(10) |
| Int64 UInt64 | NUMBER(20) |
| Single | FLOAT(24) |
| Single | BINARY_FLOAT(IEEE 754) |
| Double | DOUBLE PRECISION |
| Double | BINARY_DOUBLE(IEEE 754) |
| Decimal | NUMBER |
字符类型 | Char | VARCHAR2(1 Char)(本地字符集) NVARCHAR2(1)(Unicode字符集) |
| String(不超过4000个字节) | VARCHAR2(n Char)(本地字符集) NVARCHAR2(n)(Unicode字符集) |
| String | CLOB(本地字符集) NCLOB(Unicode字符集) |
日期类型 | DateTime | DATE(没有秒的小数位) TIMESTAMP(不关心时区) TIMESTAMP WITH LOCAL TIME ZONE(使用本地时区) |
| DateTimeOffset | TIMESTAMP WITH TIME ZONE |
| TimeSpan | INTERVAL DAY TO SECOND |
二进制类型 | Byte[](不超过2000个字节) | RAW(n) |
| Byte[] | BLOB |
布尔类型 | Boolean | VARCHAR2(1)(存储”Y”/”N”或”T”/”F”) |
| Boolean | NUMBER(1)(存储1/0) |
枚举类型 | 枚举类型 | VARCHAR2(n char)或NVARCHAR2(n)(存储常量名称) |
| 枚举类型 | NUMBER(n)(存储基础类型的值) |
可序列化类型 | 可序列化类型 | BLOB(存储序列化二进制串) |
Guid | Guid | VARCHAR2(38)(存储ToString()方法返回值) |
转发qmxle的文章。