ID
をBIGINT
として、IPAddress
をvarchar(45)
またはvarbinary(16)
として、2つのフィールドを持つテーブルを作成します。アイデアは、すべての一意のIPアドレスを保存し、他のテーブルの実際のIP address
の代わりに参照ID
を使用することです。
通常、与えられたIP address
のID
を返す、または(アドレスが見つからなかった場合)アドレスを挿入して生成されたID
を返すストアドプロシージャを作成します。
多くのレコードがあることを期待しています(正確な数はわかりません)が、上記のストアドプロシージャをできるだけ速く実行する必要があります。それで、実際のIPアドレスをテキストまたはバイト形式で保存する方法を知りたいです。どっちがいいの?
IPアドレスのバイトを文字列およびその逆に変換するためのSQL CLR
関数をすでに作成しているため、変換は問題ではありません(IPv4
とIPv6
の両方で機能します)。
検索を最適化するためにインデックスを作成する必要があると思いますが、クラスター化されたインデックスにIP address
フィールドを含める必要があるか、または別のインデックスを作成し、どのタイプで検索を高速化するべきかわかりませんか?
実際のIPアドレスを格納する方法-テキストまたはバイト形式。どっちがいいの?
ここでの「テキスト」はVARCHAR(45)
を指し、「バイト」はVARBINARY(16)
を指しているため、どちらでもないと言います。
以下の情報が与えられた場合( IPv6に関するウィキペディアの記事 から):
住所表現
IPv6アドレスの128ビットは、それぞれ16ビットの8つのグループで表されます。各グループは4桁の16進数として記述され、グループはコロン(:)で区切られます。アドレス2001:0db8:0000:0000:0000:ff00:0042:8329は、この表現の例です。便宜上、可能であれば、次の規則を適用することにより、IPv6アドレスを短い表記に短縮できます。
- 16進数のグループから1つ以上の先行ゼロが削除されます。これは通常、先行ゼロのすべてに対して行われるか、まったく行われません。たとえば、グループ0042は42に変換されます。
- ゼロの連続セクションは、二重コロン(::)に置き換えられます。二重コロンは、複数回使用するとアドレスが不確定になるため、アドレスで1回だけ使用できます。 RFC 5952は、ゼロの省略された単一セクションを示すために二重コロンを使用してはならないことを推奨しています。[41]
これらのルールの適用例:
初期アドレス:2001:0db8:0000:0000:0000:ff00:0042:8329
各グループの先行ゼロをすべて削除した後:2001:db8:0:0:0:ff00:42:8329
ゼロの連続セクションを省略した後:2001:db8 :: ff00:42:8329
まず、8つのグループを表すために8つのVARBINARY(2)
フィールドを使用します。グループ5〜8のフィールドはNULL
である必要があります。これらのフィールドはIPv6アドレスにのみ使用されるためです。グループ1〜4のフィールドは、IPv4アドレスとIPv6アドレスの両方に使用されるため、NOT NULL
である必要があります。
(VARCHAR(45)
またはVARBINARY(16)
または2つのBIGINT
フィールドに結合するのとは対照的に)各グループを独立させることにより、2つの主な利点が得られます。
IF
/IIF
/CASE
ステートメントでこれを容易にすることができます。ROW COMPRESSION
またはPAGE COMPRESSION
のいずれかを有効にすると、IPv6アドレスのスペースを大幅に節約できます。どちらのタイプのCOMPRESSIONでも、0x00
のフィールドが0バイトを占めることを許可するため、これらのゼロのグループのすべてでコストが発生することはありません。一方、上記の例のアドレスを(Wikipediaの引用で)保存した場合、中央にあるすべてのゼロの3セットは、スペースをすべて使用します(VARCHAR(45)
を実行している場合を除く)と表記を減らしましたが、それはインデックス作成にはうまく機能しない可能性があり、完全な形式に再構築するために特別な解析が必要になるので、それはオプションではないと仮定しましょう;-).ネットワークをキャプチャする必要がある場合は、呼び出されたum、[Network]
のTINYINT
フィールドを作成します:-)
ネットワーク値の詳細については、別の情報をご覧ください IPv6アドレスに関するウィキペディアの記事 :
ネットワーク
IPv6ネットワークは、2の累乗のサイズのIPv6アドレスの連続したグループであるアドレスブロックを使用します。アドレスの先行ビットセットは、特定のネットワーク内のすべてのホストで同一であり、ネットワークのアドレスまたはルーティングprefixと呼ばれます。
ネットワークアドレス範囲はCIDR表記で記述されます。ネットワークは、ブロック内の最初のアドレス(すべてゼロで終わる)、スラッシュ(/)、およびプレフィックスのビット単位のサイズに等しい10進値で示されます。たとえば、2001:db8:1234 ::/48と書かれたネットワークは、アドレス2001:db8:1234:0000:0000:0000:0000:0000で始まり、2001:db8:1234:ffff:ffff:ffff:ffffで終わります。 :ffff。
インターフェイスアドレスのルーティングプレフィックスは、CIDR表記によってアドレスで直接示される場合があります。たとえば、アドレス2001:db8:a :: 123がサブネット2001:db8:a ::/64に接続されているインターフェースの構成は、2001:db8:a :: 123/64と記述されます。
インデックスを作成するために、8つのグループフィールド、および場合によってはネットワークフィールドを含めることにした場合は、ネットワークフィールドに非クラスター化インデックスを作成するとします。
最終結果は次のようになります。
CREATE TABLE [IPAddress]
(
IPAddressID INT NOT NULL IDENTITY(-2147483648, 1),
Group8 VARBINARY(2) NULL, -- IPv6 only, NULL for IPv4
Group7 VARBINARY(2) NULL, -- IPv6 only, NULL for IPv4
Group6 VARBINARY(2) NULL, -- IPv6 only, NULL for IPv4
Group5 VARBINARY(2) NULL, -- IPv6 only, NULL for IPv4
Group4 VARBINARY(2) NOT NULL, -- both
Group3 VARBINARY(2) NOT NULL, -- both
Group2 VARBINARY(2) NOT NULL, -- both
Group1 VARBINARY(2) NOT NULL, -- both
Network TINYINT NULL
);
ALTER TABLE [IPAddress]
ADD CONSTRAINT [PK_IPAddress]
PRIMARY KEY CLUSTERED
(IPAddressID ASC)
WITH (FILLFACTOR = 100, DATA_COMPRESSION = PAGE);
CREATE NONCLUSTERED INDEX [IX_IPAddress_Groups]
ON [IPAddress] (Group1 ASC, Group2 ASC, Group3 ASC, Group4 ASC,
Group5 ASC, Group6 ASC, Group7 ASC, Group8 ASC, Network ASC)
WITH (FILLFACTOR = 100, DATA_COMPRESSION = PAGE);
ノート:
BIGINT
を使用する予定ですが、4,294,967,295を超える一意の値をキャプチャすることを本当に期待していますか?その場合は、フィールドをBIGINTに変更するだけで、シード値を0に変更することもできます。ただし、それ以外の場合は、INTを使用し、最小値から始めて、そのデータ型の範囲全体を利用できるようにする方がよいでしょう。 。SELECT *
を実行すると、フィールドが期待どおりの順序で返されます。しかし、インデックスには、1から8までupがあり、そのように入力されます。テキスト形式で値を表す計算列の例(未完成)は次のとおりです。
ALTER TABLE [IPAddress]
ADD TextAddress AS (
IIF([Group8] IS NULL,
-- IPv4
CONCAT(CONVERT(TINYINT, [Group4]), '.', CONVERT(TINYINT, [Group3]), '.',
CONVERT(TINYINT, [Group2]), '.', CONVERT(TINYINT, [Group1]),
IIF([Network] IS NOT NULL, CONCAT('/', [Network]), '')),
-- IPv6
LOWER(CONCAT(
CONVERT(VARCHAR(4), [Group8], 2), ':', CONVERT(VARCHAR(4), [Group7], 2), ':',
CONVERT(VARCHAR(4), [Group6], 2), ':', CONVERT(VARCHAR(4), [Group5], 2), ':',
CONVERT(VARCHAR(4), [Group4], 2), ':', CONVERT(VARCHAR(4), [Group3], 2), ':',
CONVERT(VARCHAR(4), [Group2], 2), ':', CONVERT(VARCHAR(4), [Group1], 2),
IIF([Network] IS NOT NULL, CONCAT('/', [Network]), '')
))
) -- end of IIF
);
テスト:
INSERT INTO IPAddress VALUES (127, 0, 0, 0, 4, 22, 222, 63, NULL); -- IPv6
INSERT INTO IPAddress VALUES (27, 10, 1234, 0, 45673, 200, 1, 6363, 48); -- IPv6
INSERT INTO IPAddress VALUES (NULL, NULL, NULL, NULL, 192, 168, 2, 63, NULL); -- v4
INSERT INTO IPAddress VALUES (NULL, NULL, NULL, NULL, 192, 168, 137, 29, 16); -- v4
SELECT [IPAddressID], [Group8], [Group1], [Network], [TextAddress]
FROM IPAddress ORDER BY [IPAddressID];
結果:
IPAddressID Group8 Group1 Network TextAddress
----------- ------ ------ ------- ---------------------
-2147483646 0x007F 0x003F NULL 007f:0000:0000:0000:0004:0016:00de:003f
-2147483645 0x001B 0x18DB 48 001b:000a:04d2:0000:b269:00c8:0001:18db/48
-2147483644 NULL 0x003F NULL 192.168.2.63
-2147483643 NULL 0x001D 16 192.168.137.29/16
小さいほど常に高速になります。値が小さいと、より多くの値を1つのページに収めることができるため、IOが少なくなり、Bツリーが浅くなる可能性があります。
もちろん、他のすべてのもの(翻訳のオーバーヘッド、読みやすさ、互換性、CPU負荷、インデックスの検索可能性など)は同じです。