在Oracle中,用于表示网络地址的适当数据类型或技术是什么,哪些地址可能是IPv4或IPv6?
背景:我正在转换使用PostgreSQL inet
data type构建的表记录网络活动,以便在同一个表中保存v4和v6地址。
但是,没有行包含v4和v6地址。 (也就是说,记录来自机器的v4堆栈或机器的v6堆栈。)
答案 0 :(得分:16)
在Oracle中,什么是合适的 数据类型或技术 代表网络地址, 地址可以是IPv4或IPv6
有两种方法:
仅供存储。 IPV4地址应该是一个整数(32位就足够了)。对于IP V6,128位,INTEGER(类似于Number(38))将会这样做。当然,这是存储。该方法认为表示是应用程序的问题。
如果采用相反的策略来存储传统表示,则需要确保IP V4和IPV6地址仅具有一个传统(字符串)表示。它以ipV4而闻名。至于IPV6,还有一种标准格式。
我的偏好是第一个策略。在最坏的情况下,您可以采用混合方法(尽管非酸)并且将二进制和ascii表示并排存储到二进制值的“优先级”。
没有行包含v4和v6 但是,地址。
IPV6格式的IPV4地址的标准表示形式为:::ffff:192.0.2.128
。
我不知道上下文,但我会保留2列,一列用于IPV4,另一列用于不同的ipV6地址。
<强>更新强>
在@ sleepyMonad的好评之后,我想指出的是,不是使用 Number 数据类型,而是使用INTEGER数据类型,它将很乐意适应可能的最高值。表示为128位整数'ff ... ff'(需要 39 十进制数字)。 38是10的的最高功率,范围从0到9 ,可以在128位上编码,但仍然可以插入 2 ** 128 - 1 的最大无符号值(十进制340282366920938463463374607431768211455)。这是一个很小的测试来说明这种可能性。
create table test (
id integer primary key,
ipv6_address_bin INTEGER );
-- Let's enter 2**128 - 1 in the nueric field
insert into test (id, ipv6_address_bin) values ( 1, to_number ( 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF', 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX') ) ;
-- retrieve it to make sure it's not "truncated".
select to_char ( ipv6_address_bin, 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX' ) from test where id = 1 ;
-- yields 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF'
select to_char ( ipv6_address_bin ) from test where id = 1 ;
-- yields 340282366920938463463374607431768211455
select LOG(2, ipv6_address_bin) from test where id = 1 ;
-- yields 128
select LOG(10, ipv6_address_bin) from test where id = 1 ;
-- yields > 38
答案 1 :(得分:7)
将其存储在RAW中。
RAW是可变长度的字节数组,所以......
...并将其中一个直接存储在RAW(16)中。
RAW可以被编入索引,可以是PK,UNIQUE或FOREIGN KEY,因此您可以使用VARCHAR2或INT / NUMBER / DECIMAL执行任何操作,但转换和存储开销较少。
为了说明INT相对于RAW的存储开销,请考虑以下示例:
CREATE TABLE IP_TABLE (
ID INT PRIMARY KEY,
IP_RAW RAW(16),
IP_INT INT
);
INSERT INTO IP_TABLE (ID, IP_RAW, IP_INT) VALUES (
1,
HEXTORAW('FFFFFFFF'),
TO_NUMBER('FFFFFFFF', 'XXXXXXXX')
);
INSERT INTO IP_TABLE (ID, IP_RAW, IP_INT) VALUES (
2,
HEXTORAW('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF'),
TO_NUMBER('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF', 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX')
);
SELECT VSIZE(IP_RAW), VSIZE(IP_INT), IP_TABLE.* FROM IP_TABLE;
结果(在Oracle 10.2下):
table IP_TABLE created.
1 rows inserted.
1 rows inserted.
VSIZE(IP_RAW) VSIZE(IP_INT) ID IP_RAW IP_INT
---------------------- ---------------------- ---------------------- -------------------------------- ----------------------
4 6 1 FFFFFFFF 4294967295
16 21 2 FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF 340282366920938463463374607431768211455
答案 2 :(得分:4)
@Alain Pannetier(因为我无法发表评论): ANSI INTEGER数据类型根据http://download.oracle.com/docs/cd/B19306_01/server.102/b14200/sql_elements001.htm#i54335映射到Oracle中的NUMBER(38)。 在表格下方,您可以找到NUMBER仅提供126位二进制精度的信息,这对于128位IPv6地址不是必需的。 最大值可能存储正常,但会有地址被路由到下一个较低的地址。
内部数字格式为ROUND((length(p)+ s)/ 2))+ 1(http://download.oracle.com/docs/cd/B19306_01/server.102/b14220/datatype.htm#i16209)。
更新:再次解决此问题之后,我现在找到了一个解决方案,可以对包含IPv6地址的网络进行高性能查询:在RAW中存储IPv6地址和子网掩码(16 )列并使用UTL_RAW.BIT_AND:
进行比较SELECT name, DECODE(UTL_RAW.BIT_AND('20010DB8000000000000000000000001', ipv6_mask), ipv6_net, 1, 0)
FROM ip_net
WHERE ipv6_net IS NOT NULL;
答案 3 :(得分:1)
你也可以使用自定义的oracle对象。
SQL>set SERVEROUTPUT on
SQL>drop table test;
Table dropped.
SQL>drop type body inaddr;
Type body dropped.
SQL>drop type inaddr;
Type dropped.
SQL>create type inaddr as object
2 ( /* TODO enter attribute and method declarations here */
3 A number(5),
4 B number(5),
5 C number(5),
6 D number(5),
7 E number(5),
8 F number(5),
9 G number(5),
10 H NUMBER(5),
11 MAP MEMBER FUNCTION display RETURN VARCHAR2,
12 MEMBER FUNCTION toString( SELF IN INADDR , CONTRACT BOOLEAN DEFAULT TRUE) RETURN VARCHAR2,
13 CONSTRUCTOR FUNCTION INADDR(SELF IN OUT NOCOPY INADDR, INADDRASSTRING VARCHAR2) RETURN SELF AS RESULT
14
15 ) NOT FINAL;
16 /
SP2-0816: Type created with compilation warnings
SQL>
SQL>
SQL>CREATE TYPE BODY INADDR AS
2
3 MAP MEMBER FUNCTION display RETURN VARCHAR2
4 IS BEGIN
5 return tostring(FALSE);
6 END;
7
8
9 MEMBER FUNCTION TOSTRING( SELF IN INADDR , CONTRACT BOOLEAN DEFAULT TRUE) RETURN VARCHAR2 IS
10 IP4 VARCHAR2(6) := 'FM990';
11 ip6 varchar2(6) := 'FM0XXX';
12 BEGIN
13 IF CONTRACT THEN
14 ip6 := 'FMXXXX';
15 end if;
16
17 IF CONTRACT AND A =0 AND B=0 AND C = 0 AND D=0 AND E =0 AND F = 65535 THEN --ipv4
18 RETURN '::FFFF:'||TO_CHAR(TRUNC(G/256),'FM990.')||TO_CHAR(MOD(G,256),'FM990.')||TO_CHAR(TRUNC(H/256),'FM990.')||TO_CHAR(MOD(H,256),'FM990');
19 ELSE
20 RETURN
21 TO_CHAR(A,ip6)||':'||
22 TO_CHAR(B,IP6)||':'||
23 TO_CHAR(C,ip6)||':'||
24 TO_CHAR(D,ip6)||':'||
25 TO_CHAR(E,ip6)||':'||
26 TO_CHAR(F,ip6)||':'||
27 TO_CHAR(G,ip6)||':'||
28 TO_CHAR(H,ip6);
29 end if;
30 end;
31
32 CONSTRUCTOR FUNCTION inaddr(SELF IN OUT NOCOPY inaddr, inaddrasstring VARCHAR2)
33 RETURN SELF AS RESULT IS
34 begin
35 if instr(inaddrasstring,'.') > 0 then
36 --ip4
37 null;
38 a := 0;
39 B := 0;
40 C := 0;
41 D := 0;
42 E := 0;
43 F := TO_NUMBER('FFFF', 'XXXX');
44 G := TO_NUMBER(TO_CHAR(TO_NUMBER(REGEXP_SUBSTR(INADDRASSTRING,'([0-9]{1,3}).',1,1,'i',1),'999'),'FM0X')
45 ||TO_CHAR(TO_NUMBER(REGEXP_SUBSTR(INADDRASSTRING,'([0-9]{1,3}).',1,2,'i',1),'999'),'FM0X')
46 ,'XXXX');
47 h := TO_NUMBER(TO_CHAR(TO_NUMBER(REGEXP_SUBSTR(INADDRASSTRING,'([0-9]{1,3}).',1,3,'i',1),'999'),'FM0X')
48 ||TO_CHAR(TO_NUMBER(REGEXP_SUBSTR(INADDRASSTRING,'([0-9]{1,3})',1,4,'i',1),'999'),'FM0X')
49 ,'XXXX');
50
51 ELSIF instr(inaddrasstring,':') > 0 then
52 --ip6
53 a := TO_NUMBER(REGEXP_SUBSTR(inaddrasstring,'([0-9a-fA-F]{1,4})',1,1,'i',1),'XXXX');
54 b := TO_NUMBER(REGEXP_SUBSTR(inaddrasstring,'([0-9a-fA-F]{1,4})',1,2,'i',1),'XXXX');
55 c := TO_NUMBER(REGEXP_SUBSTR(inaddrasstring,'([0-9a-fA-F]{1,4})',1,3,'i',1),'XXXX');
56 d := TO_NUMBER(REGEXP_SUBSTR(inaddrasstring,'([0-9a-fA-F]{1,4})',1,4,'i',1),'XXXX');
57 E := TO_NUMBER(REGEXP_SUBSTR(inaddrasstring,'([0-9a-fA-F]{1,4})',1,5,'i',1),'XXXX');
58 f := TO_NUMBER(REGEXP_SUBSTR(inaddrasstring,'([0-9a-fA-F]{1,4})',1,6,'i',1),'XXXX');
59 g := TO_NUMBER(REGEXP_SUBSTR(inaddrasstring,'([0-9a-fA-F]{1,4})',1,7,'i',1),'XXXX');
60 H := TO_NUMBER(REGEXP_SUBSTR(inaddrasstring,'([0-9a-fA-F]{1,4})',1,8,'i',1),'XXXX');
61 end if;
62
63 RETURN;
64 END;
65 end;
66 /
Type body created.
SQL>
SQL>create table test
2 (id integer primary key,
3 address inaddr);
Table created.
SQL>
SQL>select * from test;
no rows selected
SQL>
SQL>
SQL>insert into test values (1, INADDR('fe80:0000:0000:0000:0202:b3ff:fe1e:8329') );
1 row created.
SQL>INSERT INTO TEST VALUES (2, INADDR('192.0.2.128') );
1 row created.
SQL>insert into test values (3, INADDR('20.0.20.1') );
1 row created.
SQL>insert into test values (4, INADDR('fe80:0001:0002:0003:0202:b3ff:fe1e:8329') );
1 row created.
SQL>insert into test values (5, INADDR('fe80:0003:0002:0003:0202:b3ff:fe1e:8329') );
1 row created.
SQL>INSERT INTO TEST VALUES (6, INADDR('fe80:0003:0001:0003:0202:b3ff:fe1e:8329') );
1 row created.
SQL>INSERT INTO TEST VALUES (7, INADDR('fe80:0003:0001:0003:0202:b3ff:fe1e:8328') );
1 row created.
SQL>INSERT INTO TEST VALUES (8, INADDR('dead:beef:f00d:cafe:dea1:aced:b00b:1234') );
1 row created.
SQL>
SQL>COLUMN INET_ADDRESS_SHORT FORMAT A40
SQL>column inet_address_full format a40
SQL>
SQL>select t.address.toString() inet_address_short, t.address.display( ) inet_address_full
2 from test T
3 order by t.address ;
INET_ADDRESS_SHORT INET_ADDRESS_FULL
---------------------------------------- ----------------------------------------
::FFFF:20.0.20.1 0000:0000:0000:0000:0000:FFFF:1400:1401
::FFFF:192.0.2.128 0000:0000:0000:0000:0000:FFFF:C000:0280
DEAD:BEEF:F00D:CAFE:DEA1:ACED:B00B:1234 DEAD:BEEF:F00D:CAFE:DEA1:ACED:B00B:1234
FE80:0:0:0:202:B3FF:FE1E:8329 FE80:0000:0000:0000:0202:B3FF:FE1E:8329
FE80:1:2:3:202:B3FF:FE1E:8329 FE80:0001:0002:0003:0202:B3FF:FE1E:8329
FE80:3:1:3:202:B3FF:FE1E:8328 FE80:0003:0001:0003:0202:B3FF:FE1E:8328
FE80:3:1:3:202:B3FF:FE1E:8329 FE80:0003:0001:0003:0202:B3FF:FE1E:8329
FE80:3:2:3:202:B3FF:FE1E:8329 FE80:0003:0002:0003:0202:B3FF:FE1E:8329
8 rows selected.
SQL>spool off
我只是在最后一小时把它放在一起(同时教自己的物体)所以我相信它可以改进。如果我做更新,我会在这里重新发布
答案 4 :(得分:1)
我希望存储的IP地址只是字符串格式,由SYS_CONTEXT返回(&#39; USERENV&#39;,&#39; IP_ADDRESS&#39;)
在11g中的SYS_CONTEXT的引用中,仅将默认返回值长度描述为256字节,并且没有描述exacly&#39; IP_ADDRESS&#39;的返回值大小。上下文。
在文件Oracle Database and IPv6 Statement of Direction中描述:
Oracle Database 11g第2版支持标准IPv6地址 RFC2732指定的符号。通常是128位IP地址 表示为8组,每组4个十六进制数字,其中“:”符号为 组分隔符。每组中的前导零被删除。对于 例如,1080:0:0:0:8:800:200C:417A将是有效的IPv6地址。一 或者更多连续的零字段可以选择用 “::“ 分隔器。例如,1080 :: 8:800:200C:417A。
根据这些说明,我更喜欢将列 IP_ADDRESS varchar2(39)设为允许存储8组4位数和7组之间的分隔符。
答案 5 :(得分:0)
Oracle文档确实说明INTEGER是NUMBER(38)的别名,但这可能是一个错字,因为它上面的段落指出:
NUMBER(p,s)其中:p是精度... Oracle保证 数字的可移植性,精度高达20个基数-100位, 相当于39或40位十进制数字,具体取决于 小数点的位置。
因此NUMBER可以存储39到40位数字,而INTEGER可能是NUMBER(最大精度)而不是NUMBER(38)的别名。这就是为什么提供的示例有效(如果您将INTEGER更改为NUMBER,它就有效)。
答案 6 :(得分:0)
可能性是:
VARCHAR2
(示例1080::8:800:200c:417a
)NUMBER
数据类型INTEGER
数据类型RAW
值存储
RAW(4)
或RAW(16)
RAW(1)
或8 x RAW(2)
我建议使用RAW
值,因为
如果使用字符串,则必须考虑不同的IPv6格式。
1080::8:800:200C:417A
1080::8:800:200c:417a
1080::8:800:32.12.65.122
1080:0:0:0:8:800:200C:417A
1080:0:0:0:0008:0800:200C:417A
1080:0000:0000:0000:0008:0800:200C:417A
是同一IPv6 IP地址的所有合法表示。您的应用程序需要强制执行适当使用的通用格式,例如在WHERE
条件下使用。
NUMBER/INTEGER
值无意义,无需转换为人类可读的格式。您不能在PL / SQL
INTEGER
数据类型
i INTEGER := 2**128-1; -- i.e. ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff
-> ORA-06502: PL/SQL: numeric or value error: number precision too large.
如果您必须使用子网划分,则无法使用函数BITAND - 它还支持最多2 ^ 127的数字
您可以使用UTL_RAW函数UTL_RAW.BIT_AND
,UTL_RAW.BIT_COMPLEMENT
,UTL_RAW.BIT_OR
进行子网操作。
如果你必须处理真正大量的数据(我说的是数十亿行),将IP地址分成几个RAW值可能是有益的,即4 x RAW(1)
或8 x RAW(2)
。这些列将预定为Bitmap-Indexes,您可以节省大量磁盘空间并获得性能。