2013-06-12 9 views
9

私はデータベース内のネットワークのさまざまな側面のモデリングに取り組んでいます。私たちが扱っているより厄介な問題の1つは、サブネット範囲を作成し、与えられた一連のIPがそれらの範囲内にあるかどうかを判断することです。私たちの現在のモデルは、次の列を持つIPv4とIPv6の違いを占める:サブネット範囲とIPリストを指定すると、IPがすべてのローを選択します

[subnet_sk]  [int] IDENTITY(1,1) NOT NULL, 
[ipv6_network] [char](39)   NULL, 
[ipv6_broadcast] [char](39)   NULL, 
[ipv4_network] [char](15)   NULL, 
[ipv4_broadcast] [char](15)   NULL, 
[network_type] [char](4)   NOT NULL 

上記のスキーマを指摘するために重要であるいくつかの仮定を行います。我々は、記憶と比較のために完全拡張IP(192.168.001.001192.168.1.1)を利用している。 IPv6 addresses numerically in SQL serverの格納に関する問題があるため、この決定を下しました(bigintは、IPv6を表すために6つの列を使用しなければならないという意味の無署名です)。どちらかのタイプのIPはテーブル内の範囲の間にあるかどうかを判断するためにselect文オフ1を書くことは非常に簡単です。このテーブルスキーマを考える

:リストの与えられているより困難である何

select * 
    from subnet 
where '1234:0000:0000:0000:fc12:00ab:0042:1050' 
     between ipv6_network 
      and ipv6_broadcast 

-- or alternatively for IPv4 

select * 
    from subnet 
where '192.168.005.015' 
     between ipv4_network 
      and ipv4_broadcast 

IPはサブネット範囲内にあるものを判別します。 IPのリストは、ユーザ入力によって提供され、ではなく、がデータベースに格納されています。明らかに、データベースに格納されているデータについては、以下の例のように同様の結合を行うことができます。

例えば、ユーザは1234:0000:0000:0000:fc12:00ab:0042:1050,192.168.001.001および192.168.1.1を提供することができます。スプリット機能を利用することがハック感じて動作しますが

-- this only covers the IPv4 addresses from the above list a similar query would 
-- be used for IPv6 and the two queries could be unioned 
select sub.* 
    from fn_SplitList('192.168.001.001,192.168.005.015',',') split 
     join subnet sub 
     on split.Data 
      between sub.ipv4_network 
       and sub.ipv4_broadcast 

:私が出ている唯一の解決策は、IPアドレスのtable-valued function to split a listを使用しての間で使用して結合を実行することです。私は朝のより良い部分をcommon table expressionsの周りで嗅ぐのに費やしましたが、うまくいくとは思えませんでした。理想的には、単一の選択がIPv4またはIPv6列から特定の文字列をバウンスするかどうかを決定しますが、不可能な場合は、IPの集合をデータベースに渡す前にリストを分割できます。

私は上記のうちSQL Fiddleを作成しました。それらのIPがどの既存のサブネット範囲にあるかを判断するために、IPのリストが与えられていると、SQLにメカニズムがありますか(T-SQLを使用したくないですか?)上記のスキーマは、問題の適切なアプローチでさえ、別のデータモデルがより簡単なソリューションにつながるでしょうか?

+0

私は良いネットワークの知識を持っていません。私が知りたいのは、あなたを悩ませているfn_SplitList()だけであるか、シナリオ全体を処理するためのよりよい方法を探したいのですか? –

+0

@RaviSinghこのデータモデルでは、日付のリストを使用していて、範囲内に収まっているかどうかを判断しようとするのと同じ質問が出ます。ですから、私の問題は、fn_SplitListが問題を回避する方法だと感じていますが、そこにはもっとクリーンなソリューションがあるように感じます。それは、あなたのスキーマが間違っていて、あなたのデータモデルがこのように設定されていれば、クエリが簡単になると言ってくれれば、私はすべての耳になるだろう。したがって、モデリングを手助けすることはできませんが、fn_SplitListを使用しないより良いクエリを手助けすることができれば、私はあなたのソリューションに非常に興味があります。 – ahsteele

+0

これに遅れましたが、ここではSQL Serverの文字列分割について論じるいくつかの類似スレッドがあります:http://stackoverflow.com/questions/2647/split-string-in-sql and http://stackoverflow.com/質問/ 314824/t-sql-反対の - 文字列 - 連結の方法 - 分割 - 文字列 - 複数の - recoへ – rutter

答えて

1

これは完全な解決策ではありませんが、もう1つの設計のアイデア 典型的なSQLの比較を行う代わりに論理比較を使用してみてください。 私は(BIGINTで)、ビット単位の比較で浮気しようとしているSQL実装の知って非常に少ないが

行われるために多くの最適化が

、しかし私はそれが役立つかもしれないという可能性があると思い、そこにあります私が4つのip(192.168.1。1と3以上)、(bigintsがintが小さすぎる原因として、私はそれらを使用する、と私は論理的なビット単位の比較を使用するためにここにhttp://msdn.microsoft.com/en-us/library/ms174965.aspx、 (詳細)

select * from (
    select cast(192168001001 as bigint) as ip union all 
    select cast(192168001002 as bigint) as ip union all 
    select cast(192168002001 as bigint) as ip union all 
    select cast(192168002002 as bigint) as ip 
) as ip_table 
where ip & cast(192168001000 as bigint) = cast(192168001000 as bigint) 

あなたが私を見ることができるようにする必要がありますAND/&)IPアドレスとネットワークアドレス これをネットワークアドレスと比較すると、一致した場合、この範囲に該当します。

私が間違っている場合、私はこれについてもっと考える必要があります確かにスタッフ。

Resultこれは悲しいことですが、bitwise(AND)演算はバイナリデータ型では実行できません。整数型しか受け付けません...

+1

bigintはIPv4アドレスで本当にうまく動作します。残念ながら、SQL Serverでは、bigintとしてIPv6アドレスを表すために3つの列が必要なため、IPv6ではビット単位の比較が困難な符号なしのbigintが使用できません。 – ahsteele

1

私は100%明確にするために、問題のクエリで遊んであなたのSQLフィドル、 で

を探している、あなたは、すべてのホストアドレスのリストが該当すると範囲を見つけるために、クエリを必要としています。あなたのホストがリスト/データのテーブルがあるかのように

はあなたが行動することができ、その後、内部は私が得た

select * 
from (
    select '192.168.001.001' as ip union 
    select'192.168.005.015') as hosts 
inner join subnet 
    on ip between ipv4_network and ipv4_broadcast 

(あなたがそれも、サブネットなしで表示する必要がある場合は、参加または左)その上にサブネットに参加します4つの結果(各レコードに一致する2つのサブネットがありました)

1

1つの列にipv6とipv4の両方の形式を格納すると考えましたか?

Storing IP addresses in Microsoft SQL Server

それはあなたの比較のために、任意のソースデータの変換(または他の方法)が必要だろうが、少なくとも、あなたがチェックするために2つの別々のクエリを必要とする避けることができ。

私はソースデータ(FOR XML?)からCTEを作成し、データベーステーブル(サブネット)に結合する傾向があります。

0

[xml]または[heirarchyid](http://technet.microsoft.com/en-us/library/bb677290.aspx)を使用してこの問題に対処し、データをツリーとして扱う傾向があります。 Treeを実行して@subnetにマッチするノードを見つける[tree]。[run] @subnetメソッドを使って、既存のサブネットに基づいてツリーを構築するのは比較的簡単です。データをツリー(実際には)として扱い、共通のツリー処理(再帰的)メソッドを構築することで、存在するノードを見つけたり、それを挿入したり、次のノードを取得したりすることができます。以前のノード。

これは興味深い場合はもっと詳細な例を提供できますが、それほど簡単な解決策ではありませんので、そうでなければ時間を費やすつもりはありません。私がここで示しているのは、単純なプロトタイプで、入力マスクの親(単純なマッチング)であるノードを見つけます。私はこれを一例として提出しますが、ソリューションに興味がある場合は、詳細を提供するか、これらの手法を使用してソリューションを構築する方法を簡単に確認できます。

平和、 キャサリン

use [test_01]; 

    go 

    if schema_id(N'tree') is null 
     execute (N'create schema tree'); 

    go 

    if object_id(N'[tree].[run]', 
       N'FN') is not null 
     drop function [tree].[run]; 

    go 

    create function [tree].[run] (
     @network  [xml], 
     @mask_to_find [sysname], 
     @position  [sysname] 
    ) 
    returns [sysname] 
    as 
     begin 
      declare @quad [sysname] = substring(@mask_to_find, 
         0, 
         charindex(N'.', 
            @mask_to_find, 
            0)); 

      set @mask_to_find = substring(@mask_to_find, 
              charindex(N'.', @mask_to_find, 0) + 1, 
              len(@mask_to_find)); 
      set @network = @network.query('/*[@quad=sql:variable("@quad")]/*'); 

      if(@network.value('count (/*)', 
           'int') > 0) 
       begin 
        set @position = coalesce(@position + N'.', N'') + @quad; 
       end 
      else 
       set @position = coalesce(@position + N'.', N'') + N'000'; 

      if (@@nestlevel < 4) 
       return [tree].[run] (@network, 
            @mask_to_find, 
            @position); 

      return @position; 
     end 

    go 

    declare @network [xml] = N'<subnet quad="255" > 
      <subnet quad="255" > 
       <subnet quad="192" /> 
       <subnet quad="255" /> 
      </subnet> 
     </subnet> 
     <subnet quad="10" />'; 
    declare @mask_to_find [sysname] = N'255.255.190.000'; 
    declare @position [sysname]; 

    select [tree].[run] (@network, 
         @mask_to_find, 
         @position) 

    go 
関連する問題