2015-02-07 26 views
1

我正在尋找一種方法,使用內置的cidr類型從存儲在postgresql中的cidr塊獲取直接的子網。postgresql cidr查找子塊

例數據庫

CREATE TABLE nets (
    id serial primary key, 
    net cidr 
); 
INSERT INTO nets (net) VALUES 
    ('10.1.0.0/16'), 
    ('10.1.0.0/20'), 
    ('10.1.1.0/24'), 
    ('10.1.1.8/29'), 
    ('10.1.1.32/28'), 
    ('10.2.15.0/24'), 
    ('10.2.15.64/27') 

所需的查詢應該F.E.

  • 爲searchkey 10.1.0.0/16
  • 10.1.1.8/29和10.1.1.32/28爲searchkey回報10.1.0.0/20 10.1.1.0/24

我想出了與是(http://sqlfiddle.com/#!15/2b4b5/1):

SELECT 
    id, 
    net 
FROM 
    nets n 
WHERE 
    net << '10.1.1.0/24' AND 
    '10.1.1.0/24' IN (
    SELECT 
     net 
    FROM 
     nets 
    WHERE 
     net >> n.net 
    ORDER BY 
     net DESC 
    LIMIT 1 
) 
ORDER BY 
    net 

這給出了期望的結果,但它不能擴展。即使在數據庫中只有幾千個條目,這個速度也會非常慢。

有沒有另外一種方法來實現這一點,而不添加顯式的父/子關係到數據庫模型?

回答

2

更新:這是一個變量,從Nested set model轉化,也可能是更快(主要是與inet_ops的GiST指數9.4+):

SELECT c.id, c.net 
FROM nets c 
WHERE c.net << '10.1.1.0/24' 
AND NOT EXISTS(
    SELECT 1 
    FROM nets AS m 
    WHERE c.net << m.net AND m.net << '10.1.1.0/24' 
); 

原來的答覆

一個簡單的EXCEPT應該用更大的輸入集合(它不會計算每個子網絡的子計劃)更好地縮放:

(SELECT id, net 
FROM nets 
WHERE net << '10.1.1.0/24') 
EXCEPT 
(SELECT c.id, c.net 
FROM nets p 
JOIN nets c ON c.net << p.net 
WHERE p.net << '10.1.1.0/24') 
ORDER BY net; 

注意:對於較小的輸入集,EXCEPT變體可能會比您的查詢更慢。

但爲了最大限度地提高性能(包括這個&和您的查詢),您應該使用一些索引。

如果你的PostgreSQL 9.4+,您應該使用新的inet_ops的GiST索引:

CREATE INDEX nets_inet_net_gist ON nets USING gist (inet(net) inet_ops); 

否則,您可以使用network_ops B樹索引:

CREATE INDEX nets_inet_net_btree ON nets USING btree (inet(net) network_ops); 

雖然inet_ops可以直接使用<<運營商,network_ops將轉換您的表達到類似的東西:

Index Cond: (((net)::inet > '10.1.1.0/24'::inet) AND ((net)::inet <= '10.1.1.255'::inet)) 
+0

哇謝謝你的投入。如果新的查詢和索引幫助我的案例,我將準備一組更大的測試數據並報告回來。 – 2015-02-10 12:03:58

+0

只有當沒有間歇性塊時,您的查詢似乎纔有效。如果查詢10.1.0.0/20,它應該返回10.1.1.0/24。 http://sqlfiddle.com/#!15/2b4b5/6 – 2015-02-10 12:26:30

+0

@andrekeller抱歉,在except子句中選擇了錯誤的表。現在修好。 – pozs 2015-02-10 12:46:48