PostgreSQL和Oracle的数据类型的对比


PostgreSQL和Oracle的数据类型的对比系列之
一:字符类型
二:数字类型
三:日期时间类型
四:大数据类型
五:其它类型
本文总结息《卸甲笔记》,感谢原作者。
--------------------------------------------------------------
一:字符类型
PostgreSQL是世界上功能最强大的开源数据库,在国内得到了越来越多机构和开发者的青睐和应用。随着PostgreSQL的应用越来越广泛,Oracle向PostgreSQL数据库的数据迁移需求也越来越多。数据库之间数据迁移的时候,首先遇到的,并且也是最重要的,就是数据类型之间的转换。下面根据自己的理解和测试,写了一些数据类型之间的差异以及迁移时的注意事项的文章,不足之处,尚请多多指教。
字符类型
大家知道,Oracle有四种字符类型,分别是char,varchar2,nchar,nvarchar2。而PostgreSQL只有两种字符类型,分别是character (char)和 character varying(varchar)。它们之间的有什么异同呢?
1、数据最小单位
Oracle:char和varchar2的数据最小单位,默认值为BYTE,即字节数。可以修改为CHAR,则为字符。通过参数NLS_LENGTH_SEMANTICS来控制。
而Oracle的nchar和nvarchar2是针对特定字符集来的,数据最小单位根据字符集不同而不同。如字符集是AL16UTF16,就是2个字节,而字符集是UTF8,就是3个,通过参数NLS_NCHAR_CHARACTERSET来控制(我的Oracle的NLS_NCHAR_CHARACTERSET值就是AL16UTF16,一个字为2byte)。
当数据不够最大长度的时候,char和nchar写入/取出的是最大长度的字符串,超出实际长度的使用空格补齐。而varchar2和 nvarchar2写入/取出的是实际操作的字符串。
SQL> create table o_test(id int primary key,
2 name char(5),
3 n_name nchar(5),
4 addr varchar2(5),
5 n_addr nvarchar2(5));
表已创建。
SQL> insert into o_test values(1, 'li', 'li', 'BJ', 'BJ');
已创建 1 行。
SQL> select lengthb(name), lengthb(n_name), lengthb(addr),lengthb(n_addr)
2 from o_test;
LENGTHB(NAME) LENGTHB(N_NAME) LENGTHB(ADDR) LENGTHB(N_ADDR)
------------- --------------- ------------- ---------------
5 10 2 4
PostgreSQL:char和 varchar的数据最小单位都是字符(CHAR)。
当数据不够最大长度的时候,char类型的数据物理上用空白填充到指定的长度,并且以这种方式存储和显示。但是,填充的空白是无意义的。在取出来实际使用的时候,空白会自动被删除。VARCHAR类型的数据,会按照实际长度存储。最后的空白都是有意义的,不会被删除。
highgo=# create table p_test(id smallint primary key,
highgo(# name char(5),
highgo(# v_name varchar(5));
CREATE TABLE
highgo=# insert into p_test values(1,'赵 ', '赵老大 ');
INSERT 0 1
highgo=# select length(name), length(v_name) from p_test;
length | length
--------+--------
1 | 4
(1 行记录)
2、数据类型的最大长度
Oracle:char类型的最大长度是2000字节,varchar2是4000个字节。
nchar,nvarchar2类型的最大长度根据数据集不同而不同。最终的byte数不能超过2000和4000。如字符集为AL16UTF16,则nchar的为2000/2=1000,而nvarchar2则为4000/2=2000
SQL> create table o_test2(name char(2001));
create table o_test2(name char(2001))
第 1 行出现错误:
ORA-00910: 指定的长度对于数据类型而言过长
SQL> create table o_test2(name char(2000));
表已创建。
SQL> create table o_test3(name varchar2(4001));
create table o_test3(name varchar2(4001))
第 1 行出现错误:
ORA-00910: 指定的长度对于数据类型而言过长
SQL> create table o_test3(name varchar2(4000));
表已创建。
SQL> create table o_table5(name nchar(1001));
create table o_table5(name nchar(1001))
第 1 行出现错误:
ORA-00910: 指定的长度对于数据类型而言过长
SQL> create table o_table5(name nchar(1000));
表已创建。
SQL> create table o_table6(name nvarchar2(2001));
create table o_table6(name nvarchar2(2001))
第 1 行出现错误:
ORA-00910: 指定的长度对于数据类型而言过长
SQL> create table o_table6(name nvarchar2(2000));
表已创建。
PostgreSQL:理论最大数据长度是1G,默认值是10 1024 1024=10485760(具体的值由htup_details.h的MaxAttrSize决定)
highgo=# create table p_test3(name char(10485761));
错误: 类型 char 的长度不能超过 10485760
第1行create table p_test3(name char(10485761));
^
highgo=# create table p_test3(name char(10485760));
CREATE TABLE
highgo=# create table p_test4(name varchar(10485761));
错误: 类型 varchar 的长度不能超过 10485760
第1行create table p_test4(name varchar(10485761));
^
highgo=# create table p_test4(name varchar(10485760));
CREATE TABLE
3、定义数据时的参数(字符数)
Oracle:char类型,不带字符数的时候,是1个字符(char(1)),而varchar2必须有字符数。
nchar和nvarchar2分别与char,varchar2类似。
SQL> create table o_test1(name char, addr varchar);
create table o_test1(name char, addr varchar)
第 1 行出现错误:
ORA-00906: 缺失左括号
SQL> create table o_test1(name char, addr varchar(5));
表已创建。
SQL> insert into o_test1 values('12','OK');
insert into o_test1 values('12','OK')
第 1 行出现错误:
ORA-12899: 列 "SCOTT"."O_TEST1"."NAME" 的值太大 (实际值: 2, 最大值: 1)
SQL> insert into o_test1 values('1','OK');
已创建 1 行。
PostgreSQL:char类型,不带字符数的时候,也是默认一个字符(char(1)),而varchar不带字符数的时候,没有限制。
highgo=# create table p_test1(name char, addr varchar);
CREATE TABLE
highgo=# insert into p_test1 values('12', '1234567890');
错误: 对于字符类型来说这个值太长了(1)
highgo=# insert into p_test1 values('1', '1234567890');
INSERT 0 1
4、插入数据库的时候的,字符数超出最大长度部分的处理。
Oracle:报错。即使超出的部分是字符末的空格。
SQL> insert into o_test1 values('1','123456');
insert into o_test1 values('1','123456')
第 1 行出现错误:
ORA-12899: 列 "SCOTT"."O_TEST1"."ADDR" 的值太大 (实际值: 6, 最大值: 5)
SQL> insert into o_test1 values('1','1234 ');
insert into o_test1 values('1','1234 ')
第 1 行出现错误:
ORA-12899: 列 "SCOTT"."O_TEST1"."ADDR" 的值太大 (实际值: 6, 最大值: 5)
PostgreSQL:当超出部分是有效的字符的时候,报错。
当是最后的空格时,截断为最大长度,不报错。但是当使用明确的类型转换为最大字符数的时候,截断为最大长度,不报错。
highgo=# create table p_test2(name char(2), addr varchar(5));
CREATE TABLE
highgo=# insert into p_test2 values('123', '12345');
错误:对于字符类型来说这个值太长了(2)
highgo=# insert into p_test2 values('12 ', '12345');
INSERT 0 1
highgo=# insert into p_test2 values(102 :: char(2) , '12345');
INSERT 0 1
highgo=# select * from p_test2;
name | addr
------+-------
12 | 12345
10 | 12345
(2 行记录)
字符类型数据迁移过程中,应该先根据Oracle数据的字符集和数据长度,从类型和长度上保证对方的数据能够完全的转换过来。再注意一些转换过程中SQL的写法,字符型的数据基本上就能够完全的、正确的迁移到PostgreSQL数据库中来了。
参考文档:
PostgreSQL 9.4.4 中文手册:8.3.字符类型
http://www.postgres.cn/docs/9.4/datatype-character.html
Database SQL Language Reference:Oracle Built-in Data Types
http://docs.oracle.com/cd/E11882_01/server.112/e41084/sql_elements001.htm#SQLRF30020
--------------------------------------------------------------
二:数字类型
数字类型
Oracle内建的数字类型有四种,分别是number,float,binary_float,binary_double。由于和其余数据库的数据兼容,而产生了smallint,int,Integer,Decimal等多种数字类型,其实内部都是使用内建的四种实现的。因此我们只讨论内建的四种数据类型。
PostgreSQL的数字类型有三类。分别是 整数类型,包括smallint,integer和bigint。任意精度类型,包括numeric和decimal两种(其中decimal等同于numeric),以及浮点数类型,包括real和double。它们之间的区别和迁移时应该注意什么呢?下面按照精确类型(整数类型和任意精度类型)和非精确类型(浮点数类型)两类来进行讨论。
1、精确类型(整数和任意精度数值)
Oracle的number(p, s) 类型,根据精度(p)和小数位数(s)的不同,分别对应PostgreSQL中的多种情况。
1.1、当小数位数(s)=0时
当小数位数(s)=0时,可以对应PostgreSQL的smallint, integer, bigint, numeric(p)等多种数据类型。由于smallint,Integer,bigint的算术运算效率比numberic高的多,所以迁移时建议如下处理:
1.1.1、当p <= 4时,使用integer或者smallint
当p <= 4时,可以使用smallint和Integer,推荐使用Integer,因为它在取值范围、存储空间、性能之间最为平衡。只有磁盘空间紧张的情况下,才建议使用smallint。
Oracle number(p, 0) p <=4
SQL> create table o_test1( value number(4,0));
表已创建。
SQL> insert into o_test1 values(99999);
insert into o_test1 values(99999)
第 1 行出现错误:
ORA-01438: 值大于为此列指定的允许精度
SQL> insert into o_test1 values(9999);
已创建 1 行。
PostgreSQL smallint
postgres=# create table p_test1(value smallint);
CREATE TABLE
postgres=# insert into p_test1 values(99999);
错误: smallint 超出范围
postgres=# insert into p_test1 values(9999);
INSERT 0 1
1.1.2、当4 < p <= 9时,应该使用integer
Oracle number(p, 0) 4 < p <= 9
SQL> create table o_test1(value number(9,0));
表已创建。
SQL> insert into o_test1 values(9999999999);
insert into o_test1 values(9999999999)
第 1 行出现错误:
ORA-01438: 值大于为此列指定的允许精度
SQL> insert into o_test1 values(999999999);
已创建 1 行。
PostgreSQL integer
postgres=# create table p_test1(value integer);
CREATE TABLE
postgres=# insert into p_test1 values(9999999999);
错误: 整数超出范围
postgres=# insert into p_test1 values(999999999);
INSERT 0 1
1.1.3、当9 < p <= 18时,使用bigint
当9 < p <= 18时,这个时候integer的精度已经不够了,应该使用bigint
Oracle number(p, 0) 9 < p <= 18
SQL> create table o_test1(value number(18,0));
表已创建。
SQL> insert into o_test1 values(9999999999999999999);
insert into o_test1 values(9999999999999999999)
第 1 行出现错误:
ORA-01438: 值大于为此列指定的允许精度
SQL> insert into o_test1 values(999999999999999999);
已创建 1 行。
PostgreSQL bigint
postgres=# create table p_test1 (value bigint);
CREATE TABLE
postgres=# insert into p_test1 values(9999999999999999999);
错误: bigint 超出范围
postgres=# insert into p_test1 values(999999999999999999);
INSERT 0 1
1.1.4、当p > 18时,使用numeric(p, 0)
当p>18的时候,需要使用numeric(p, 0)。才能保证数据转换的精度。
Oracle number(p, 0) p > 18
SQL> create table o_test1(value number(19,0));
表已创建。
SQL> insert into o_test1 values(9999999999999999999);
已创建 1 行。
PostgreSQL numeric(19, 0)
postgres=# create table p_test1 (value numeric(19, 0));
CREATE TABLE
postgres=# insert into p_test1 values(9999999999999999999);
INSERT 0 1
1.2、当小数位数(s) > 0时
当小数位数(s) > 0时,由于有小数,只能够使用numeric(p, s)。
1.2.1、当0 < s <=p的时候,使用numeric(p, s)
Oracle number(p, s) 0 < s <= p
SQL> create table o_test1(value number(5,3));
表已创建。
SQL> insert into o_test1 values(123.567);
insert into o_test1 values(123.567)
第 1 行出现错误:
ORA-01438: 值大于为此列指定的允许精度
SQL> insert into o_test1 values(12.56789);
已创建 1 行。
SQL> select * from o_test1;
VALUE
----------
12.568
PostgreSQL numeric(p, s) p >= s
postgres=# create table p_test1( value numeric(5, 3));
CREATE TABLE
postgres=# insert into p_test1 values(123.456);
错误: 数字字段溢出
描述: 精度为5,范围是3的字段必须四舍五入到小于10^2的绝对值.
postgres=# insert into p_test1 values(12.456789);
INSERT 0 1
postgres=# select * from p_test1;
value
--------
12.457
(1 行记录)
1.2.2、当 p < s的时候,使用numeric(s, s)
当 p < s的时候,使用numeric(s, s)。此时,Oracle可以控制小数点后0的个数,而PostgreSQL不可。由于数据迁移,只需要把数据按照原来的样式迁移过来即可,所以不存在问题。
Oracle number(p, s) s > p
SQL> create table o_test1(value number(3,5));
表已创建。
SQL> insert into o_test1 values(0.056789);
insert into o_test1 values(0.056789)
第 1 行出现错误:
ORA-01438: 值大于为此列指定的允许精度
SQL> insert into o_test1 values(0.0056789);
已创建 1 行。
SQL> select * from o_test1;
VALUE
----------
.00568
PostgreSQL numeric(p, s) s > p
postgres=# create table p_test1( value numeric(5, 5));
CREATE TABLE
postgres=# insert into p_test1 values(0.056789);
INSERT 0 1
postgres=# insert into p_test1 values(0.0056789);
INSERT 0 1
postgres=# select * from p_test1;
value
---------
0.05679
0.00568
(2 行记录)
1.3、当小数位数(s) < 0时
当小数位数(s) < 0的时候,由于此时p代表精度的位数,s位决定了在何处进行位数的舍入。s < 0的时候,这种根据s来进行数据舍入的功能PostgreSQL尚不具备,但由于数据迁移时只要能将Oracle的数据完全转换过来就行了。所以对于数据迁移来说也不存在问题。
PostgreSQL中,小数位数必须大于等于0。此时的迁移,需要把p + |s| 作为数据的精度来判断。其余和s=0的时候相同。
1.3.1、当p + |s| <= 4时,使用integer或者smallint
当p+ |s| <= 4时,可以使用smallint和Integer,推荐使用Integer,只有磁盘空间紧张的情况下,才建议使用smallInt。
Oracle number(p, s) p+ |s| <= 4
SQL> create table o_test1( value number(2,-2));
表已创建。
SQL> insert into o_test1 values(9950);
insert into o_test1 values(9950)
*
第 1 行出现错误:
ORA-01438: 值大于为此列指定的允许精度
SQL> insert into o_test1 values(9940);
已创建 1 行。
SQL> select * from o_test1;
VALUE
----------
9900
PostgreSQL smallint
postgres=# create table p_test1( value smallint);
CREATE TABLE
postgres=# insert into p_test1 values(9950);
INSERT 0 1
postgres=# insert into p_test1 values(9940);
INSERT 0 1
postgres=# select * from p_test1;
value
-------
9950
9940
(2 行记录)
1.3.2、当4 < p+ |s| <= 9时,使用Integer
当4 < p+ |s| <= 9时,应该使用Integer。
Oracle number(p, s) 4 < p+ |s| <= 9
SQL> create table o_test1( value number(6,-3));
表已创建。
SQL> insert into o_test1 values(999999500);
insert into o_test1 values(999999500)
第 1 行出现错误:
ORA-01438: 值大于为此列指定的允许精度
SQL> insert into o_test1 values(999999400);
已创建 1 行。
SQL> select * from o_test1;
VALUE
----------
999999000
PostgreSQL integer
postgres=# create table p_test1( value integer);
CREATE TABLE
postgres=# insert into p_test1 values(999999500);
INSERT 0 1
postgres=# insert into p_test1 values(999999400);
INSERT 0 1
postgres=# select * from p_test1;
value
-----------
999999500
999999400
(2 行记录)
1.3.3、当9 < p+ |s| <= 18时,使用bigint
当9 < p+ |s| <= 18时,这个时候Integer的精度已经不够了,可以使用bigint。
Oracle 9 < p+ |s| <= 18
SQL> create table o_test1( value number(12,-6));
表已创建。
SQL> insert into o_test1 values(999999999999500000);
insert into o_test1 values(999999999999500000)
第 1 行出现错误:
ORA-01438: 值大于为此列指定的允许精度
SQL> insert into o_test1 values(999999999999400000);
已创建 1 行。
PostgreSQL bigint
postgres=# create table p_test1( value bigint);
CREATE TABLE
postgres=# insert into p_test1 values(999999999999500000);
INSERT 0 1
postgres=# insert into p_test1 values(999999999999400000);
INSERT 0 1
postgres=# select * from p_test1;
value
--------------------
999999999999500000
999999999999400000
(2 行记录)
1.3.4、当p+ |s| >18时,使用numeric(p + |s|, 0)
当p+ |s| >18的时候,只能使用numeric(p + |s|, 0)。
Oracle p+ |s| > 18
SQL> create table o_test1( value number(15,-6));
表已创建。
SQL> insert into o_test1 values(999999999999999500000);
insert into o_test1 values(999999999999999500000)
第 1 行出现错误:
ORA-01438: 值大于为此列指定的允许精度
SQL> insert into o_test1 values(999999999999999400000);
已创建 1 行。
SQL> select * from o_test1;
VALUE
------------------------------
999999999999999000000
PostgreSQL numeric(p + |s|)
postgres=# create table p_test1( value numeric(21));
CREATE TABLE
postgres=# insert into p_test1 values(999999999999999500000);
INSERT 0 1
postgres=# insert into p_test1 values(999999999999999400000);
INSERT 0 1
postgres=# select * from p_test1;
value
-----------------------
999999999999999500000
999999999999999400000
2、非精确类型(浮点数类型)
浮点数是不精确的、变精度的数字类型。由于有下层处理器、操作系统和编译器对它的支持,所以很多情况下处理速度会快的多。但是由于只是以近似值存储的,对于想得到精确值的情况,不可以使用。
Oracle的float类型包括float[(p)], Binary_float, Binary_double三种类型。基本上和PostgreSQL的real和double precision相对应。PostgreSQL也提供了float(p)类型,但是和real和double precision是基本相同的,所以此处只讨论real和double precision两种。
2.1、Oracle float(p)
Oracle的float类型,根据值的范围区间,可以简单认为在1E-37 ~1E+37之间时,和PostgreSQL的real对应。超出这个区间的时候,则和PostgreSQL的double precision对应。但是精度方面,PostgreSQL尚达不到Oracle的float能达到的精度。
Oracle的 float的精度p是用二进制的1-126表示,若转化为10进制,则需要乘以0.30103。所以,大致相当于1-126*0.30103 ~ 1- 38。而PostgreSQL的real精度是6位,而double precision精度大概是在15位,可以通过修改pg_settings的extra_float_digits参数的值增加-15~3位的精度。但还支持不到更大的位数。但在实际应用中,不会有人把浮点数放到那么大的精度。PostgreSQL默认的精度已经足够。
Oracle float(p)
SQL> create table o_test1( value float(126));
表已创建。
SQL> insert into o_test1 values(12345678901234567890123456789012345678901234567890);
已创建 1 行。
SQL> select * from o_test1;
VALUE
--------------------------------------------------
12345678901234567890123456789012345679000000000000
PostgreSQL double precision
postgres=# create table p_test1(value double precision);
CREATE TABLE
postgres=# insert into p_test1 values(12345678901234567890123456789012345678901234567890);
INSERT 0 1
postgres=# select * from p_test1;
value
-----------------------
1.23456789012346e+049
2.2、Binary_float类型
Binary_float类型,对应于PostgreSQL的real类型。
Binary_float的值在1.17549E-38F 至3.40282E+38F之间,对应于PostgreSQL的real类型。real类型的精度至少是6位小数。可以通过修改pg_settings的extra_float_digits参数的值,调整real的精度和Binary_float相符合。
Oracle Binary_Float
SQL> create table o_test1(col1 BINARY_float);
表已创建。
SQL> insert into o_test1 values (123456789012345678901234567890123456789);
已创建 1 行。
SQL> insert into o_test1 values (1234567890123456789012345678901234567890);
已创建 1 行。
SQL> select * from o_test1;
COL1
--------------------
1.23456786E+038
Inf
PostgreSQL real
postgres=# create table p_test1(value real);
CREATE TABLE
postgres=# insert into p_test1 values(123456789012345678901234567890123456789);
INSERT 0 1
postgres=# insert into p_test1 values(1234567890123456789012345678901234567890);
错误: 值超出范围: 上溢
postgres=# select * from p_test1;
value
--------------
1.23457e+038
2.3、Binary_double类型
Binary_double类型,对应于PostgreSQL的double precision类型。
Binary_double的值在2.22507485850720E-308 至 1.79769313486231E+308之间。对应的PostgreSQL的double precision的范围通常是1E-307到1E+308, 精度至少15位数字。
Oracle Binary_double
SQL> create table o_test1(col1 Binary_double);
表已创建。
SQL> insert into o_test1 values (12345678901234567890123456789012345678901234567890);
已创建 1 行。
SQL> select * from o_test1;
COL1
--------------------
1.2345678901235E+049
PostgreSQL double precision
postgres=# create table p_test1(value double precision);
CREATE TABLE
postgres=# insert into p_test1 values(12345678901234567890123456789012345678901234567890);
INSERT 0 1
postgres=# select * from p_test1;
value
-----------------------
1.23456789012346e+049
(1 行记录)
Oracle的数字类型向PostgreSQL的数据迁移过程中,只要根据Oracle的数据的精度,在PostgreSQL中选择相同或者更大精度的类型。数据就能够迁移过来。但为了转换过来后数据库的效率(特别是整数的时候),需要选择合适的数据类型,才能够完整、正确并且高效的完成Oracle数字类型向PostgreSQL数字类型的迁移。
参考文档:
PostgreSQL 9.4.4 中文手册:数值类型
http://www.postgres.cn/docs/9.4/datatype-numeric.html
Database SQL Language Reference:Data Types
http://docs.oracle.com/cd/E11882_01/server.112/e41084/sql_elements001.htm#i45441
--------------------------------------------------------------
三:日期时间类型
Oracle日期时间类型有两类,一类是日期时间类型,包括Date, Timestamp with time zone, Timestamp with local time zone。另一类是Interval类型。主要有Interval year to month 和Interval day to second两种。PostgreSQL也有类似的两类。其中的日期时间类型包括Timestamp with time zone, Timestamp without time zone, Date,Time with time zone , Time without time zone五种。Interval类型就是Interval。它们之间的对应关系是什么呢?
1、日期时间类型
1.1、Date类型
Oracle的Date类型包括年、月、日、时、分、秒六个字段, 时间跨度是公元前4712年1月1日~公元9999年12月31日。PostgreSQL中,可以使用Timestamp(0) without time zone来对应,时间跨度从公元前4713年~公元294276年。
所有Oracle的日期时间类型,时间跨度都是公元前4712年1月1日~公元9999年12月31日。而PostgreSQL的时间跨度远远超过Oracle的时间跨度。PostgreSQL中Timestamp的时间跨度远大于Oracle的,而Date类型的时间跨度比Timestamp的更大。所以在时间的极值方面不存在问题。
注意:PostgreSQL中的Date类型仅仅包含日期,不包括时、分、秒信息。因此不能使用Date类型来对应Oracle的Date类型。
Oracle Date
SQL> create table o_test(value date);
表已创建。
SQL> insert into o_test values(to_date('-4712-01-01 00:30:45', 'syyyy-mm-dd hh24:mi:ss'));
已创建 1 行。
SQL> insert into o_test values(to_date('-4712-01-01 00:30:45', 'syyyy-mm-dd hh24
:mi:ss') - interval '1' day);
insert into o_test values(to_date('-4712-01-01 00:30:45', 'syyyy-mm-dd hh24:mi:s
s') - interval '1' day)
第 1 行出现错误:
ORA-01841: (完整) 年份值必须介于 -4713 和 +9999 之间, 且不为 0
SQL> insert into o_test values(to_date('9999-12-31 12:30:45', 'yyyy-mm-dd hh24:mi:ss'));
已创建 1 行。
SQL> insert into o_test values(to_date('9999-12-31 12:30:45', 'yyyy-mm-dd hh24:m
i:ss') + interval '1' day);
insert into o_test values(to_date('9999-12-31 12:30:45', 'yyyy-mm-dd hh24:mi:ss'
) + interval '1' day)
第 1 行出现错误:
ORA-01841: (完整) 年份值必须介于 -4713 和 +9999 之间, 且不为 0
SQL> select to_char(value, 'syyyy/mm/dd hh24:mi:ss') from o_test;
TO_CHAR(VALUE,'SYYYY
--------------------
-4712/01/01 00:30:45
9999/12/31 12:30:45
PostgreSQL Timestamp(0) without time zone
postgres=# create table p_test1(value timestamp(0) without time zone);
CREATE TABLE
postgres=# insert into p_test1 values('4712-01-01 00:30:45BC');
INSERT 0 1
postgres=# insert into p_test1 values(to_timestamp('4712-01-01 00:30:45BC','yyyy-mm-dd hh24:mi:ssBC') - interval '1 day');
INSERT 0 1
postgres=# insert into p_test1 values('9999-12-31 23:59:59');
INSERT 0 1
postgres=# insert into p_test1 values(to_timestamp('9999-12-31 23:59:59','yyyy-mm-dd hh24:mi:ss') + interval '1 day');
INSERT 0 1
postgres=# select * from p_test1;
value
------------------------
4712-01-01 00:30:45 BC
4713-12-31 00:30:45 BC
9999-12-31 23:59:59
10000-01-01 23:59:59
(4 行记录)
1.2、Timestamp(p)类型
Oracle的Timestamp包含年、月、日、时、分、秒、毫秒。其中最后的毫秒的精度最大为9位,默认为6位。基本上等同于PostgresQL的Timestamp without time zone。后者同样包含年、月、日、时、分、秒、毫秒。最后的毫秒精度最大为6位,默认为6位。精度方面,PostgreSQL比Oracle稍差。但是实际应用中,毫秒的6位精度已经足够。
Oracle数据中的毫秒数如果小于精度,会在有效数据后自动以0补足位数。PostgreSQL的毫秒数如果小于精度,不会在末尾补0。
Oracle Timestamp 默认精度
SQL> create table o_test(value timestamp);
表已创建。
SQL> insert into o_test values(to_timestamp('2012-12-31 12:30:50.123456', 'yyyy-mm-dd hh24:mi:ss.ff6'));
已创建 1 行。
SQL> insert into o_test values(to_timestamp('2012-12-31 12:30:50.123456789', 'yyyy-mm-dd hh24:mi:ss.ff9'));
已创建 1 行。
SQL> insert into o_test values(to_timestamp('2012-12-31 12:30:50.123', 'yyyy-mm-dd hh24:mi:ss.ff'));
已创建 1 行。
SQL> select * from o_test;
VALUE
---------------------------------------------------------------------------
31-12月-12 12.30.50.123456 下午
31-12月-12 12.30.50.123457 下午
31-12月-12 12.30.50.123000 下午
PostgreSQL Timestamp without time zone 默认精度
postgres=# create table p_test1(value timestamp without time zone);
CREATE TABLE
postgres=# insert into p_test1 values('2012-12-31 12:30:45.123456');
INSERT 0 1
postgres=# insert into p_test1 values('2012-12-31 12:30:45.123456789');
INSERT 0 1
postgres=# insert into p_test1 values('2012-12-31 12:30:45.123');
INSERT 0 1
postgres=# select * from p_test1;
value
----------------------------
2012-12-31 12:30:45.123456
2012-12-31 12:30:45.123457
2012-12-31 12:30:45.123
(3 行记录)
Oracle Timestamp 指定精度
SQL> create table o_test1(value timestamp(9));
表已创建。
SQL> insert into o_test1 values(to_timestamp('2012-12-31 12:30:50.123456789', 'yyyy-mm-dd hh24:mi:ss.ff9'));
已创建 1 行。
SQL> insert into o_test1 values(to_timestamp('2012-12-31 12:30:50.123', 'yyyy-mm-dd hh24:mi:ss.ff'));
已创建 1 行。
SQL> select * from o_test1;
VALUE
---------------------------------------------------------------------------
31-12月-12 12.30.50.123456789 下午
31-12月-12 12.30.50.123000000 下午
PostgreSQL Timestamp without time zone 指定精度
postgres=# create table p_test2(value timestamp(3) without time zone);
CREATE TABLE
postgres=# insert into p_test2 values('2012-12-31 12:30:45.123456789');
INSERT 0 1
postgres=# insert into p_test2 values('2012-12-31 12:30:45.123');
INSERT 0 1
postgres=# insert into p_test2 values('2012-12-31 12:30:45.1');
INSERT 0 1
postgres=# select * from p_test2;
value
-------------------------
2012-12-31 12:30:45.123
2012-12-31 12:30:45.123
2012-12-31 12:30:45.1
(3 行记录)
1.3、Timestamp(p) with time zone类型
Oracle的Timestamp(p) with time zone类型,是在数据库的字段里面保存本字段相关的时区信息。功能方面基本上等同于PostgreSQL的Timestamp(p) with time zone。不同之处在于:Oracle会在数据库字段中存储数据所在的时区,而PostgreSQL却是把数据的时区自动转换成为数据库的时区(初始值由Postgresql.conf中的timezone定义,可以使用set timezone来进行更改)。
数据精度方面,和 1.2、Timestamp(p)类型 相同。
Oracle Timestamp with time zone
SQL> create table o_test( value timestamp with time zone);
表已创建。
SQL> insert into o_test values(systimestamp);
已创建 1 行。
SQL> insert into o_test values(to_timestamp_tz('2012-12-31 12:30:50.123456 +10:0
0', 'yyyy-mm-dd hh24:mi:ss.ff tzh:tzm'));
已创建 1 行。
SQL> select * from o_test;
VALUE
---------------------------------------------------------------------------
30-6月 -16 02.16.47.836000 下午 +08:00
31-12月-12 12.30.50.123456 下午 +10:00
PostgreSQL Timestamp(p) with time zone
postgres=# create table p_test1(value timestamp with time zone);
CREATE TABLE
postgres=# insert into p_test1 values(current_timestamp);
INSERT 0 1
postgres=# insert into p_test1 values('2012-12-31 12:30:50.123456+10');
INSERT 0 1
postgres=# insert into p_test1 values('2012-12-31 12:30:50.123456 PST');
INSERT 0 1
postgres=# select * from p_test1;
value
-------------------------------
2016-06-30 15:35:18.345153+08
2012-12-31 10:30:50.123456+08
2013-01-01 04:30:50.123456+08
(3 行记录)
1.4、Timestamp(p) with local time zone类型
Oracle的Timestamp(p) with local time zone类型,是在数据库的字段中不保存本字段的时区信息。而是使用数据库的time zone信息。当将数据写入数据库的时候,会自动将数据转换为数据库的时区写入。当取得数据的时候,会将数据转化为用户Session所在的时区进行显示。这种数据类型也对应于PostgreSQL的Timestamp with time zone。
Oracle Tiimestamp with local time zone
SQL> select sessiontimezone from dual;
SESSIONTIMEZONE
---------------------------------------------------------------------------
+08:00
SQL> create table o_test( value timestamp with local time zone);
表已创建。
SQL> insert into o_test values(systimestamp);
已创建 1 行。
SQL> insert into o_test values(to_timestamp_tz('2012-12-31 12:30:50.123456 +10:00', 'yyyy-mm-dd hh24:mi:ss:ff tzh:tzm'));
已创建 1 行。
SQL> select * from o_test;
VALUE
---------------------------------------------------------------------------
01-7月 -16 09.49.20.860000 上午
31-12月-12 10.30.50.123456 上午
SQL> alter session set TIME_ZONE='-5:00';
会话已更改。
SQL> select * from o_test;
VALUE
---------------------------------------------------------------------------
30-6月 -16 08.49.20.860000 下午
30-12月-12 09.30.50.123456 下午
PostgreSQL Timestamp(p) with time zone
postgres=# show timezone;
TimeZone
----------------
Asia/Hong_Kong
(1 行记录)
postgres=# create table p_test1(value timestamp with time zone);
CREATE TABLE
postgres=# insert into p_test1 values(current_timestamp);
INSERT 0 1
postgres=# insert into p_test1 values('2012-12-31 12:30:50.123456 +10:00');
INSERT 0 1
postgres=# select * from p_test1;
value
-------------------------------
2016-07-01 10:09:05.958953+08
2012-12-31 10:30:50.123456+08
(2 行记录)
postgres=# set timezone to '-5';
SET
postgres=# select * from p_test1;
value
-------------------------------
2016-06-30 21:09:05.958953-05
2012-12-30 21:30:50.123456-05
(2 行记录)
2、Interval类型
Oracle的Interval 类型表示时间的间隔。分为Interval year(p) to month 和 Interval day(p) to second两类。分别对应间隔为年月和 间隔为日、时、分、秒的情况。PostgreSQL的数据类型中,也有Interval 数据类型。
2.1、Interval year(p) to month类型
Oracle的Interval year(p) to month类型表示年月的时间间隔。year, month一起使用的时候,写法类似于Interval '12-11' year to month。其中的年份根据精度p来决定,p值为1~4,默认为2。月份只能是0~11之间的值。其实year, month都可以单独使用。单独使用时,年份同样是根据精度p来决定。PostgreSQL的Interval中,有类似的用法。而且用法比Oracle还灵活。
Oracle Interval year(p) to month
SQL> create table o_test(value interval year to month);
表已创建。
SQL> insert into o_test values(interval '100-5' year(3) to month);
insert into o_test values(interval '100-5' year(3) to month)
第 1 行出现错误:
ORA-01873: 间隔的前导精度太小
SQL> insert into o_test values(interval '10-5' year to month);
已创建 1 行。
SQL> insert into o_test values(interval '10-12' year to month);
insert into o_test values(interval '10-12' year to month)
第 1 行出现错误:
ORA-01843: 无效的月份
SQL> insert into o_test values(interval '30' year );
已创建 1 行。
SQL> insert into o_test values(interval '1199' month);
已创建 1 行。
SQL> select * from o_test;
VALUE
---------------------------------------------------------------------------
+10-05
+30-00
+99-11
PostgreSQL Interval
postgres=# create table p_test1(value interval);
CREATE TABLE
postgres=# insert into p_test1 values('100-5');
INSERT 0 1
postgres=# insert into p_test1 values('90-12');
错误: 间隔字段超出范围: "90-12"
第1行insert into p_test1 values('90-12');
^
postgres=# insert into p_test1 values('90-11');
INSERT 0 1
postgres=# insert into p_test1 values('90-0');
INSERT 0 1
postgres=# insert into p_test1 values('10000 year');
INSERT 0 1
postgres=# insert into p_test1 values('12000 month');
INSERT 0 1
postgres=# insert into p_test1 values('1000 year 12000 month');
INSERT 0 1
postgres=# select * from p_test1;
value
------------------
100 years 5 mons
90 years 11 mons
90 years
10000 years
1000 years
2000 years
(6 行记录)
2.2、Interval day(p) to second类型
Oracle的Interval day(p) to second类型表示日、时、分、秒的时间间隔。一起使用的时候,写法类似于Interval '12 5:6:7.8' day to second。其中的日期由精度p来决定,p值为0~9,默认为2。小时为0~23,分钟为0~59,秒为0~59。其实日、时、分、秒也都可以单独使用。单独使用时,日期,时、分、秒的值都可以使用精度p来控制。
PostgreSQL的Interval中,也有类似的用法,而且用法比Oracle灵活的多。
Oracle Interval day(p) to second
SQL> create table o_test(value interval day(3) to second);
表已创建。
SQL> insert into o_test values('1000 1:23:45');
insert into o_test values('1000 1:23:45')
第 1 行出现错误:
ORA-01873: 间隔的前导精度太小
SQL> insert into o_test values('999 1:23:45');
已创建 1 行。
SQL> insert into o_test values('100 24:50:45');
insert into o_test values('100 24:50:45')
第 1 行出现错误:
ORA-01850: 小时值必须介于 0 和 23 之间
SQL> insert into o_test values('100 23:60:45');
insert into o_test values('100 23:60:45')
第 1 行出现错误:
ORA-01851: 分钟值必须介于 0 和 59 之间
SQL> insert into o_test values('100 23:40:75');
insert into o_test values('100 23:40:75')
第 1 行出现错误:
ORA-01852: 秒值必须介于 0 和 59 之间
SQL> insert into o_test values(interval '2399' hour);
已创建 1 行。
SQL> insert into o_test values(interval '2400' hour);
insert into o_test values(interval '2400' hour)
第 1 行出现错误:
ORA-01873: 间隔的前导精度太小
SQL> insert into o_test values(interval '2400' hour(4));
已创建 1 行。
SQL> insert into o_test values(interval '800000' minute);
insert into o_test values(interval '800000' minute)
第 1 行出现错误:
ORA-01873: 间隔的前导精度太小
SQL> insert into o_test values(interval '800000' minute(6));
已创建 1 行。
SQL> insert into o_test values(interval '80000000' second);
insert into o_test values(interval '80000000' second)
第 1 行出现错误:
ORA-01873: 间隔的前导精度太小
SQL> insert into o_test values(interval '80000000' second(8));
已创建 1 行。
SQL> select * from o_test;
VALUE
---------------------------------------------------------------------------
+999 01:23:45.000000
+099 23:00:00.000000
+100 00:00:00.000000
+555 13:20:00.000000
+925 22:13:20.000000
PostgreSQL Interval
postgres=# create table p_test1(value interval);
CREATE TABLE
postgres=# insert into p_test1 values('1000 1:23:45');
INSERT 0 1
postgres=# insert into p_test1 values('100 50:23:45');
INSERT 0 1
postgres=# insert into p_test1 values('100 50:60:45');
错误: 间隔字段超出范围: "100 50:60:45"
第1行insert into p_test1 values('100 50:60:45');
^
postgres=# insert into p_test1 values('1000 day');
INSERT 0 1
postgres=# insert into p_test1 values('80000 hour');
INSERT 0 1
postgres=# insert into p_test1 values('8000000 minute');
INSERT 0 1
postgres=# insert into p_test1 values('800000000 second');
INSERT 0 1
postgres=# select * from p_test1;
value
--------------------
1000 days 01:23:45
100 days 50:23:45
1000 days
80000:00:00
133333:20:00
222222:13:20
(6 行记录)
Oracle的日期时间类型向PostgreSQL的数据迁移过程相对来说简单一些。由于PostgreSQL的数据类型的极值超越Oracle,因此,数据迁移过程中,只要根据Oracle的数据精度,在PostgreSQL中选择正确的数据类型。并留意一下二者SQL写法的不同,应该就能够完整正确的迁移过来。
参考文档:
PostgreSQL 9.4.4 中文手册:日期/时间类型
http://www.postgres.cn/docs/9.4/datatype-datetime.html
Database SQL Language Reference:Data Types
http://docs.oracle.com/cd/E11882_01/server.112/e41084/sql_elements001.htm#i45441
--------------------------------------------------------------
四:大数据类型
Oracle的大数据类型主要包括三类。分别是存储在数据库内部的类型,包括BLOB,CLOB,NCLOB。存储在外部的类型,就是BFILE。还有Oracle不推荐使用,仅用于特殊环境的类型。包括为了兼容老版本而使用的Long和为了数据在不同系统间移动而利用的Long raw类型。
PostgreSQL的大数据类型只有两种,就是存储二进制数据的bytea和存储字符类型的text。下面介绍一下它们之间的对应和迁移时的一些注意事项。
注意:PostgreSQL对应的大数据类型还有一个对象标识符类型(oid)。它是一个标识符,指向在pg_largeobject 系统表中的一个bytea类型的对象。由于它是用一个四字节的无符号整数实现,不能够提供大数据库范围内的唯一性保证。因此,postgreSQL不推荐使用oid类型。加上它的内部实现,也是使用bytea类型,所以就不单独介绍了。
1、存储在数据库内部的类型
1.1、BLOB类型
Oracle的Blob类型主要内容是二进制的大对象。最大长度是(4G-1)*database block size。在PostgreSQL中,与之对应的是bytea。最大长度是1G。虽然最大长度小于Blob,但是在实际应用中已经足够了。
Oracle BLOB
SQL> create table o_test(value blob);
表已创建。
SQL> insert into o_test values('867814324901abedf4314312');
已创建 1 行。
SQL> insert into o_test values('867814324901abedf4314312t');
insert into o_test values('867814324901abedf4314312t')
第 1 行出现错误:
ORA-01465: 无效的十六进制数字
SQL> select * from o_test;
VALUE
--------------------------------------------------------------------------------
867814324901ABEDF4314312
PostgreSQL bytea
postgres=# create table p_test(value bytea);
CREATE TABLE
postgres=# insert into p_test values('867814324901abedf4314312');
INSERT 0 1
postgres=# insert into p_test values('867814324901abedf4314312t');
INSERT 0 1
postgres=# select * from p_test;
value
------------------------------------------------------
\x383637383134333234393031616265646634333134333132
\x38363738313433323439303161626564663433313433313274
(2 行记录)
1.2、CLOB类型
Oracle的Clob类型,主要存储基于数据库字符集的单字节或多字节文本信息,最大长度是(4G-1)*database block size。PostgreSQL中,可以使用text来对应。text的最大长度是1G,比Oracle的小。但是,实际应用中,1G已经足够。
Oracle CLOB
SQL> create table o_test( value clob);
表已创建。
SQL> insert into o_test values('122334543543666345435313421');
已创建 1 行。
VALUE
--------------------------------------------------------------------------------
122334543543666345435313421
PostgreSQL text
postgres=# create table p_test(value text);
CREATE TABLE
postgres=# insert into p_test values('1234567890123');
INSERT 0 1
postgres=# select * from p_test;
value
---------------
1234567890123
(1 行记录)
1.3、NCLOB类型
Oracle的NClob类型,主要存储固定长度的UNICODE字符串,最大长度是(4G-1)*database block size。PostgreSQL中,可以使用text来对应。text的最大长度是1G,比Oracle的小。但在实际应用中,1G已经足够。
Oracle CLOB
SQL> create table o_test1(value NCLOB);
表已创建。
SQL> insert into o_test1 values('1223344452525341');
已创建 1 行。
SQL> select DBMS_LOB.GETLENGTH(value) from o_test1;
DBMS_LOB.GETLENGTH(VALUE)
-------------------------
16
SQL> select * from o_test1;
VALUE
--------------------------------------------------------------------------------
1223344452525341
PostgreSQL text
postgres=# create table p_test(value text);
CREATE TABLE
postgres=# insert into p_test values('1234567890123');
INSERT 0 1
postgres=# select * from p_test;
value
---------------
1234567890123
(1 行记录)
2、存储在数据库外部的类型
2.1、BFILE类型
存储在数据库外部的类型,就是指BFILE类型。这是一种特殊的数据类型。数据并不保存在数据库内。数据库内仅仅是一个文件的指针。指明了文件的路径和文件名。实际的文件存储在硬盘上。只能对文件进行读取,而且不能Commit和Rollback。Oracle需要对文件所在的路径有读权限。文件的大小理论上可以达到2^64-1字节。
PostgreSQL中,没有储存在外部的数据类型。可以使用bytea来对应BFILE类型。数据类型的最大长度,没有BFILE的大。但是实际应用中,bytea的大小已经足够了。
Oracle BFILE
SQL> create table o_test(value bfile);
表已创建。
SQL> begin
2 insert into o_test values(bfilename('BFILE1', '00.txt'));
3 end;
4 /
PL/SQL 过程已成功完成。
SQL> declare txt1 BFILE;
2 piece raw(100);
3 amount binary_integer :=100;
4 begin
5 select value into txt1 from o_test;
6 DBMS_LOB.OPEN(txt1);
7 DBMS_LOB.READ(txt1, amount, 1, piece);
8 DBMS_LOB.CLOSE(txt1);
9 DBMS_OUTPUT.PUT_LINE(RAWTOHEX(piece));
10 end;
11 /
313233343535363635343333
PL/SQL 过程已成功完成。
PostgreSQL bytea
postgres=# create table p_test(value bytea);
CREATE TABLE
postgres=# insert into p_test values('123455665433');
INSERT 0 1
postgres=# select * from p_test;
value
----------------------------
\x313233343535363635343333
(1 行记录)
3、Oracle不推荐使用的大数据类型
3.1、LONG RAW类型
Oracle的long raw主要用于不同的系统之间转移数据的时候所用,是Oracle不推荐使用的数据类型。Oracle推荐使用Blob来代替Long raw,并且Long raw的数据可以直接转换成Blob类型。Long raw的最大范围是2G。PostgreSQL中与之对应的就是bytea类型。
3.2、LONG类型
Oracle的Long类型是Oracle不推荐使用的一个数据类型。使用它有很多的限制。比如一个表中只能有1个字段,不能用在where条件中使用等等。Oracle保留它的目的只是为了兼容以前的版本。Long类型的最大范围是2^31 -1(即2G-1)个字节。
在PostgreSQL中,Long类型的对应字段是text。虽然最大字符数少于Long类型,但是,text的最大长度1G在实际应用中已经足够了。
Oracle LONG
SQL> create table o_test(value long);
表已创建。
SQL> insert into o_test values('12sdfadsfewr34qdfsdsvvatearaewra');
已创建 1 行。
SQL> select * from o_test;
VALUE
--------------------------------------------------------------------------------
12sdfadsfewr34qdfsdsvvatearaewra
SQL> select * from o_test where value ='12sdfadsfewr34qdfsdsvvatearaewra';
select * from o_test where value ='12sdfadsfewr34qdfsdsvvatearaewra'
第 1 行出现错误:
ORA-00997: 非法使用 LONG 数据类型
PostgreSQL text
postgres=# create table p_test(value text);
CREATE TABLE
postgres=# insert into p_test values('12sdfadsfewr34qdfsdsvvatearaewra');
INSERT 0 1
postgres=# select * from p_test;
value
----------------------------------
12sdfadsfewr34qdfsdsvvatearaewra
Oracle的大数据类型虽然多一些,但是PostgreSQL中基本上就是text和bytea两个,分别对应文本信息和二进制信息。迁移的时候,文本信息转成text,二进制信息转成bytea。特殊类型BFILE形式的,可以额外写一些代码把数据从文件中读出转换成bytea。这样就可以完成大数据类型的迁移。
参考文档:
PostgreSQL 9.4.4 中文手册:字符类型,二进制类型,对象标识符类型
http://www.postgres.cn/docs/9.4/datatype-character.html (字符类型)
http://www.postgres.cn/docs/9.4/datatype-binary.html (二进制类型)
http://www.postgres.cn/docs/9.4/datatype-oid.html (对象标识符类型)
Database SQL Language Reference:Data Types
http://docs.oracle.com/cd/E11882_01/server.112/e41084/sql_elements001.htm#i45441
--------------------------------------------------------------
五:其它类型
Oracle的内建数据类型中,还有一些其它的类型。不能够归类到字符、数字、时间和大数据类型等。比如RAW,ROWID,UROWID等。在数据迁移的时候,在PostgreSQL中也有与之相对应的类型。
1、RAW类型
Oracle的RAW类型主要用于不同的系统之间转移数据的时候。用于保存位串,以字节为单位。作为数据库列的数据类型的时候,最大位数为2000(作为变量的时候,最大位数为32767。因为现在谈论的是数据迁移,所以作为变量的情况就不讨论了)。
PostgreSQL中,可以使用bytea来对应RAW类型。
Oracle RAW
SQL> create table o_test(value raw(2000));
表已创建。
SQL> create table o_test2(value raw(2001));
create table o_test2(value raw(2001))
第 1 行出现错误:
ORA-00910: 指定的长度对于数据类型而言过长
SQL> insert into o_test values(utl_raw.cast_to_raw('Hello! This is John'));
已创建 1 行。
SQL> select * from o_test;
VALUE
--------------------------------------------------------------------------------
48656C6C6F212054686973206973204A6F686E
PostgreSQL bytea
postgres=# create table p_test(value bytea);
CREATE TABLE
postgres=# insert into p_test values(cast('Hello! This is John' as bytea));
INSERT 0 1
postgres=# select * from p_test;
value
------------------------------------------
\x48656c6c6f212054686973206973204a6f686e
(1 行记录)
2、ROWID类型
Oracle的ROWID类型代表一条记录在数据库中的物理地址。使用BASE64编码的字符串表示的物理地址。固定是18位。ROWID是Oracle中的伪列,也可以定义一个ROWID类型的列,但是必须用户自己保证该列的数据有效性。
PostgreSQL中,可以使用char(18)来迁移对象。PostgreSQL中还有一个CTID数据类型,指的是一条记录位于哪个数据块的哪个位移上面。作用和ROWID类似。但是用的是数字对,数据迁移时,不能够使用它对Oracle的ROWID进行迁移。
Oracle ROWID
SQL> create table o_test(value1 int, value2 rowid);
表已创建。
SQL> insert into o_test values(1, '1234567890ABCDEFAB');
已创建 1 行。
SQL> insert into o_test values(2, '1234567890ABCDEFABC');
insert into o_test values(2, '1234567890ABCDEFABC')
第 1 行出现错误:
ORA-01410: 无效的 ROWID
SQL> select * from o_test;
VALUE1 VALUE2
---------- ------------------
1 B23456AM9AABCDEFAB
SQL> select t.rowid, t.* from o_test t;
ROWID VALUE1 VALUE2
------------------ ---------- ------------------
AAASQiAAEAAAAJ8AAA 1 B23456AM9AABCDEFAB
PostgreSQL char(18)
postgres=# create table p_test(value1 int , value2 char(18));
CREATE TABLE
postgres=# insert into p_test values(1, 'B23456AM9AABCDEFAB');
INSERT 0 1
postgres=# insert into p_test values(1, 'AAASQiAAEAAAAJ8AAA');
INSERT 0 1
postgres=# select * from p_test;
value1 | value2
--------+--------------------
1 | B23456AM9AABCDEFAB
1 | AAASQiAAEAAAAJ8AAA
(2 行记录)
postgres=# select ctid, * from p_test;
ctid | value1 | value2
-------+--------+--------------------
(0,1) | 1 | B23456AM9AABCDEFAB
(0,2) | 1 | AAASQiAAEAAAAJ8AAA
(2 行记录)
3、UROWID类型
Oracle的UROWID类型支持Oracle的物理ROWID和逻辑ROWID,在索引组织表(Index Organization Table,简称为IOT)中的ROWID,就是逻辑ROWID。并且支持非Oracle数据库的表的ROWID。也就是支持所有类型的ROWID。最大长度是4000。
PostgreSQL中,可以使用varchar来迁移对象。
Oracle UROWID
SQL> create table o_test(value1 int, value2 urowid);
表已创建。
SQL> insert into o_test values(1,'AAASQiAAEAAAAJ8AAA');
已创建 1 行。
SQL> select value2 from o_test;
VALUE2
--------------------------------------------------------------------------------
AAASQiAAEAAAAJ8AAA
SQL> create table o_test2(id number primary key) organization index;
表已创建。
SQL> insert into o_test2 values('12345');
已创建 1 行。
SQL> insert into o_test2 values('12345989873827276839302');
已创建 1 行。
SQL> set numw 25
SQL> select t.rowid, t.* from o_test2 t;
ROWID ID
----------------------------------------- -------------------------
*BAEAAjMEwwIYLv4 12345
*BAEAAjMNzAIYLmNjSlNJTVReA/4 12345989873827276839302
PostgreSQL VARCHAR
postgres=# create table p_test(value varchar);
CREATE TABLE
postgres=# insert into p_test values('AAASQiAAEAAAAJ8AAA');
INSERT 0 1
postgres=# insert into p_test values('*BAEAAjMEwwIYLv4');
INSERT 0 1
postgres=# insert into p_test values('*BAEAAjMNzAIYLmNjSlNJTVReA/4');
INSERT 0 1
postgres=# select value,length(value) from p_test;
value | length
------------------------------+--------
AAASQiAAEAAAAJ8AAA | 18
*BAEAAjMEwwIYLv4 | 16
*BAEAAjMNzAIYLmNjSlNJTVReA/4 | 28
(3 行记录)
Oracle的build-in类型里面的这几种特殊的数据类型,在数据迁移中是比较简单的。在向PostgreSQL数据库进行数据迁移的时候只要选对数据类型,应该就可以正确的迁移过来。
参考文档:
PostgreSQL 9.4.4 中文手册:字符类型,二进制类型,对象标识符类型
http://www.postgres.cn/docs/9.4/datatype-binary.html (二进制类型)
http://www.postgres.cn/docs/9.4/datatype-character.html (字符类型)
Database SQL Language Reference:Data Types
http://docs.oracle.com/cd/E11882_01/server.112/e41084/sql_elements001.htm#i45441
--------------------------------------------------------------
一:字符类型
二:数字类型
三:日期时间类型
四:大数据类型
五:其它类型
本文总结息《卸甲笔记》,感谢原作者。
--------------------------------------------------------------
一:字符类型
PostgreSQL是世界上功能最强大的开源数据库,在国内得到了越来越多机构和开发者的青睐和应用。随着PostgreSQL的应用越来越广泛,Oracle向PostgreSQL数据库的数据迁移需求也越来越多。数据库之间数据迁移的时候,首先遇到的,并且也是最重要的,就是数据类型之间的转换。下面根据自己的理解和测试,写了一些数据类型之间的差异以及迁移时的注意事项的文章,不足之处,尚请多多指教。
字符类型
大家知道,Oracle有四种字符类型,分别是char,varchar2,nchar,nvarchar2。而PostgreSQL只有两种字符类型,分别是character (char)和 character varying(varchar)。它们之间的有什么异同呢?
1、数据最小单位
Oracle:char和varchar2的数据最小单位,默认值为BYTE,即字节数。可以修改为CHAR,则为字符。通过参数NLS_LENGTH_SEMANTICS来控制。
而Oracle的nchar和nvarchar2是针对特定字符集来的,数据最小单位根据字符集不同而不同。如字符集是AL16UTF16,就是2个字节,而字符集是UTF8,就是3个,通过参数NLS_NCHAR_CHARACTERSET来控制(我的Oracle的NLS_NCHAR_CHARACTERSET值就是AL16UTF16,一个字为2byte)。
当数据不够最大长度的时候,char和nchar写入/取出的是最大长度的字符串,超出实际长度的使用空格补齐。而varchar2和 nvarchar2写入/取出的是实际操作的字符串。
SQL> create table o_test(id int primary key,
2 name char(5),
3 n_name nchar(5),
4 addr varchar2(5),
5 n_addr nvarchar2(5));
表已创建。
SQL> insert into o_test values(1, 'li', 'li', 'BJ', 'BJ');
已创建 1 行。
SQL> select lengthb(name), lengthb(n_name), lengthb(addr),lengthb(n_addr)
2 from o_test;
LENGTHB(NAME) LENGTHB(N_NAME) LENGTHB(ADDR) LENGTHB(N_ADDR)
------------- --------------- ------------- ---------------
5 10 2 4
PostgreSQL:char和 varchar的数据最小单位都是字符(CHAR)。
当数据不够最大长度的时候,char类型的数据物理上用空白填充到指定的长度,并且以这种方式存储和显示。但是,填充的空白是无意义的。在取出来实际使用的时候,空白会自动被删除。VARCHAR类型的数据,会按照实际长度存储。最后的空白都是有意义的,不会被删除。
highgo=# create table p_test(id smallint primary key,
highgo(# name char(5),
highgo(# v_name varchar(5));
CREATE TABLE
highgo=# insert into p_test values(1,'赵 ', '赵老大 ');
INSERT 0 1
highgo=# select length(name), length(v_name) from p_test;
length | length
--------+--------
1 | 4
(1 行记录)
2、数据类型的最大长度
Oracle:char类型的最大长度是2000字节,varchar2是4000个字节。
nchar,nvarchar2类型的最大长度根据数据集不同而不同。最终的byte数不能超过2000和4000。如字符集为AL16UTF16,则nchar的为2000/2=1000,而nvarchar2则为4000/2=2000
SQL> create table o_test2(name char(2001));
create table o_test2(name char(2001))
第 1 行出现错误:
ORA-00910: 指定的长度对于数据类型而言过长
SQL> create table o_test2(name char(2000));
表已创建。
SQL> create table o_test3(name varchar2(4001));
create table o_test3(name varchar2(4001))
第 1 行出现错误:
ORA-00910: 指定的长度对于数据类型而言过长
SQL> create table o_test3(name varchar2(4000));
表已创建。
SQL> create table o_table5(name nchar(1001));
create table o_table5(name nchar(1001))
第 1 行出现错误:
ORA-00910: 指定的长度对于数据类型而言过长
SQL> create table o_table5(name nchar(1000));
表已创建。
SQL> create table o_table6(name nvarchar2(2001));
create table o_table6(name nvarchar2(2001))
第 1 行出现错误:
ORA-00910: 指定的长度对于数据类型而言过长
SQL> create table o_table6(name nvarchar2(2000));
表已创建。
PostgreSQL:理论最大数据长度是1G,默认值是10 1024 1024=10485760(具体的值由htup_details.h的MaxAttrSize决定)
highgo=# create table p_test3(name char(10485761));
错误: 类型 char 的长度不能超过 10485760
第1行create table p_test3(name char(10485761));
^
highgo=# create table p_test3(name char(10485760));
CREATE TABLE
highgo=# create table p_test4(name varchar(10485761));
错误: 类型 varchar 的长度不能超过 10485760
第1行create table p_test4(name varchar(10485761));
^
highgo=# create table p_test4(name varchar(10485760));
CREATE TABLE
3、定义数据时的参数(字符数)
Oracle:char类型,不带字符数的时候,是1个字符(char(1)),而varchar2必须有字符数。
nchar和nvarchar2分别与char,varchar2类似。
SQL> create table o_test1(name char, addr varchar);
create table o_test1(name char, addr varchar)
第 1 行出现错误:
ORA-00906: 缺失左括号
SQL> create table o_test1(name char, addr varchar(5));
表已创建。
SQL> insert into o_test1 values('12','OK');
insert into o_test1 values('12','OK')
第 1 行出现错误:
ORA-12899: 列 "SCOTT"."O_TEST1"."NAME" 的值太大 (实际值: 2, 最大值: 1)
SQL> insert into o_test1 values('1','OK');
已创建 1 行。
PostgreSQL:char类型,不带字符数的时候,也是默认一个字符(char(1)),而varchar不带字符数的时候,没有限制。
highgo=# create table p_test1(name char, addr varchar);
CREATE TABLE
highgo=# insert into p_test1 values('12', '1234567890');
错误: 对于字符类型来说这个值太长了(1)
highgo=# insert into p_test1 values('1', '1234567890');
INSERT 0 1
4、插入数据库的时候的,字符数超出最大长度部分的处理。
Oracle:报错。即使超出的部分是字符末的空格。
SQL> insert into o_test1 values('1','123456');
insert into o_test1 values('1','123456')
第 1 行出现错误:
ORA-12899: 列 "SCOTT"."O_TEST1"."ADDR" 的值太大 (实际值: 6, 最大值: 5)
SQL> insert into o_test1 values('1','1234 ');
insert into o_test1 values('1','1234 ')
第 1 行出现错误:
ORA-12899: 列 "SCOTT"."O_TEST1"."ADDR" 的值太大 (实际值: 6, 最大值: 5)
PostgreSQL:当超出部分是有效的字符的时候,报错。
当是最后的空格时,截断为最大长度,不报错。但是当使用明确的类型转换为最大字符数的时候,截断为最大长度,不报错。
highgo=# create table p_test2(name char(2), addr varchar(5));
CREATE TABLE
highgo=# insert into p_test2 values('123', '12345');
错误:对于字符类型来说这个值太长了(2)
highgo=# insert into p_test2 values('12 ', '12345');
INSERT 0 1
highgo=# insert into p_test2 values(102 :: char(2) , '12345');
INSERT 0 1
highgo=# select * from p_test2;
name | addr
------+-------
12 | 12345
10 | 12345
(2 行记录)
字符类型数据迁移过程中,应该先根据Oracle数据的字符集和数据长度,从类型和长度上保证对方的数据能够完全的转换过来。再注意一些转换过程中SQL的写法,字符型的数据基本上就能够完全的、正确的迁移到PostgreSQL数据库中来了。
参考文档:
PostgreSQL 9.4.4 中文手册:8.3.字符类型
http://www.postgres.cn/docs/9.4/datatype-character.html
Database SQL Language Reference:Oracle Built-in Data Types
http://docs.oracle.com/cd/E11882_01/server.112/e41084/sql_elements001.htm#SQLRF30020
--------------------------------------------------------------
二:数字类型
数字类型
Oracle内建的数字类型有四种,分别是number,float,binary_float,binary_double。由于和其余数据库的数据兼容,而产生了smallint,int,Integer,Decimal等多种数字类型,其实内部都是使用内建的四种实现的。因此我们只讨论内建的四种数据类型。
PostgreSQL的数字类型有三类。分别是 整数类型,包括smallint,integer和bigint。任意精度类型,包括numeric和decimal两种(其中decimal等同于numeric),以及浮点数类型,包括real和double。它们之间的区别和迁移时应该注意什么呢?下面按照精确类型(整数类型和任意精度类型)和非精确类型(浮点数类型)两类来进行讨论。
1、精确类型(整数和任意精度数值)
Oracle的number(p, s) 类型,根据精度(p)和小数位数(s)的不同,分别对应PostgreSQL中的多种情况。
1.1、当小数位数(s)=0时
当小数位数(s)=0时,可以对应PostgreSQL的smallint, integer, bigint, numeric(p)等多种数据类型。由于smallint,Integer,bigint的算术运算效率比numberic高的多,所以迁移时建议如下处理:
1.1.1、当p <= 4时,使用integer或者smallint
当p <= 4时,可以使用smallint和Integer,推荐使用Integer,因为它在取值范围、存储空间、性能之间最为平衡。只有磁盘空间紧张的情况下,才建议使用smallint。
Oracle number(p, 0) p <=4
SQL> create table o_test1( value number(4,0));
表已创建。
SQL> insert into o_test1 values(99999);
insert into o_test1 values(99999)
第 1 行出现错误:
ORA-01438: 值大于为此列指定的允许精度
SQL> insert into o_test1 values(9999);
已创建 1 行。
PostgreSQL smallint
postgres=# create table p_test1(value smallint);
CREATE TABLE
postgres=# insert into p_test1 values(99999);
错误: smallint 超出范围
postgres=# insert into p_test1 values(9999);
INSERT 0 1
1.1.2、当4 < p <= 9时,应该使用integer
Oracle number(p, 0) 4 < p <= 9
SQL> create table o_test1(value number(9,0));
表已创建。
SQL> insert into o_test1 values(9999999999);
insert into o_test1 values(9999999999)
第 1 行出现错误:
ORA-01438: 值大于为此列指定的允许精度
SQL> insert into o_test1 values(999999999);
已创建 1 行。
PostgreSQL integer
postgres=# create table p_test1(value integer);
CREATE TABLE
postgres=# insert into p_test1 values(9999999999);
错误: 整数超出范围
postgres=# insert into p_test1 values(999999999);
INSERT 0 1
1.1.3、当9 < p <= 18时,使用bigint
当9 < p <= 18时,这个时候integer的精度已经不够了,应该使用bigint
Oracle number(p, 0) 9 < p <= 18
SQL> create table o_test1(value number(18,0));
表已创建。
SQL> insert into o_test1 values(9999999999999999999);
insert into o_test1 values(9999999999999999999)
第 1 行出现错误:
ORA-01438: 值大于为此列指定的允许精度
SQL> insert into o_test1 values(999999999999999999);
已创建 1 行。
PostgreSQL bigint
postgres=# create table p_test1 (value bigint);
CREATE TABLE
postgres=# insert into p_test1 values(9999999999999999999);
错误: bigint 超出范围
postgres=# insert into p_test1 values(999999999999999999);
INSERT 0 1
1.1.4、当p > 18时,使用numeric(p, 0)
当p>18的时候,需要使用numeric(p, 0)。才能保证数据转换的精度。
Oracle number(p, 0) p > 18
SQL> create table o_test1(value number(19,0));
表已创建。
SQL> insert into o_test1 values(9999999999999999999);
已创建 1 行。
PostgreSQL numeric(19, 0)
postgres=# create table p_test1 (value numeric(19, 0));
CREATE TABLE
postgres=# insert into p_test1 values(9999999999999999999);
INSERT 0 1
1.2、当小数位数(s) > 0时
当小数位数(s) > 0时,由于有小数,只能够使用numeric(p, s)。
1.2.1、当0 < s <=p的时候,使用numeric(p, s)
Oracle number(p, s) 0 < s <= p
SQL> create table o_test1(value number(5,3));
表已创建。
SQL> insert into o_test1 values(123.567);
insert into o_test1 values(123.567)
第 1 行出现错误:
ORA-01438: 值大于为此列指定的允许精度
SQL> insert into o_test1 values(12.56789);
已创建 1 行。
SQL> select * from o_test1;
VALUE
----------
12.568
PostgreSQL numeric(p, s) p >= s
postgres=# create table p_test1( value numeric(5, 3));
CREATE TABLE
postgres=# insert into p_test1 values(123.456);
错误: 数字字段溢出
描述: 精度为5,范围是3的字段必须四舍五入到小于10^2的绝对值.
postgres=# insert into p_test1 values(12.456789);
INSERT 0 1
postgres=# select * from p_test1;
value
--------
12.457
(1 行记录)
1.2.2、当 p < s的时候,使用numeric(s, s)
当 p < s的时候,使用numeric(s, s)。此时,Oracle可以控制小数点后0的个数,而PostgreSQL不可。由于数据迁移,只需要把数据按照原来的样式迁移过来即可,所以不存在问题。
Oracle number(p, s) s > p
SQL> create table o_test1(value number(3,5));
表已创建。
SQL> insert into o_test1 values(0.056789);
insert into o_test1 values(0.056789)
第 1 行出现错误:
ORA-01438: 值大于为此列指定的允许精度
SQL> insert into o_test1 values(0.0056789);
已创建 1 行。
SQL> select * from o_test1;
VALUE
----------
.00568
PostgreSQL numeric(p, s) s > p
postgres=# create table p_test1( value numeric(5, 5));
CREATE TABLE
postgres=# insert into p_test1 values(0.056789);
INSERT 0 1
postgres=# insert into p_test1 values(0.0056789);
INSERT 0 1
postgres=# select * from p_test1;
value
---------
0.05679
0.00568
(2 行记录)
1.3、当小数位数(s) < 0时
当小数位数(s) < 0的时候,由于此时p代表精度的位数,s位决定了在何处进行位数的舍入。s < 0的时候,这种根据s来进行数据舍入的功能PostgreSQL尚不具备,但由于数据迁移时只要能将Oracle的数据完全转换过来就行了。所以对于数据迁移来说也不存在问题。
PostgreSQL中,小数位数必须大于等于0。此时的迁移,需要把p + |s| 作为数据的精度来判断。其余和s=0的时候相同。
1.3.1、当p + |s| <= 4时,使用integer或者smallint
当p+ |s| <= 4时,可以使用smallint和Integer,推荐使用Integer,只有磁盘空间紧张的情况下,才建议使用smallInt。
Oracle number(p, s) p+ |s| <= 4
SQL> create table o_test1( value number(2,-2));
表已创建。
SQL> insert into o_test1 values(9950);
insert into o_test1 values(9950)
*
第 1 行出现错误:
ORA-01438: 值大于为此列指定的允许精度
SQL> insert into o_test1 values(9940);
已创建 1 行。
SQL> select * from o_test1;
VALUE
----------
9900
PostgreSQL smallint
postgres=# create table p_test1( value smallint);
CREATE TABLE
postgres=# insert into p_test1 values(9950);
INSERT 0 1
postgres=# insert into p_test1 values(9940);
INSERT 0 1
postgres=# select * from p_test1;
value
-------
9950
9940
(2 行记录)
1.3.2、当4 < p+ |s| <= 9时,使用Integer
当4 < p+ |s| <= 9时,应该使用Integer。
Oracle number(p, s) 4 < p+ |s| <= 9
SQL> create table o_test1( value number(6,-3));
表已创建。
SQL> insert into o_test1 values(999999500);
insert into o_test1 values(999999500)
第 1 行出现错误:
ORA-01438: 值大于为此列指定的允许精度
SQL> insert into o_test1 values(999999400);
已创建 1 行。
SQL> select * from o_test1;
VALUE
----------
999999000
PostgreSQL integer
postgres=# create table p_test1( value integer);
CREATE TABLE
postgres=# insert into p_test1 values(999999500);
INSERT 0 1
postgres=# insert into p_test1 values(999999400);
INSERT 0 1
postgres=# select * from p_test1;
value
-----------
999999500
999999400
(2 行记录)
1.3.3、当9 < p+ |s| <= 18时,使用bigint
当9 < p+ |s| <= 18时,这个时候Integer的精度已经不够了,可以使用bigint。
Oracle 9 < p+ |s| <= 18
SQL> create table o_test1( value number(12,-6));
表已创建。
SQL> insert into o_test1 values(999999999999500000);
insert into o_test1 values(999999999999500000)
第 1 行出现错误:
ORA-01438: 值大于为此列指定的允许精度
SQL> insert into o_test1 values(999999999999400000);
已创建 1 行。
PostgreSQL bigint
postgres=# create table p_test1( value bigint);
CREATE TABLE
postgres=# insert into p_test1 values(999999999999500000);
INSERT 0 1
postgres=# insert into p_test1 values(999999999999400000);
INSERT 0 1
postgres=# select * from p_test1;
value
--------------------
999999999999500000
999999999999400000
(2 行记录)
1.3.4、当p+ |s| >18时,使用numeric(p + |s|, 0)
当p+ |s| >18的时候,只能使用numeric(p + |s|, 0)。
Oracle p+ |s| > 18
SQL> create table o_test1( value number(15,-6));
表已创建。
SQL> insert into o_test1 values(999999999999999500000);
insert into o_test1 values(999999999999999500000)
第 1 行出现错误:
ORA-01438: 值大于为此列指定的允许精度
SQL> insert into o_test1 values(999999999999999400000);
已创建 1 行。
SQL> select * from o_test1;
VALUE
------------------------------
999999999999999000000
PostgreSQL numeric(p + |s|)
postgres=# create table p_test1( value numeric(21));
CREATE TABLE
postgres=# insert into p_test1 values(999999999999999500000);
INSERT 0 1
postgres=# insert into p_test1 values(999999999999999400000);
INSERT 0 1
postgres=# select * from p_test1;
value
-----------------------
999999999999999500000
999999999999999400000
2、非精确类型(浮点数类型)
浮点数是不精确的、变精度的数字类型。由于有下层处理器、操作系统和编译器对它的支持,所以很多情况下处理速度会快的多。但是由于只是以近似值存储的,对于想得到精确值的情况,不可以使用。
Oracle的float类型包括float[(p)], Binary_float, Binary_double三种类型。基本上和PostgreSQL的real和double precision相对应。PostgreSQL也提供了float(p)类型,但是和real和double precision是基本相同的,所以此处只讨论real和double precision两种。
2.1、Oracle float(p)
Oracle的float类型,根据值的范围区间,可以简单认为在1E-37 ~1E+37之间时,和PostgreSQL的real对应。超出这个区间的时候,则和PostgreSQL的double precision对应。但是精度方面,PostgreSQL尚达不到Oracle的float能达到的精度。
Oracle的 float的精度p是用二进制的1-126表示,若转化为10进制,则需要乘以0.30103。所以,大致相当于1-126*0.30103 ~ 1- 38。而PostgreSQL的real精度是6位,而double precision精度大概是在15位,可以通过修改pg_settings的extra_float_digits参数的值增加-15~3位的精度。但还支持不到更大的位数。但在实际应用中,不会有人把浮点数放到那么大的精度。PostgreSQL默认的精度已经足够。
Oracle float(p)
SQL> create table o_test1( value float(126));
表已创建。
SQL> insert into o_test1 values(12345678901234567890123456789012345678901234567890);
已创建 1 行。
SQL> select * from o_test1;
VALUE
--------------------------------------------------
12345678901234567890123456789012345679000000000000
PostgreSQL double precision
postgres=# create table p_test1(value double precision);
CREATE TABLE
postgres=# insert into p_test1 values(12345678901234567890123456789012345678901234567890);
INSERT 0 1
postgres=# select * from p_test1;
value
-----------------------
1.23456789012346e+049
2.2、Binary_float类型
Binary_float类型,对应于PostgreSQL的real类型。
Binary_float的值在1.17549E-38F 至3.40282E+38F之间,对应于PostgreSQL的real类型。real类型的精度至少是6位小数。可以通过修改pg_settings的extra_float_digits参数的值,调整real的精度和Binary_float相符合。
Oracle Binary_Float
SQL> create table o_test1(col1 BINARY_float);
表已创建。
SQL> insert into o_test1 values (123456789012345678901234567890123456789);
已创建 1 行。
SQL> insert into o_test1 values (1234567890123456789012345678901234567890);
已创建 1 行。
SQL> select * from o_test1;
COL1
--------------------
1.23456786E+038
Inf
PostgreSQL real
postgres=# create table p_test1(value real);
CREATE TABLE
postgres=# insert into p_test1 values(123456789012345678901234567890123456789);
INSERT 0 1
postgres=# insert into p_test1 values(1234567890123456789012345678901234567890);
错误: 值超出范围: 上溢
postgres=# select * from p_test1;
value
--------------
1.23457e+038
2.3、Binary_double类型
Binary_double类型,对应于PostgreSQL的double precision类型。
Binary_double的值在2.22507485850720E-308 至 1.79769313486231E+308之间。对应的PostgreSQL的double precision的范围通常是1E-307到1E+308, 精度至少15位数字。
Oracle Binary_double
SQL> create table o_test1(col1 Binary_double);
表已创建。
SQL> insert into o_test1 values (12345678901234567890123456789012345678901234567890);
已创建 1 行。
SQL> select * from o_test1;
COL1
--------------------
1.2345678901235E+049
PostgreSQL double precision
postgres=# create table p_test1(value double precision);
CREATE TABLE
postgres=# insert into p_test1 values(12345678901234567890123456789012345678901234567890);
INSERT 0 1
postgres=# select * from p_test1;
value
-----------------------
1.23456789012346e+049
(1 行记录)
Oracle的数字类型向PostgreSQL的数据迁移过程中,只要根据Oracle的数据的精度,在PostgreSQL中选择相同或者更大精度的类型。数据就能够迁移过来。但为了转换过来后数据库的效率(特别是整数的时候),需要选择合适的数据类型,才能够完整、正确并且高效的完成Oracle数字类型向PostgreSQL数字类型的迁移。
参考文档:
PostgreSQL 9.4.4 中文手册:数值类型
http://www.postgres.cn/docs/9.4/datatype-numeric.html
Database SQL Language Reference:Data Types
http://docs.oracle.com/cd/E11882_01/server.112/e41084/sql_elements001.htm#i45441
--------------------------------------------------------------
三:日期时间类型
Oracle日期时间类型有两类,一类是日期时间类型,包括Date, Timestamp with time zone, Timestamp with local time zone。另一类是Interval类型。主要有Interval year to month 和Interval day to second两种。PostgreSQL也有类似的两类。其中的日期时间类型包括Timestamp with time zone, Timestamp without time zone, Date,Time with time zone , Time without time zone五种。Interval类型就是Interval。它们之间的对应关系是什么呢?
1、日期时间类型
1.1、Date类型
Oracle的Date类型包括年、月、日、时、分、秒六个字段, 时间跨度是公元前4712年1月1日~公元9999年12月31日。PostgreSQL中,可以使用Timestamp(0) without time zone来对应,时间跨度从公元前4713年~公元294276年。
所有Oracle的日期时间类型,时间跨度都是公元前4712年1月1日~公元9999年12月31日。而PostgreSQL的时间跨度远远超过Oracle的时间跨度。PostgreSQL中Timestamp的时间跨度远大于Oracle的,而Date类型的时间跨度比Timestamp的更大。所以在时间的极值方面不存在问题。
注意:PostgreSQL中的Date类型仅仅包含日期,不包括时、分、秒信息。因此不能使用Date类型来对应Oracle的Date类型。
Oracle Date
SQL> create table o_test(value date);
表已创建。
SQL> insert into o_test values(to_date('-4712-01-01 00:30:45', 'syyyy-mm-dd hh24:mi:ss'));
已创建 1 行。
SQL> insert into o_test values(to_date('-4712-01-01 00:30:45', 'syyyy-mm-dd hh24
:mi:ss') - interval '1' day);
insert into o_test values(to_date('-4712-01-01 00:30:45', 'syyyy-mm-dd hh24:mi:s
s') - interval '1' day)
第 1 行出现错误:
ORA-01841: (完整) 年份值必须介于 -4713 和 +9999 之间, 且不为 0
SQL> insert into o_test values(to_date('9999-12-31 12:30:45', 'yyyy-mm-dd hh24:mi:ss'));
已创建 1 行。
SQL> insert into o_test values(to_date('9999-12-31 12:30:45', 'yyyy-mm-dd hh24:m
i:ss') + interval '1' day);
insert into o_test values(to_date('9999-12-31 12:30:45', 'yyyy-mm-dd hh24:mi:ss'
) + interval '1' day)
第 1 行出现错误:
ORA-01841: (完整) 年份值必须介于 -4713 和 +9999 之间, 且不为 0
SQL> select to_char(value, 'syyyy/mm/dd hh24:mi:ss') from o_test;
TO_CHAR(VALUE,'SYYYY
--------------------
-4712/01/01 00:30:45
9999/12/31 12:30:45
PostgreSQL Timestamp(0) without time zone
postgres=# create table p_test1(value timestamp(0) without time zone);
CREATE TABLE
postgres=# insert into p_test1 values('4712-01-01 00:30:45BC');
INSERT 0 1
postgres=# insert into p_test1 values(to_timestamp('4712-01-01 00:30:45BC','yyyy-mm-dd hh24:mi:ssBC') - interval '1 day');
INSERT 0 1
postgres=# insert into p_test1 values('9999-12-31 23:59:59');
INSERT 0 1
postgres=# insert into p_test1 values(to_timestamp('9999-12-31 23:59:59','yyyy-mm-dd hh24:mi:ss') + interval '1 day');
INSERT 0 1
postgres=# select * from p_test1;
value
------------------------
4712-01-01 00:30:45 BC
4713-12-31 00:30:45 BC
9999-12-31 23:59:59
10000-01-01 23:59:59
(4 行记录)
1.2、Timestamp(p)类型
Oracle的Timestamp包含年、月、日、时、分、秒、毫秒。其中最后的毫秒的精度最大为9位,默认为6位。基本上等同于PostgresQL的Timestamp without time zone。后者同样包含年、月、日、时、分、秒、毫秒。最后的毫秒精度最大为6位,默认为6位。精度方面,PostgreSQL比Oracle稍差。但是实际应用中,毫秒的6位精度已经足够。
Oracle数据中的毫秒数如果小于精度,会在有效数据后自动以0补足位数。PostgreSQL的毫秒数如果小于精度,不会在末尾补0。
Oracle Timestamp 默认精度
SQL> create table o_test(value timestamp);
表已创建。
SQL> insert into o_test values(to_timestamp('2012-12-31 12:30:50.123456', 'yyyy-mm-dd hh24:mi:ss.ff6'));
已创建 1 行。
SQL> insert into o_test values(to_timestamp('2012-12-31 12:30:50.123456789', 'yyyy-mm-dd hh24:mi:ss.ff9'));
已创建 1 行。
SQL> insert into o_test values(to_timestamp('2012-12-31 12:30:50.123', 'yyyy-mm-dd hh24:mi:ss.ff'));
已创建 1 行。
SQL> select * from o_test;
VALUE
---------------------------------------------------------------------------
31-12月-12 12.30.50.123456 下午
31-12月-12 12.30.50.123457 下午
31-12月-12 12.30.50.123000 下午
PostgreSQL Timestamp without time zone 默认精度
postgres=# create table p_test1(value timestamp without time zone);
CREATE TABLE
postgres=# insert into p_test1 values('2012-12-31 12:30:45.123456');
INSERT 0 1
postgres=# insert into p_test1 values('2012-12-31 12:30:45.123456789');
INSERT 0 1
postgres=# insert into p_test1 values('2012-12-31 12:30:45.123');
INSERT 0 1
postgres=# select * from p_test1;
value
----------------------------
2012-12-31 12:30:45.123456
2012-12-31 12:30:45.123457
2012-12-31 12:30:45.123
(3 行记录)
Oracle Timestamp 指定精度
SQL> create table o_test1(value timestamp(9));
表已创建。
SQL> insert into o_test1 values(to_timestamp('2012-12-31 12:30:50.123456789', 'yyyy-mm-dd hh24:mi:ss.ff9'));
已创建 1 行。
SQL> insert into o_test1 values(to_timestamp('2012-12-31 12:30:50.123', 'yyyy-mm-dd hh24:mi:ss.ff'));
已创建 1 行。
SQL> select * from o_test1;
VALUE
---------------------------------------------------------------------------
31-12月-12 12.30.50.123456789 下午
31-12月-12 12.30.50.123000000 下午
PostgreSQL Timestamp without time zone 指定精度
postgres=# create table p_test2(value timestamp(3) without time zone);
CREATE TABLE
postgres=# insert into p_test2 values('2012-12-31 12:30:45.123456789');
INSERT 0 1
postgres=# insert into p_test2 values('2012-12-31 12:30:45.123');
INSERT 0 1
postgres=# insert into p_test2 values('2012-12-31 12:30:45.1');
INSERT 0 1
postgres=# select * from p_test2;
value
-------------------------
2012-12-31 12:30:45.123
2012-12-31 12:30:45.123
2012-12-31 12:30:45.1
(3 行记录)
1.3、Timestamp(p) with time zone类型
Oracle的Timestamp(p) with time zone类型,是在数据库的字段里面保存本字段相关的时区信息。功能方面基本上等同于PostgreSQL的Timestamp(p) with time zone。不同之处在于:Oracle会在数据库字段中存储数据所在的时区,而PostgreSQL却是把数据的时区自动转换成为数据库的时区(初始值由Postgresql.conf中的timezone定义,可以使用set timezone来进行更改)。
数据精度方面,和 1.2、Timestamp(p)类型 相同。
Oracle Timestamp with time zone
SQL> create table o_test( value timestamp with time zone);
表已创建。
SQL> insert into o_test values(systimestamp);
已创建 1 行。
SQL> insert into o_test values(to_timestamp_tz('2012-12-31 12:30:50.123456 +10:0
0', 'yyyy-mm-dd hh24:mi:ss.ff tzh:tzm'));
已创建 1 行。
SQL> select * from o_test;
VALUE
---------------------------------------------------------------------------
30-6月 -16 02.16.47.836000 下午 +08:00
31-12月-12 12.30.50.123456 下午 +10:00
PostgreSQL Timestamp(p) with time zone
postgres=# create table p_test1(value timestamp with time zone);
CREATE TABLE
postgres=# insert into p_test1 values(current_timestamp);
INSERT 0 1
postgres=# insert into p_test1 values('2012-12-31 12:30:50.123456+10');
INSERT 0 1
postgres=# insert into p_test1 values('2012-12-31 12:30:50.123456 PST');
INSERT 0 1
postgres=# select * from p_test1;
value
-------------------------------
2016-06-30 15:35:18.345153+08
2012-12-31 10:30:50.123456+08
2013-01-01 04:30:50.123456+08
(3 行记录)
1.4、Timestamp(p) with local time zone类型
Oracle的Timestamp(p) with local time zone类型,是在数据库的字段中不保存本字段的时区信息。而是使用数据库的time zone信息。当将数据写入数据库的时候,会自动将数据转换为数据库的时区写入。当取得数据的时候,会将数据转化为用户Session所在的时区进行显示。这种数据类型也对应于PostgreSQL的Timestamp with time zone。
Oracle Tiimestamp with local time zone
SQL> select sessiontimezone from dual;
SESSIONTIMEZONE
---------------------------------------------------------------------------
+08:00
SQL> create table o_test( value timestamp with local time zone);
表已创建。
SQL> insert into o_test values(systimestamp);
已创建 1 行。
SQL> insert into o_test values(to_timestamp_tz('2012-12-31 12:30:50.123456 +10:00', 'yyyy-mm-dd hh24:mi:ss:ff tzh:tzm'));
已创建 1 行。
SQL> select * from o_test;
VALUE
---------------------------------------------------------------------------
01-7月 -16 09.49.20.860000 上午
31-12月-12 10.30.50.123456 上午
SQL> alter session set TIME_ZONE='-5:00';
会话已更改。
SQL> select * from o_test;
VALUE
---------------------------------------------------------------------------
30-6月 -16 08.49.20.860000 下午
30-12月-12 09.30.50.123456 下午
PostgreSQL Timestamp(p) with time zone
postgres=# show timezone;
TimeZone
----------------
Asia/Hong_Kong
(1 行记录)
postgres=# create table p_test1(value timestamp with time zone);
CREATE TABLE
postgres=# insert into p_test1 values(current_timestamp);
INSERT 0 1
postgres=# insert into p_test1 values('2012-12-31 12:30:50.123456 +10:00');
INSERT 0 1
postgres=# select * from p_test1;
value
-------------------------------
2016-07-01 10:09:05.958953+08
2012-12-31 10:30:50.123456+08
(2 行记录)
postgres=# set timezone to '-5';
SET
postgres=# select * from p_test1;
value
-------------------------------
2016-06-30 21:09:05.958953-05
2012-12-30 21:30:50.123456-05
(2 行记录)
2、Interval类型
Oracle的Interval 类型表示时间的间隔。分为Interval year(p) to month 和 Interval day(p) to second两类。分别对应间隔为年月和 间隔为日、时、分、秒的情况。PostgreSQL的数据类型中,也有Interval 数据类型。
2.1、Interval year(p) to month类型
Oracle的Interval year(p) to month类型表示年月的时间间隔。year, month一起使用的时候,写法类似于Interval '12-11' year to month。其中的年份根据精度p来决定,p值为1~4,默认为2。月份只能是0~11之间的值。其实year, month都可以单独使用。单独使用时,年份同样是根据精度p来决定。PostgreSQL的Interval中,有类似的用法。而且用法比Oracle还灵活。
Oracle Interval year(p) to month
SQL> create table o_test(value interval year to month);
表已创建。
SQL> insert into o_test values(interval '100-5' year(3) to month);
insert into o_test values(interval '100-5' year(3) to month)
第 1 行出现错误:
ORA-01873: 间隔的前导精度太小
SQL> insert into o_test values(interval '10-5' year to month);
已创建 1 行。
SQL> insert into o_test values(interval '10-12' year to month);
insert into o_test values(interval '10-12' year to month)
第 1 行出现错误:
ORA-01843: 无效的月份
SQL> insert into o_test values(interval '30' year );
已创建 1 行。
SQL> insert into o_test values(interval '1199' month);
已创建 1 行。
SQL> select * from o_test;
VALUE
---------------------------------------------------------------------------
+10-05
+30-00
+99-11
PostgreSQL Interval
postgres=# create table p_test1(value interval);
CREATE TABLE
postgres=# insert into p_test1 values('100-5');
INSERT 0 1
postgres=# insert into p_test1 values('90-12');
错误: 间隔字段超出范围: "90-12"
第1行insert into p_test1 values('90-12');
^
postgres=# insert into p_test1 values('90-11');
INSERT 0 1
postgres=# insert into p_test1 values('90-0');
INSERT 0 1
postgres=# insert into p_test1 values('10000 year');
INSERT 0 1
postgres=# insert into p_test1 values('12000 month');
INSERT 0 1
postgres=# insert into p_test1 values('1000 year 12000 month');
INSERT 0 1
postgres=# select * from p_test1;
value
------------------
100 years 5 mons
90 years 11 mons
90 years
10000 years
1000 years
2000 years
(6 行记录)
2.2、Interval day(p) to second类型
Oracle的Interval day(p) to second类型表示日、时、分、秒的时间间隔。一起使用的时候,写法类似于Interval '12 5:6:7.8' day to second。其中的日期由精度p来决定,p值为0~9,默认为2。小时为0~23,分钟为0~59,秒为0~59。其实日、时、分、秒也都可以单独使用。单独使用时,日期,时、分、秒的值都可以使用精度p来控制。
PostgreSQL的Interval中,也有类似的用法,而且用法比Oracle灵活的多。
Oracle Interval day(p) to second
SQL> create table o_test(value interval day(3) to second);
表已创建。
SQL> insert into o_test values('1000 1:23:45');
insert into o_test values('1000 1:23:45')
第 1 行出现错误:
ORA-01873: 间隔的前导精度太小
SQL> insert into o_test values('999 1:23:45');
已创建 1 行。
SQL> insert into o_test values('100 24:50:45');
insert into o_test values('100 24:50:45')
第 1 行出现错误:
ORA-01850: 小时值必须介于 0 和 23 之间
SQL> insert into o_test values('100 23:60:45');
insert into o_test values('100 23:60:45')
第 1 行出现错误:
ORA-01851: 分钟值必须介于 0 和 59 之间
SQL> insert into o_test values('100 23:40:75');
insert into o_test values('100 23:40:75')
第 1 行出现错误:
ORA-01852: 秒值必须介于 0 和 59 之间
SQL> insert into o_test values(interval '2399' hour);
已创建 1 行。
SQL> insert into o_test values(interval '2400' hour);
insert into o_test values(interval '2400' hour)
第 1 行出现错误:
ORA-01873: 间隔的前导精度太小
SQL> insert into o_test values(interval '2400' hour(4));
已创建 1 行。
SQL> insert into o_test values(interval '800000' minute);
insert into o_test values(interval '800000' minute)
第 1 行出现错误:
ORA-01873: 间隔的前导精度太小
SQL> insert into o_test values(interval '800000' minute(6));
已创建 1 行。
SQL> insert into o_test values(interval '80000000' second);
insert into o_test values(interval '80000000' second)
第 1 行出现错误:
ORA-01873: 间隔的前导精度太小
SQL> insert into o_test values(interval '80000000' second(8));
已创建 1 行。
SQL> select * from o_test;
VALUE
---------------------------------------------------------------------------
+999 01:23:45.000000
+099 23:00:00.000000
+100 00:00:00.000000
+555 13:20:00.000000
+925 22:13:20.000000
PostgreSQL Interval
postgres=# create table p_test1(value interval);
CREATE TABLE
postgres=# insert into p_test1 values('1000 1:23:45');
INSERT 0 1
postgres=# insert into p_test1 values('100 50:23:45');
INSERT 0 1
postgres=# insert into p_test1 values('100 50:60:45');
错误: 间隔字段超出范围: "100 50:60:45"
第1行insert into p_test1 values('100 50:60:45');
^
postgres=# insert into p_test1 values('1000 day');
INSERT 0 1
postgres=# insert into p_test1 values('80000 hour');
INSERT 0 1
postgres=# insert into p_test1 values('8000000 minute');
INSERT 0 1
postgres=# insert into p_test1 values('800000000 second');
INSERT 0 1
postgres=# select * from p_test1;
value
--------------------
1000 days 01:23:45
100 days 50:23:45
1000 days
80000:00:00
133333:20:00
222222:13:20
(6 行记录)
Oracle的日期时间类型向PostgreSQL的数据迁移过程相对来说简单一些。由于PostgreSQL的数据类型的极值超越Oracle,因此,数据迁移过程中,只要根据Oracle的数据精度,在PostgreSQL中选择正确的数据类型。并留意一下二者SQL写法的不同,应该就能够完整正确的迁移过来。
参考文档:
PostgreSQL 9.4.4 中文手册:日期/时间类型
http://www.postgres.cn/docs/9.4/datatype-datetime.html
Database SQL Language Reference:Data Types
http://docs.oracle.com/cd/E11882_01/server.112/e41084/sql_elements001.htm#i45441
--------------------------------------------------------------
四:大数据类型
Oracle的大数据类型主要包括三类。分别是存储在数据库内部的类型,包括BLOB,CLOB,NCLOB。存储在外部的类型,就是BFILE。还有Oracle不推荐使用,仅用于特殊环境的类型。包括为了兼容老版本而使用的Long和为了数据在不同系统间移动而利用的Long raw类型。
PostgreSQL的大数据类型只有两种,就是存储二进制数据的bytea和存储字符类型的text。下面介绍一下它们之间的对应和迁移时的一些注意事项。
注意:PostgreSQL对应的大数据类型还有一个对象标识符类型(oid)。它是一个标识符,指向在pg_largeobject 系统表中的一个bytea类型的对象。由于它是用一个四字节的无符号整数实现,不能够提供大数据库范围内的唯一性保证。因此,postgreSQL不推荐使用oid类型。加上它的内部实现,也是使用bytea类型,所以就不单独介绍了。
1、存储在数据库内部的类型
1.1、BLOB类型
Oracle的Blob类型主要内容是二进制的大对象。最大长度是(4G-1)*database block size。在PostgreSQL中,与之对应的是bytea。最大长度是1G。虽然最大长度小于Blob,但是在实际应用中已经足够了。
Oracle BLOB
SQL> create table o_test(value blob);
表已创建。
SQL> insert into o_test values('867814324901abedf4314312');
已创建 1 行。
SQL> insert into o_test values('867814324901abedf4314312t');
insert into o_test values('867814324901abedf4314312t')
第 1 行出现错误:
ORA-01465: 无效的十六进制数字
SQL> select * from o_test;
VALUE
--------------------------------------------------------------------------------
867814324901ABEDF4314312
PostgreSQL bytea
postgres=# create table p_test(value bytea);
CREATE TABLE
postgres=# insert into p_test values('867814324901abedf4314312');
INSERT 0 1
postgres=# insert into p_test values('867814324901abedf4314312t');
INSERT 0 1
postgres=# select * from p_test;
value
------------------------------------------------------
\x383637383134333234393031616265646634333134333132
\x38363738313433323439303161626564663433313433313274
(2 行记录)
1.2、CLOB类型
Oracle的Clob类型,主要存储基于数据库字符集的单字节或多字节文本信息,最大长度是(4G-1)*database block size。PostgreSQL中,可以使用text来对应。text的最大长度是1G,比Oracle的小。但是,实际应用中,1G已经足够。
Oracle CLOB
SQL> create table o_test( value clob);
表已创建。
SQL> insert into o_test values('122334543543666345435313421');
已创建 1 行。
VALUE
--------------------------------------------------------------------------------
122334543543666345435313421
PostgreSQL text
postgres=# create table p_test(value text);
CREATE TABLE
postgres=# insert into p_test values('1234567890123');
INSERT 0 1
postgres=# select * from p_test;
value
---------------
1234567890123
(1 行记录)
1.3、NCLOB类型
Oracle的NClob类型,主要存储固定长度的UNICODE字符串,最大长度是(4G-1)*database block size。PostgreSQL中,可以使用text来对应。text的最大长度是1G,比Oracle的小。但在实际应用中,1G已经足够。
Oracle CLOB
SQL> create table o_test1(value NCLOB);
表已创建。
SQL> insert into o_test1 values('1223344452525341');
已创建 1 行。
SQL> select DBMS_LOB.GETLENGTH(value) from o_test1;
DBMS_LOB.GETLENGTH(VALUE)
-------------------------
16
SQL> select * from o_test1;
VALUE
--------------------------------------------------------------------------------
1223344452525341
PostgreSQL text
postgres=# create table p_test(value text);
CREATE TABLE
postgres=# insert into p_test values('1234567890123');
INSERT 0 1
postgres=# select * from p_test;
value
---------------
1234567890123
(1 行记录)
2、存储在数据库外部的类型
2.1、BFILE类型
存储在数据库外部的类型,就是指BFILE类型。这是一种特殊的数据类型。数据并不保存在数据库内。数据库内仅仅是一个文件的指针。指明了文件的路径和文件名。实际的文件存储在硬盘上。只能对文件进行读取,而且不能Commit和Rollback。Oracle需要对文件所在的路径有读权限。文件的大小理论上可以达到2^64-1字节。
PostgreSQL中,没有储存在外部的数据类型。可以使用bytea来对应BFILE类型。数据类型的最大长度,没有BFILE的大。但是实际应用中,bytea的大小已经足够了。
Oracle BFILE
SQL> create table o_test(value bfile);
表已创建。
SQL> begin
2 insert into o_test values(bfilename('BFILE1', '00.txt'));
3 end;
4 /
PL/SQL 过程已成功完成。
SQL> declare txt1 BFILE;
2 piece raw(100);
3 amount binary_integer :=100;
4 begin
5 select value into txt1 from o_test;
6 DBMS_LOB.OPEN(txt1);
7 DBMS_LOB.READ(txt1, amount, 1, piece);
8 DBMS_LOB.CLOSE(txt1);
9 DBMS_OUTPUT.PUT_LINE(RAWTOHEX(piece));
10 end;
11 /
313233343535363635343333
PL/SQL 过程已成功完成。
PostgreSQL bytea
postgres=# create table p_test(value bytea);
CREATE TABLE
postgres=# insert into p_test values('123455665433');
INSERT 0 1
postgres=# select * from p_test;
value
----------------------------
\x313233343535363635343333
(1 行记录)
3、Oracle不推荐使用的大数据类型
3.1、LONG RAW类型
Oracle的long raw主要用于不同的系统之间转移数据的时候所用,是Oracle不推荐使用的数据类型。Oracle推荐使用Blob来代替Long raw,并且Long raw的数据可以直接转换成Blob类型。Long raw的最大范围是2G。PostgreSQL中与之对应的就是bytea类型。
3.2、LONG类型
Oracle的Long类型是Oracle不推荐使用的一个数据类型。使用它有很多的限制。比如一个表中只能有1个字段,不能用在where条件中使用等等。Oracle保留它的目的只是为了兼容以前的版本。Long类型的最大范围是2^31 -1(即2G-1)个字节。
在PostgreSQL中,Long类型的对应字段是text。虽然最大字符数少于Long类型,但是,text的最大长度1G在实际应用中已经足够了。
Oracle LONG
SQL> create table o_test(value long);
表已创建。
SQL> insert into o_test values('12sdfadsfewr34qdfsdsvvatearaewra');
已创建 1 行。
SQL> select * from o_test;
VALUE
--------------------------------------------------------------------------------
12sdfadsfewr34qdfsdsvvatearaewra
SQL> select * from o_test where value ='12sdfadsfewr34qdfsdsvvatearaewra';
select * from o_test where value ='12sdfadsfewr34qdfsdsvvatearaewra'
第 1 行出现错误:
ORA-00997: 非法使用 LONG 数据类型
PostgreSQL text
postgres=# create table p_test(value text);
CREATE TABLE
postgres=# insert into p_test values('12sdfadsfewr34qdfsdsvvatearaewra');
INSERT 0 1
postgres=# select * from p_test;
value
----------------------------------
12sdfadsfewr34qdfsdsvvatearaewra
Oracle的大数据类型虽然多一些,但是PostgreSQL中基本上就是text和bytea两个,分别对应文本信息和二进制信息。迁移的时候,文本信息转成text,二进制信息转成bytea。特殊类型BFILE形式的,可以额外写一些代码把数据从文件中读出转换成bytea。这样就可以完成大数据类型的迁移。
参考文档:
PostgreSQL 9.4.4 中文手册:字符类型,二进制类型,对象标识符类型
http://www.postgres.cn/docs/9.4/datatype-character.html (字符类型)
http://www.postgres.cn/docs/9.4/datatype-binary.html (二进制类型)
http://www.postgres.cn/docs/9.4/datatype-oid.html (对象标识符类型)
Database SQL Language Reference:Data Types
http://docs.oracle.com/cd/E11882_01/server.112/e41084/sql_elements001.htm#i45441
--------------------------------------------------------------
五:其它类型
Oracle的内建数据类型中,还有一些其它的类型。不能够归类到字符、数字、时间和大数据类型等。比如RAW,ROWID,UROWID等。在数据迁移的时候,在PostgreSQL中也有与之相对应的类型。
1、RAW类型
Oracle的RAW类型主要用于不同的系统之间转移数据的时候。用于保存位串,以字节为单位。作为数据库列的数据类型的时候,最大位数为2000(作为变量的时候,最大位数为32767。因为现在谈论的是数据迁移,所以作为变量的情况就不讨论了)。
PostgreSQL中,可以使用bytea来对应RAW类型。
Oracle RAW
SQL> create table o_test(value raw(2000));
表已创建。
SQL> create table o_test2(value raw(2001));
create table o_test2(value raw(2001))
第 1 行出现错误:
ORA-00910: 指定的长度对于数据类型而言过长
SQL> insert into o_test values(utl_raw.cast_to_raw('Hello! This is John'));
已创建 1 行。
SQL> select * from o_test;
VALUE
--------------------------------------------------------------------------------
48656C6C6F212054686973206973204A6F686E
PostgreSQL bytea
postgres=# create table p_test(value bytea);
CREATE TABLE
postgres=# insert into p_test values(cast('Hello! This is John' as bytea));
INSERT 0 1
postgres=# select * from p_test;
value
------------------------------------------
\x48656c6c6f212054686973206973204a6f686e
(1 行记录)
2、ROWID类型
Oracle的ROWID类型代表一条记录在数据库中的物理地址。使用BASE64编码的字符串表示的物理地址。固定是18位。ROWID是Oracle中的伪列,也可以定义一个ROWID类型的列,但是必须用户自己保证该列的数据有效性。
PostgreSQL中,可以使用char(18)来迁移对象。PostgreSQL中还有一个CTID数据类型,指的是一条记录位于哪个数据块的哪个位移上面。作用和ROWID类似。但是用的是数字对,数据迁移时,不能够使用它对Oracle的ROWID进行迁移。
Oracle ROWID
SQL> create table o_test(value1 int, value2 rowid);
表已创建。
SQL> insert into o_test values(1, '1234567890ABCDEFAB');
已创建 1 行。
SQL> insert into o_test values(2, '1234567890ABCDEFABC');
insert into o_test values(2, '1234567890ABCDEFABC')
第 1 行出现错误:
ORA-01410: 无效的 ROWID
SQL> select * from o_test;
VALUE1 VALUE2
---------- ------------------
1 B23456AM9AABCDEFAB
SQL> select t.rowid, t.* from o_test t;
ROWID VALUE1 VALUE2
------------------ ---------- ------------------
AAASQiAAEAAAAJ8AAA 1 B23456AM9AABCDEFAB
PostgreSQL char(18)
postgres=# create table p_test(value1 int , value2 char(18));
CREATE TABLE
postgres=# insert into p_test values(1, 'B23456AM9AABCDEFAB');
INSERT 0 1
postgres=# insert into p_test values(1, 'AAASQiAAEAAAAJ8AAA');
INSERT 0 1
postgres=# select * from p_test;
value1 | value2
--------+--------------------
1 | B23456AM9AABCDEFAB
1 | AAASQiAAEAAAAJ8AAA
(2 行记录)
postgres=# select ctid, * from p_test;
ctid | value1 | value2
-------+--------+--------------------
(0,1) | 1 | B23456AM9AABCDEFAB
(0,2) | 1 | AAASQiAAEAAAAJ8AAA
(2 行记录)
3、UROWID类型
Oracle的UROWID类型支持Oracle的物理ROWID和逻辑ROWID,在索引组织表(Index Organization Table,简称为IOT)中的ROWID,就是逻辑ROWID。并且支持非Oracle数据库的表的ROWID。也就是支持所有类型的ROWID。最大长度是4000。
PostgreSQL中,可以使用varchar来迁移对象。
Oracle UROWID
SQL> create table o_test(value1 int, value2 urowid);
表已创建。
SQL> insert into o_test values(1,'AAASQiAAEAAAAJ8AAA');
已创建 1 行。
SQL> select value2 from o_test;
VALUE2
--------------------------------------------------------------------------------
AAASQiAAEAAAAJ8AAA
SQL> create table o_test2(id number primary key) organization index;
表已创建。
SQL> insert into o_test2 values('12345');
已创建 1 行。
SQL> insert into o_test2 values('12345989873827276839302');
已创建 1 行。
SQL> set numw 25
SQL> select t.rowid, t.* from o_test2 t;
ROWID ID
----------------------------------------- -------------------------
*BAEAAjMEwwIYLv4 12345
*BAEAAjMNzAIYLmNjSlNJTVReA/4 12345989873827276839302
PostgreSQL VARCHAR
postgres=# create table p_test(value varchar);
CREATE TABLE
postgres=# insert into p_test values('AAASQiAAEAAAAJ8AAA');
INSERT 0 1
postgres=# insert into p_test values('*BAEAAjMEwwIYLv4');
INSERT 0 1
postgres=# insert into p_test values('*BAEAAjMNzAIYLmNjSlNJTVReA/4');
INSERT 0 1
postgres=# select value,length(value) from p_test;
value | length
------------------------------+--------
AAASQiAAEAAAAJ8AAA | 18
*BAEAAjMEwwIYLv4 | 16
*BAEAAjMNzAIYLmNjSlNJTVReA/4 | 28
(3 行记录)
Oracle的build-in类型里面的这几种特殊的数据类型,在数据迁移中是比较简单的。在向PostgreSQL数据库进行数据迁移的时候只要选对数据类型,应该就可以正确的迁移过来。
参考文档:
PostgreSQL 9.4.4 中文手册:字符类型,二进制类型,对象标识符类型
http://www.postgres.cn/docs/9.4/datatype-binary.html (二进制类型)
http://www.postgres.cn/docs/9.4/datatype-character.html (字符类型)
Database SQL Language Reference:Data Types
http://docs.oracle.com/cd/E11882_01/server.112/e41084/sql_elements001.htm#i45441
--------------------------------------------------------------