2016-08-05 46 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) 

此查找表中大約有350萬條記錄。

我的目標是通過找出其中的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服務器2014'。語法和功能的差異往往會影響答案。 – HABO

+0

我使用sql-server和tsql進行了標記。 SQLServer是2014 –

+0

@habo我建議發表評論的第一部分作爲答案。我認爲你是真實的。 – Bohemian

回答

1

更新

總結散佈各種意見之間的信息:

  1. 的IP地址列是含而不留十進制值VarChar(50)串填充。這些列上的索引將按字母順序排序,而不是數字,即「10」<「2」。 (隨着左填充的排序將是正確的數值,以及: 「10」 - > 「02」。)

  2. WHERE條款(where @DecimalIP between GeoBlocks.StartIPNum and GeoBlocks.EndIPNumb)使用混合數據類型。 @DecimalIPBIGINT,而兩列是VarChar(50)。 SQL通過實現數據類型優先級方案來處理混合數據類型之間的操作。 (Ref。)這會導致將每行中的IP地址從字符串轉換爲BIGINT值,因此比較是以數字形式完成的,「預期」結果以相當大的成本返回。在這種情況下,索引(都是)無用。

  3. 將列更改爲BIGINT將允許使用索引來提高性能並確保比較以數字方式而不是按字母順序進行。包含StartIPNumEndIPNumb列的單個索引將大大提高性能。請注意,如果不允許重疊的地址範圍,那麼索引在StartIPNum上實際上將是唯一的,並且可以用StartIPNum上的索引替換爲EndIPNumb,作爲包括列以獲得性能。

原來的答案

如果在點表示法使用IPv4地址,例如「192.168.0。42" ,您可以將字符串轉換成BIGINT值這個UDF:

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子句中的整數列

如果將值存儲爲整數,則以下函數會將它們轉換回標準化字符串,其中地址的每個部分都是三位數字,因爲這些值可用於比較他們將按字母和數字排序相同的方式。

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地址創建一條記錄。現在,每條記錄代表一系列bigint IP地址。這意味着4,294,967,296條記錄,而不是現在的3,500,000條記錄。 (我是TSQL的新手!) –

+0

不,你仍然可以使用地址範圍。您還可以創建包含_both_「StartIPNum」和「EndIPNumb」的單個索引。這應該會對性能產生影響。 (麻木?) – HABO

0

我猜索引設置不正確。你可以提高它像這樣:

  1. 走進SQL Server Management Studio中,並從菜單中打開一個新的查詢窗口
  2. 選擇:查詢 - >包括實際的執行計劃(按Ctrl + M)
  3. 鍵入您的查詢並執行它

現在查詢將會執行,並且執行計劃將被顯示。如果索引不好,它會告訴你一個提示,指出哪些索引缺失以及你可以做什麼。

它會顯示您需要創建索引精確的SQL語句。 只需複製並粘貼該語句並執行它,然後您的索引應該工作。

+0

謝謝HABO。我沒有執行你提供的代碼,但採納了你的建議。 1。我改變了表格結構,所以IP現在是bigint。 2.我創建了一個以startipnum和endipnum作爲索引字段的聚集索引。現在,大約需要750毫秒的查詢需要大約150毫秒。感謝大家的投入! –

+0

150ms仍然太慢。順便說一句,我不是哈博;) –