15

Oracle で、ネットワーク アドレスを表すための適切なデータ型または手法は何ですか?どのアドレスが IPv4 または IPv6 である可能性がありますか?

inet背景: PostgreSQLデータ型を使用して構築されたネットワーク アクティビティを記録するテーブルを変換して、同じテーブルに v4 と v6 の両方のアドレスを保持します。

ただし、v4 アドレスと v6 アドレスの両方を含む行はありません。(つまり、レコードはマシンの v4 スタックまたはマシンの v6 スタックからのものです。)

4

7 に答える 7

16

Oracle で、ネットワーク アドレスを表すための適切なデータ型または手法は何ですか。どのアドレスが IPv4 または IPv6 である可能性がありますか

2 つのアプローチがあります。

  1. 収納のみ。
  2. 従来の表現の保存

収納専用です。IPV4 アドレスは整数でなければなりません (32 ビットで十分です)。IP V6 の場合、128 ビットの INTEGER (Number(38) に似ています) で十分です。もちろん、それは保存です。そのアプローチは、表現はアプリケーションの問題であるという見解をとります。

従来の表現を格納するという反対の戦略を取る場合、IP V4 および IPV6 アドレスに従来の (文字列) 表現が 1 つだけあることを確認する必要があります。ipV4で有名です。IPV6には標準フォーマットもあります。

私の好みは最初の戦略です。最悪の場合、ハイブリッド アプローチを採用し (非酸)、バイナリと ASCII 表現の両方を、バイナリ値に「優先度」を付けて並べて格納できます。

ただし、v4 アドレスと v6 アドレスの両方を含む行はありません。

IPV6 形式での IPV4 アドレスの標準表現は次のとおり::ffff:192.0.2.128です。

コンテキストはわかりませんが、2 つの列を予約します。1 つは IPV4 用で、もう 1 つは個別の ipV6 アドレス用です。

更新
@sleepyMonad の良いコメントに続いて、Numberデータ型の代わりに INTEGER データ型を使用することが望ましいことを指摘したいと思います。これは、128 ビット整数で表現できる可能な限り高い値に喜んで対応します。 'ff...ff' ( 39桁の 10 進数が必要です)。38 は、128 ビットでエンコードできる0 から 9 の範囲の 10 の最大累乗ですが、 2**128 - 1 (10 進数 34028236692093846346374607431768211455) の最大符号なし値を挿入できます。この可能性を説明するための小さなテストを次に示します。

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
于 2011-02-09T21:15:58.433 に答える
8

RAWで保存してください。

RAWは可変長バイト配列なので....

  • IPv4を4バイトの配列として扱うだけです
  • および 16 バイトの配列としての IPv6

...そして、それらのいずれかを RAW(16) に直接保存します。


RAW は、PK、UNIQUE、または FOREIGN KEY としてインデックスを作成できるため、VARCHAR2 または INT/NUMBER/DECIMAL で通常できることは何でも実行できますが、変換とストレージのオーバーヘッドは少なくなります。

RAW に対する INT のストレージ オーバーヘッドを説明するために、次の例を考えてみましょう。

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 
于 2012-02-03T03:11:49.413 に答える
4

@Alain Pannetier (まだコメントできないため): ANSI INTEGER データ型は、 http ://download.oracle.com/docs/cd/B19306_01/server.102/b14200/sql_elements001 に従って Oracle の NUMBER(38) にマップされます.htm#i54335 . 表の下に、NUMBER は 128 ビットの IPv6 アドレスには不十分な 126 ビットのバイナリ精度しか提供しないという情報があります。最大値は正常に保存される可能性がありますが、次の低い値に丸められるアドレスがあります。

内部の数値形式は ROUND((length(p)+s)/2))+1 ( http://download.oracle.com/docs/cd/B19306_01/server.102/b14220/datatype.htm#i16209 )です。 .

更新:再び問題をいじった後、IPv6 アドレスを含むネットワークの高性能クエリを可能にするソリューションを見つけました: IPv6 アドレスとサブネット マスクを RAW(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;
于 2011-10-20T12:32:11.437 に答える
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

私はちょうどこの 1 時間でこれをまとめた (同時にオブジェクトを自分自身に教えた) ので、改善できると確信しています。更新したらここに再投稿します

于 2012-05-30T04:02:02.277 に答える
1

SYS_CONTEXT ('USERENV', 'IP_ADDRESS') によって返される IP アドレスを文字列形式で格納することをお勧めします

11gのSYS_CONTEXTを参照して、デフォルトの戻り値の長さ 256 バイトのみが説明されており、正確な「IP_ADDRESS」コンテキストの戻り値のサイズは説明されていません。

Oracle Database and IPv6 Statement of Direction のドキュメントでは、次のように説明されています。

Oracle Database 11g Release 2 は、RFC2732 で指定された標準の IPv6 アドレス表記をサポートしています。128 ビットの IP アドレスは、一般に 4 つの 16 進数の 8 つのグループとして表され、グループ セパレータとして「:」記号が使用されます。各グループの先頭のゼロは削除されます。たとえば、1080:0:0:0:8:800:200C:417A は有効な IPv6 アドレスです。オプションで、1 つ以上の連続するゼロ フィールドを「::」セパレータで圧縮できます。たとえば、1080::8:800:200C:417A です。

このメモから、列IP_ADDRESS varchar2(39)を作成して、8 つのグループを 4 桁で格納し、このグループ間に 7 つのセパレーターを配置することを好みます。

于 2014-03-12T10:07:09.187 に答える
0

Oracle のドキュメントには、INTEGER は NUMBER(38) のエイリアスであると記載されていますが、その上の段落に次のように記載されているため、おそらくタイプミスです。

NUMBER(p,s) ここで、 p は精度です... Oracle では、100 進法で最大 20 桁の精度を持つ数値の移植性が保証されています。これは、小数点の位置に応じて 39 桁または 40 桁に相当します。

したがって、NUMBER は 39 ~ 40 桁を格納でき、INTEGER は NUMBER(38) ではなく NUMBER(最大精度) のエイリアスである可能性があります。提供された例が機能するのには理由があります (INTEGER を NUMBER に変更すると機能します)。

于 2012-02-03T02:07:25.660 に答える
0

可能性は次のとおりです。

  • 文字列として保存、つまりVARCHAR2(例1080::8:800:200c:417a)
  • 数値として保存
    • NUMBERデータ・タイプ
    • INTEGERデータ・タイプ
  • RAW値 として保存
    • 1 つの RAW 値、つまり、IPv4 または IPv6 それぞれのRAW(4)orRAW(16)
    • IPv4 または IPv6 の場合、それぞれ4 xRAW(1)または 8 xRAW(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人間が読める形式に変換しない限り、値は無意味です。INTEGERPL/SQLではデータ型を使用できません

    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_COMPLEMENTUTL_RAW.BIT_OR

  • 非常に大量のデータ (数十億行について話している) を処理する必要がある場合は、IP アドレスをいくつかの RAW 値、つまり 4 xRAW(1)または 8 xに分割すると有益な場合がありますRAW(2)。このような列はビットマップ インデックス用にあらかじめ設定されており、多くのディスク スペースを節約し、パフォーマンスを向上させることができます。

于 2018-05-31T07:12:47.047 に答える