2015-02-07 29 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