2016-08-05 6 views
0

私はMaxMindの無料データベースを使用してIP検索を行っています。私は次の表にデータを変換します:検索のスピードアップ

CREATE TABLE [dbo].[GeoBlocks](
    [StartIPNum] [varchar](50) NULL, 
    [EndIPNumb] [varchar](50) NULL, 
    [LocationNum] [varchar](50) NULL, 
    [PostalCode] [varchar](50) NULL, 
    [Latitude] [varchar](50) NULL, 
    [Longitude] [varchar](50) NULL) 

このルックアップテーブルには約3.5Mのレコードがあります。

私の目標は、IPは、私のストアドプロシージャは、次のようになります

StartIPNumとEndIPNumの間で記録見つけることによって、IPのためのLocationNum(小数点形式)を決定することです: パラメータ:@DecimalIP BIGINT

select GeoBlocks.StartIPNum ,@DecimalIP as DecimalIp 
    ,GeoBlocks.Postalcode ,GeoBlocks.Latitude as Latitude 
    ,GeoBlocks.Longitude as Longitude 
    from GeoBlocks 
    where @DecimalIP between GeoBlocks.StartIPNum and GeoBlocks.EndIPNumb 

StartIPNumとEndIPNumで一意のインデックスを作成しました。

ただし、これを実行すると、SQLサーバーはクエリのWhere部分のテーブルスキャンを実行します。このクエリは650〜750msかかる。 (私のサーバー上のほとんどのクエリは0-2msかかります)

どのようにこのクエリをスピードアップしますか?

追加サンプルデータ:

StartIPNum EndIPNumb LocationNum PostalCode Latitude Longitude 
1350218632 1350218639 2782113     48.2000 16.3667 
1350218640 1350218655 2782113     48.2000 16.3667 
1350218656 1350218687 2782113     48.2000 16.3667 
1350218688 1350218751 2782113     48.2000 16.3667 
1350218752 1350218783 2782113     48.2000 16.3667 
+1

IPV4アドレスを使用している場合は、それらを 'BIGINT'値に変換して適切なインデックスを使用できます。アルファベット順にソートされた文字列の "小数"(?!)の値は、あなたが望むことをしないかもしれません。いくつかのサンプルデータはどうですか?ヒント:適切なソフトウェア(MySQL、Oracle、DB2など)とバージョンの両方でデータベースの質問にタグを付けると便利です。 'sql-server-2014'です。構文と機能の相違は、しばしば答えに影響します。 – HABO

+0

私はsql-serverとtsqlでタグ付けしました。 SQLServerは2014 –

+0

@haboです。あなたのコメントの最初の部分を回答として投稿することをお勧めします。私はあなたにスポットがあると思う。 – Bohemian

答えて

1

更新

  1. IPアドレスの列が残ってない10進値を含むVarChar(50)文字列です:

    は、様々な意見に散在の情報を要約しますパディングこれらの列のインデックスは、数字ではなくアルファベット順にソートされます(「10」<「2」)。 (左パディングでは、ソートは数値でも正しくなります: "10"> "02")

  2. WHERE句(where @DecimalIP between GeoBlocks.StartIPNum and GeoBlocks.EndIPNumb)は、混合データ型を使用します。 @DecimalIPBIGINTであり、2つの列はVarChar(50)です。 SQLは、データ型優先順位体系を実装することにより、混合データ型間の操作を処理します。 (Ref。)これは、各行にIPアドレスがBIGINT値に文字列から変換させ、したがって比較が数値的に行われ、「期待」の結果は、かなりのコストに戻されます。この場合、索引は役に立たなくなります。

  3. 列をBIGINTに変更すると、パフォーマンスが向上し、アルファベット順ではなく数値で比較されるようにインデックスを使用できます。両方StartIPNumEndIPNumb列を含む単一インデックスはパフォーマンスが大幅に向上します。重複したアドレス範囲が許可されていない場合は、インデックスが有効にStartIPNumにユニークになり、パフォーマンスのため含まコラムとしてEndIPNumbStartIPNum上の指標に置き換えることができることに注意してください。

オリジナル回答

あなたは、例えば、ドット表記にIPV4アドレスを使用している場合"192.168.0。42" 、あなたはこのUDFとBIGINT値に文字列を変換することができます

create function [dbo].[IntegerIPV4Address](@IPV4Address VarChar(16)) 
    returns BigInt 
    with SchemaBinding 
    begin 
    declare @Dot1 as Int = CharIndex('.', @IPV4Address); 
    declare @Dot2 as Int = CharIndex('.', @IPV4Address, @Dot1 + 1); 
    declare @Dot3 as Int = CharIndex('.', @IPV4Address, @Dot2 + 1); 
    return Cast(Substring(@IPV4Address, 0, @Dot1) as BigInt) * 0x1000000 + 
    Cast(Substring(@IPV4Address, @Dot1 + 1, @Dot2 - @Dot1 - 1) as BigInt) * 0x10000 + 
    Cast(Substring(@IPV4Address, @Dot2 + 1, @Dot3 - @Dot2 - 1) as BigInt) * 0x100 + 
    Cast(Substring(@IPV4Address, @Dot3 + 1, Len(@IPV4Address) * 1) as BigInt); 
    end 

あなたは整数値を格納したり、関数の結果に基づいて計算列にインデックスを作成するか、あなたを変更する必要があることに注意してください。 WHERE句の整数列を参照するクエリ

数値を整数として格納すると、次の関数は、アドレスの各部分が3桁の正規化された文字列に変換します。これらの値は、アルファベットと数値の両方で同じ方法で並べ替えます。

create function [dbo].[NormalizedIPV4Address](@IntegerIPV4Address as BigInt) 
    returns VarChar(16) 
    with SchemaBinding -- Deterministic function. 
    begin 
    declare @BinaryAddress as VarBinary(4) = Cast(@IntegerIPV4Address as VarBinary(4)); 
    return Right('00' + Cast(Cast(Substring(@BinaryAddress, 1, 1) as Int) as VarChar(3)), 3) + 
    '.' + Right('00' + Cast(Cast(Substring(@BinaryAddress, 2, 1) as Int) as VarChar(3)), 3) + 
    '.' + Right('00' + Cast(Cast(Substring(@BinaryAddress, 3, 1) as Int) as VarChar(3)), 3) + 
    '.' + Right('00' + Cast(Cast(Substring(@BinaryAddress, 4, 1) as Int) as VarChar(3)), 3) 
    end 

文字列値をテーブルにラウンドトリップして、両方の関数を使用して正しく並べ替えるように、すべてを正規化された形式にすることができます。将来のすべての挿入と更新が正規化されることを必要とするので理想的な解決策ではありませんが、現時点では役立ちます。

+0

bigintへの変換は問題ありません。テーブル構造をbigintに変更し、すべてのデータをbigintとして再インポートできます。しかし、私が完全に理解していないのは、IPアドレスごとにレコードを1つ作成することを提案しているかどうかです。今、各レコードは、大規模なIPアドレスの範囲を表します。それは私が今持っている3,500,000とは対照的に4,294,967,296の記録を意味する。 (私はTSQLを初めて使っています!) –

+0

いいえ、あなたは引き続きアドレス範囲を使用できます。 _both_ 'StartIPNum'と' EndIPNumb'を含む単一のインデックスを作成することもできます。それはパフォーマンスの驚異をもたらすはずです。 (Numb?) – HABO

0

インデックスが正しく設定されていないようです。

  1. SQL Server Management Studioのに移動し、新しいクエリウィンドウを開く
  2. 選択メニューから:あなたはこのようにそれを改善することができQuery->実際の実行計画(Ctrlキー+ M)
  3. を含めます
  4. はあなたのクエリを入力し、それが

今、クエリが実行され、実行、および実行計画が表示されます。インデックスが悪い場合は、インデックスがないヒントとできることが表示されます。

インデックスを作成するために必要な正確なSQL文が表示されます。 そのステートメントをコピーして貼り付けて実行すると、インデックスが機能するはずです。

+0

ありがとうHABO。私はあなたが与えたコードを実装しなかったが、あなたの提案を取った。 1。私はテーブルの構造を変更してIPが今やbigintになるようにしました。 2.インデックスフィールドとしてstartipnumとendipnumの両方を持つクラスタ化インデックスを作成しました。今や約750msを要した同じクエリには約150msかかる。みんなの感謝をよろしく! –

+0

150msはまだ遅すぎます。ところで、私はハボではない;) –

関連する問題